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:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/core/routing/routes.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/onboarding.dart';
|
||||
|
||||
part 'router.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
GoRouter router(Ref ref) {
|
||||
return GoRouter(debugLogDiagnostics: true, routes: $appRoutes);
|
||||
Future<GoRouter> router(Ref ref) async {
|
||||
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
|
||||
// **************************************************************************
|
||||
|
||||
String _$routerHash() => r'25fc899b87c080600f6e1d87d510db333378bdfd';
|
||||
String _$routerHash() => r'ce7cc5b5f236781a06867a852a08ed03d3582022';
|
||||
|
||||
/// See also [router].
|
||||
@ProviderFor(router)
|
||||
final routerProvider = Provider<GoRouter>.internal(
|
||||
final routerProvider = FutureProvider<GoRouter>.internal(
|
||||
router,
|
||||
name: r'routerProvider',
|
||||
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')
|
||||
// ignore: unused_element
|
||||
typedef RouterRef = ProviderRef<GoRouter>;
|
||||
typedef RouterRef = FutureProviderRef<GoRouter>;
|
||||
// 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
|
||||
|
||||
@@ -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/presentation/screens/container_edit.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/general_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());
|
||||
}
|
||||
}
|
||||
|
||||
@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 => [
|
||||
$aboutRoute,
|
||||
$onboardingRoute,
|
||||
$settingsRoute,
|
||||
$browserRoute,
|
||||
$bangCategoriesRoute,
|
||||
@@ -41,6 +42,40 @@ mixin _$AboutRoute on GoRouteData {
|
||||
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(
|
||||
path: '/settings',
|
||||
name: 'SettingsRoute',
|
||||
|
||||
@@ -34,6 +34,7 @@ class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin {
|
||||
}
|
||||
|
||||
Selectable<BangData> getBangDataList({
|
||||
Iterable<String>? triggers,
|
||||
Iterable<BangGroup>? groups,
|
||||
String? domain,
|
||||
String? category,
|
||||
@@ -41,6 +42,9 @@ class BangDao extends DatabaseAccessor<BangDatabase> with _$BangDaoMixin {
|
||||
bool? orderMostFrequentFirst,
|
||||
}) {
|
||||
final selectable = select(db.bangDataView);
|
||||
if (triggers != null) {
|
||||
selectable.where((t) => t.trigger.isIn(triggers));
|
||||
}
|
||||
if (groups != null) {
|
||||
selectable.where((t) => t.group.isInValues(groups));
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ Stream<Map<String, List<String>>> bangCategories(Ref ref) {
|
||||
@Riverpod()
|
||||
Stream<List<BangData>> bangList(
|
||||
Ref ref, {
|
||||
List<String>? triggers,
|
||||
List<BangGroup>? groups,
|
||||
String? domain,
|
||||
({String category, String? subCategory})? categoryFilter,
|
||||
@@ -37,6 +38,7 @@ Stream<List<BangData>> bangList(
|
||||
}) {
|
||||
final repository = ref.watch(bangDataRepositoryProvider.notifier);
|
||||
return repository.watchBangs(
|
||||
triggers: triggers,
|
||||
groups: groups,
|
||||
domain: domain,
|
||||
categoryFilter: categoryFilter,
|
||||
|
||||
@@ -43,7 +43,7 @@ final bangCategoriesProvider =
|
||||
// ignore: unused_element
|
||||
typedef BangCategoriesRef =
|
||||
AutoDisposeStreamProviderRef<Map<String, List<String>>>;
|
||||
String _$bangListHash() => r'dc168bd1246bea3e5f8015dd52fbf41c5f98ca13';
|
||||
String _$bangListHash() => r'd1e0bb9fa4f523ce516e075c0d149bf7803ebb2b';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
@@ -77,12 +77,14 @@ class BangListFamily extends Family<AsyncValue<List<BangData>>> {
|
||||
|
||||
/// See also [bangList].
|
||||
BangListProvider call({
|
||||
List<String>? triggers,
|
||||
List<BangGroup>? groups,
|
||||
String? domain,
|
||||
({String category, String? subCategory})? categoryFilter,
|
||||
bool? orderMostFrequentFirst,
|
||||
}) {
|
||||
return BangListProvider(
|
||||
triggers: triggers,
|
||||
groups: groups,
|
||||
domain: domain,
|
||||
categoryFilter: categoryFilter,
|
||||
@@ -93,6 +95,7 @@ class BangListFamily extends Family<AsyncValue<List<BangData>>> {
|
||||
@override
|
||||
BangListProvider getProviderOverride(covariant BangListProvider provider) {
|
||||
return call(
|
||||
triggers: provider.triggers,
|
||||
groups: provider.groups,
|
||||
domain: provider.domain,
|
||||
categoryFilter: provider.categoryFilter,
|
||||
@@ -119,6 +122,7 @@ class BangListFamily extends Family<AsyncValue<List<BangData>>> {
|
||||
class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
||||
/// See also [bangList].
|
||||
BangListProvider({
|
||||
List<String>? triggers,
|
||||
List<BangGroup>? groups,
|
||||
String? domain,
|
||||
({String category, String? subCategory})? categoryFilter,
|
||||
@@ -126,6 +130,7 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
||||
}) : this._internal(
|
||||
(ref) => bangList(
|
||||
ref as BangListRef,
|
||||
triggers: triggers,
|
||||
groups: groups,
|
||||
domain: domain,
|
||||
categoryFilter: categoryFilter,
|
||||
@@ -138,6 +143,7 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
||||
: _$bangListHash,
|
||||
dependencies: BangListFamily._dependencies,
|
||||
allTransitiveDependencies: BangListFamily._allTransitiveDependencies,
|
||||
triggers: triggers,
|
||||
groups: groups,
|
||||
domain: domain,
|
||||
categoryFilter: categoryFilter,
|
||||
@@ -151,12 +157,14 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.triggers,
|
||||
required this.groups,
|
||||
required this.domain,
|
||||
required this.categoryFilter,
|
||||
required this.orderMostFrequentFirst,
|
||||
}) : super.internal();
|
||||
|
||||
final List<String>? triggers;
|
||||
final List<BangGroup>? groups;
|
||||
final String? domain;
|
||||
final ({String category, String? subCategory})? categoryFilter;
|
||||
@@ -175,6 +183,7 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
triggers: triggers,
|
||||
groups: groups,
|
||||
domain: domain,
|
||||
categoryFilter: categoryFilter,
|
||||
@@ -191,6 +200,7 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is BangListProvider &&
|
||||
other.triggers == triggers &&
|
||||
other.groups == groups &&
|
||||
other.domain == domain &&
|
||||
other.categoryFilter == categoryFilter &&
|
||||
@@ -200,6 +210,7 @@ class BangListProvider extends AutoDisposeStreamProvider<List<BangData>> {
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, triggers.hashCode);
|
||||
hash = _SystemHash.combine(hash, groups.hashCode);
|
||||
hash = _SystemHash.combine(hash, domain.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')
|
||||
// ignore: unused_element
|
||||
mixin BangListRef on AutoDisposeStreamProviderRef<List<BangData>> {
|
||||
/// The parameter `triggers` of this provider.
|
||||
List<String>? get triggers;
|
||||
|
||||
/// The parameter `groups` of this provider.
|
||||
List<BangGroup>? get groups;
|
||||
|
||||
@@ -230,6 +244,8 @@ class _BangListProviderElement
|
||||
with BangListRef {
|
||||
_BangListProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
List<String>? get triggers => (origin as BangListProvider).triggers;
|
||||
@override
|
||||
List<BangGroup>? get groups => (origin as BangListProvider).groups;
|
||||
@override
|
||||
|
||||
@@ -46,6 +46,7 @@ class BangDataRepository extends _$BangDataRepository {
|
||||
}
|
||||
|
||||
Stream<List<BangData>> watchBangs({
|
||||
Iterable<String>? triggers,
|
||||
Iterable<BangGroup>? groups,
|
||||
String? domain,
|
||||
({String category, String? subCategory})? categoryFilter,
|
||||
@@ -55,6 +56,7 @@ class BangDataRepository extends _$BangDataRepository {
|
||||
.read(bangDatabaseProvider)
|
||||
.bangDao
|
||||
.getBangDataList(
|
||||
triggers: triggers,
|
||||
groups: groups,
|
||||
domain: domain,
|
||||
category: categoryFilter?.category,
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'data.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$bangDataRepositoryHash() =>
|
||||
r'de89d7089ce6a92015bee233f08d920981518793';
|
||||
r'095708cfffe8d0fb604dc02f69e77ae1c050a893';
|
||||
|
||||
/// See also [BangDataRepository].
|
||||
@ProviderFor(BangDataRepository)
|
||||
|
||||
@@ -22,7 +22,7 @@ GeckoSelectionActionService selectionActionService(Ref ref) {
|
||||
unawaited(
|
||||
service.setActions([
|
||||
SearchAction((text) async {
|
||||
final router = ref.read(routerProvider);
|
||||
final router = await ref.read(routerProvider.future);
|
||||
final isCurrentPrivate =
|
||||
ref.read(selectedTabStateProvider)?.isPrivate ?? false;
|
||||
final route = SearchRoute(
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'providers.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$selectionActionServiceHash() =>
|
||||
r'c45737a0a403bff1bdb1931b89701300f8dc3726';
|
||||
r'456bab83920ae78969bece514d5524e2445e06b6';
|
||||
|
||||
/// See also [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: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';
|
||||
|
||||
part 'database.g.dart';
|
||||
|
||||
@DriftDatabase(include: {'database.drift'}, daos: [SettingDao, CacheDao])
|
||||
@DriftDatabase(
|
||||
include: {'database.drift'},
|
||||
daos: [SettingDao, CacheDao, OnboardingDao],
|
||||
)
|
||||
class UserDatabase extends _$UserDatabase {
|
||||
@override
|
||||
final int schemaVersion = 1;
|
||||
|
||||
@@ -10,6 +10,11 @@ CREATE TABLE icon_cache (
|
||||
fetch_date DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE onboarding (
|
||||
revision INTEGER NOT NULL,
|
||||
completion_date DATETIME NOT NULL
|
||||
);
|
||||
|
||||
evictCacheEntries:
|
||||
DELETE FROM icon_cache
|
||||
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 {
|
||||
_$UserDatabase(QueryExecutor e) : super(e);
|
||||
$UserDatabaseManager get managers => $UserDatabaseManager(this);
|
||||
late final Setting setting = Setting(this);
|
||||
late final IconCache iconCache = IconCache(this);
|
||||
late final Onboarding onboarding = Onboarding(this);
|
||||
late final SettingDao settingDao = SettingDao(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}) {
|
||||
return customUpdate(
|
||||
'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 =>
|
||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
||||
@override
|
||||
List<DatabaseSchemaEntity> get allSchemaEntities => [setting, iconCache];
|
||||
List<DatabaseSchemaEntity> get allSchemaEntities => [
|
||||
setting,
|
||||
iconCache,
|
||||
onboarding,
|
||||
];
|
||||
}
|
||||
|
||||
typedef $SettingCreateCompanionBuilder =
|
||||
@@ -780,6 +970,149 @@ typedef $IconCacheProcessedTableManager =
|
||||
IconCacheData,
|
||||
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 {
|
||||
final _$UserDatabase _db;
|
||||
@@ -787,4 +1120,6 @@ class $UserDatabaseManager {
|
||||
$SettingTableManager get setting => $SettingTableManager(_db, _db.setting);
|
||||
$IconCacheTableManager get 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,
|
||||
darkTheme: darkTheme,
|
||||
themeMode: themeMode,
|
||||
routerConfig: router,
|
||||
routerConfig: router.valueOrNull,
|
||||
);
|
||||
},
|
||||
onFailure: (errorMessage) {
|
||||
|
||||
@@ -57,6 +57,7 @@ dependencies:
|
||||
path: ../packages/simple_intent_receiver
|
||||
skeletonizer: ^2.0.1
|
||||
sliver_tools: ^0.2.12
|
||||
smooth_page_indicator: ^1.2.1
|
||||
speech_to_text_google_dialog:
|
||||
git:
|
||||
url: https://github.com/FaFre/speech_to_text_google_dialog.git
|
||||
|
||||
Reference in New Issue
Block a user