update onboarding, perimissions, layout

This commit is contained in:
Fabian Freund
2025-06-26 00:13:58 +02:00
parent 5889c1272a
commit 4f2b76c09d
5 changed files with 137 additions and 58 deletions
@@ -7,6 +7,7 @@ import 'package:weblibre/core/providers/router.dart';
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
import 'package:weblibre/features/onboarding/presentation/pages/abstract/i_form_page.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/default_search.dart';
import 'package:weblibre/features/onboarding/presentation/pages/permissions.dart';
import 'package:weblibre/features/onboarding/presentation/pages/ublock_opt_in.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/onboarding/presentation/pages/welcome.dart';
import 'package:weblibre/features/user/domain/repositories/onboarding.dart'; import 'package:weblibre/features/user/domain/repositories/onboarding.dart';
@@ -32,6 +33,7 @@ class OnboardingScreen extends HookConsumerWidget {
const WelcomePage(), const WelcomePage(),
const DefaultSearchPage(), const DefaultSearchPage(),
UBlockOptInPage(formKey: GlobalKey<FormState>()), UBlockOptInPage(formKey: GlobalKey<FormState>()),
PermissionsPage(formKey: GlobalKey<FormState>()),
]; ];
}, [currentRevision, targetRevision]); }, [currentRevision, targetRevision]);
@@ -13,6 +13,8 @@ class DefaultSearchPage extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final bangs = ref.watch(bangListProvider(triggers: defaultBangs)); final bangs = ref.watch(bangListProvider(triggers: defaultBangs));
final activeBang = ref.watch(defaultSearchBangDataProvider).valueOrNull; final activeBang = ref.watch(defaultSearchBangDataProvider).valueOrNull;
@@ -26,67 +28,77 @@ class DefaultSearchPage extends HookConsumerWidget {
); );
} }
return bangs.when( return Padding(
skipLoadingOnReload: true, padding: const EdgeInsets.symmetric(horizontal: 16.0),
data: (availableBangs) { child: bangs.when(
return ListView( skipLoadingOnReload: true,
children: [ data: (availableBangs) {
SizedBox( return ListView(
height: 48, children: [
child: Row( const SizedBox(height: 24),
mainAxisAlignment: MainAxisAlignment.spaceBetween, Center(
children: [ child: Text(
const Text('Default Search:'), 'Default Search Provider',
if (activeBang != null) style: theme.textTheme.headlineMedium,
FilterChip( ),
showCheckmark: false,
label: Text(activeBang.websiteName),
avatar: UrlIcon([
activeBang.getTemplateUrl(''),
], iconSize: 20),
selected: true,
onSelected: (value) {},
),
],
), ),
), const SizedBox(height: 24),
const Divider(), SizedBox(
Wrap( height: 48,
spacing: 8.0, child: Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
...availableBangs.map( children: [
(bang) => FilterChip( const Text('Selected Provider'),
showCheckmark: false, if (activeBang != null)
label: Text(bang.websiteName), FilterChip(
avatar: UrlIcon([bang.getTemplateUrl('')], iconSize: 20), showCheckmark: false,
selected: activeBang?.trigger == bang.trigger, label: Text(activeBang.websiteName),
onSelected: (selected) async { avatar: UrlIcon([
if (selected) { activeBang.getTemplateUrl(''),
await updateSearchProvider(bang.trigger); ], 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);
} }
}, },
), ),
), ],
ActionChip( ),
label: const Text('Search more'), ],
avatar: const Icon(Icons.search), );
onPressed: () async { },
final trigger = await const BangSearchRoute().push<String?>( error: (error, stackTrace) => const SizedBox.shrink(),
context, loading: () => const SizedBox(height: 48, width: double.infinity),
); ),
if (trigger != null) {
await updateSearchProvider(trigger);
}
},
),
],
),
],
);
},
error: (error, stackTrace) => const SizedBox.shrink(),
loading: () => const SizedBox(height: 48, width: double.infinity),
); );
} }
} }
@@ -0,0 +1,63 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:weblibre/features/onboarding/presentation/pages/abstract/i_form_page.dart';
import 'package:weblibre/presentation/hooks/cached_future.dart';
class PermissionsPage extends HookConsumerWidget implements IFormPage {
@override
final GlobalKey<FormState> formKey;
const PermissionsPage({super.key, required this.formKey});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final notificationPermissionEnabled = useCachedFuture(
() => Permission.notification.isGranted,
);
return Form(
key: formKey,
child: ListView(
children: [
const SizedBox(height: 24),
Center(
child: Text('Permissions', style: theme.textTheme.headlineMedium),
),
const SizedBox(height: 24),
// Padding(
// padding: const EdgeInsets.symmetric(horizontal: 16.0),
// child: const Text('WebLibre requires following permissions'),
// ),
// const SizedBox(height: 24),
Skeletonizer(
enabled: !notificationPermissionEnabled.hasData,
child: FormField(
initialValue: true,
onSaved: (newValue) async {
if (newValue == true) {
await Permission.notification.request();
}
},
builder: (field) => SwitchListTile(
value: field.value ?? true,
title: const Text('Notifications'),
subtitle: const Text(
'Required to inform about download status',
),
onChanged: (notificationPermissionEnabled.data == true)
? null
: (value) {
field.didChange(value);
},
),
),
),
],
),
);
}
}
@@ -21,10 +21,11 @@ class UBlockOptInPage extends HookConsumerWidget implements IFormPage {
key: formKey, key: formKey,
child: ListView( child: ListView(
children: [ children: [
const SizedBox(height: 24),
SvgPicture.asset('assets/images/ublock.svg'), SvgPicture.asset('assets/images/ublock.svg'),
const SizedBox(height: 8), const SizedBox(height: 8),
Center( Center(
child: Text('uBlock Origin', style: theme.textTheme.headlineLarge), child: Text('uBlock Origin', style: theme.textTheme.headlineMedium),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
const Padding( const Padding(
+1
View File
@@ -48,6 +48,7 @@ dependencies:
package_info_plus: ^8.3.0 package_info_plus: ^8.3.0
path: ^1.9.1 path: ^1.9.1
path_provider: ^2.1.5 path_provider: ^2.1.5
permission_handler: ^12.0.0+1
pretty_qr_code: ^3.4.0 pretty_qr_code: ^3.4.0
riverpod: ^2.6.1 riverpod: ^2.6.1
riverpod_annotation: ^2.6.1 riverpod_annotation: ^2.6.1