diff --git a/app/lib/domain/services/app_initialization.dart b/app/lib/domain/services/app_initialization.dart new file mode 100644 index 00000000..973d4a59 --- /dev/null +++ b/app/lib/domain/services/app_initialization.dart @@ -0,0 +1,54 @@ +import 'package:bang_navigator/features/about/data/repositories/package_info_repository.dart'; +import 'package:bang_navigator/features/bangs/domain/repositories/sync.dart'; +import 'package:bang_navigator/features/search_browser/domain/services/session.dart'; +import 'package:bang_navigator/features/settings/data/repositories/settings_repository.dart'; +import 'package:exceptions/exceptions.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'app_initialization.g.dart'; + +@Riverpod(keepAlive: true) +class AppInitializationService extends _$AppInitializationService { + /// Will de facto restart the app + Future reinitialize() async { + ref.invalidateSelf(); + return initialize(); + } + + Future initialize() async { + state = await Result.fromAsync(() async { + final errors = []; + + //Ensure Package info is loaded + await ref.read(packageInfoProvider.future); + + final syncResults = await ref + .read(bangSyncRepositoryProvider.notifier) + .syncAllBangGroups(syncInterval: const Duration(days: 7)); + + for (final MapEntry(value: result) in syncResults.entries) { + result.onFailure(errors.add); + } + + final settings = await ref.read(settingsRepositoryProvider.future); + if (settings.incognitoMode) { + await ref.read(sessionServiceProvider.notifier).clearAllData(); + } + + if (settings.kagiSession case final String session) { + if (session.isNotEmpty) { + await ref + .read(sessionServiceProvider.notifier) + .setKagiSession(session); + } + } + + return (initialized: true, errors: errors); + }); + } + + @override + Result<({bool initialized, List errors})> build() { + return Result.success((initialized: false, errors: List.empty())); + } +} diff --git a/app/lib/domain/services/app_initialization.g.dart b/app/lib/domain/services/app_initialization.g.dart new file mode 100644 index 00000000..1534b96a --- /dev/null +++ b/app/lib/domain/services/app_initialization.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'app_initialization.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$appInitializationServiceHash() => + r'7b36828c2368c3b3a97b799dd8192854629c8379'; + +/// See also [AppInitializationService]. +@ProviderFor(AppInitializationService) +final appInitializationServiceProvider = NotifierProvider< + AppInitializationService, + Result<({bool initialized, List errors})>>.internal( + AppInitializationService.new, + name: r'appInitializationServiceProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$appInitializationServiceHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$AppInitializationService + = Notifier errors})>>; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/app/lib/features/search_browser/domain/services/session.dart b/app/lib/features/search_browser/domain/services/session.dart index e3862298..60710093 100644 --- a/app/lib/features/search_browser/domain/services/session.dart +++ b/app/lib/features/search_browser/domain/services/session.dart @@ -44,10 +44,6 @@ class SessionService extends _$SessionService { await webViewLoaded.future.whenComplete(() => headlessWebView..dispose()); } - void initializationDone() { - state = true; - } - @override - bool build() => false; + void build() {} } diff --git a/app/lib/features/search_browser/domain/services/session.g.dart b/app/lib/features/search_browser/domain/services/session.g.dart index 85326564..3c81fd49 100644 --- a/app/lib/features/search_browser/domain/services/session.g.dart +++ b/app/lib/features/search_browser/domain/services/session.g.dart @@ -6,12 +6,12 @@ part of 'session.dart'; // RiverpodGenerator // ************************************************************************** -String _$sessionServiceHash() => r'48126bad2cb8d5c38296307bede64bf549365acf'; +String _$sessionServiceHash() => r'fd2cd03bbf2dd254bb19e541c71a6fb249f5a7bb'; /// See also [SessionService]. @ProviderFor(SessionService) final sessionServiceProvider = - AutoDisposeNotifierProvider.internal( + AutoDisposeNotifierProvider.internal( SessionService.new, name: r'sessionServiceProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -21,6 +21,6 @@ final sessionServiceProvider = allTransitiveDependencies: null, ); -typedef _$SessionService = AutoDisposeNotifier; +typedef _$SessionService = AutoDisposeNotifier; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/app/lib/main.dart b/app/lib/main.dart index caec13bf..8591a47b 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,11 +1,10 @@ import 'package:bang_navigator/core/logger.dart'; import 'package:bang_navigator/core/providers.dart'; -import 'package:bang_navigator/features/about/data/repositories/package_info_repository.dart'; -import 'package:bang_navigator/features/bangs/domain/repositories/sync.dart'; -import 'package:bang_navigator/features/search_browser/domain/services/session.dart'; -import 'package:bang_navigator/features/settings/data/repositories/settings_repository.dart'; +import 'package:bang_navigator/domain/services/app_initialization.dart'; import 'package:bang_navigator/presentation/hooks/on_initialization.dart'; +import 'package:bang_navigator/presentation/widgets/failure_widget.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:home_widget/home_widget.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -39,28 +38,9 @@ void main() async { builder: (context, ref, child) { useOnInitialization( () async { - await ref.read(packageInfoProvider.future); - await ref - .read(bangSyncRepositoryProvider.notifier) - .syncAllBangGroups(syncInterval: const Duration(days: 7)); - - final settings = - await ref.read(settingsRepositoryProvider.future); - - if (settings.incognitoMode) { - await ref.read(sessionServiceProvider.notifier).clearAllData(); - } - - if (settings.kagiSession case final String session) { - if (session.isNotEmpty) { - await ref - .read(sessionServiceProvider.notifier) - .setKagiSession(session); - } - } - - ref.read(sessionServiceProvider.notifier).initializationDone(); + .read(appInitializationServiceProvider.notifier) + .initialize(); }, ); @@ -76,18 +56,11 @@ class MainApp extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isInitialized = ref.watch(sessionServiceProvider); + final initializationResult = ref.watch(appInitializationServiceProvider); final router = ref.watch(routerProvider); - if (!isInitialized) { - return const Center( - child: CircularProgressIndicator(), - ); - } - - return MaterialApp.router( - debugShowCheckedModeBanner: false, - theme: ThemeData( + final themeData = useMemoized( + () => ThemeData( useMaterial3: true, brightness: Brightness.dark, // colorScheme: ColorScheme.fromSeed( @@ -95,7 +68,44 @@ class MainApp extends HookConsumerWidget { // brightness: Brightness.dark, // ), ), - routerConfig: router, + ); + + return initializationResult.fold( + (initializationState) { + if (!initializationState.initialized) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + return MaterialApp.router( + debugShowCheckedModeBanner: false, + theme: themeData, + routerConfig: router, + ); + }, + onFailure: (errorMessage) { + return MaterialApp( + debugShowCheckedModeBanner: false, + theme: themeData, + home: Scaffold( + appBar: AppBar( + title: const Text('Initiallization Error'), + ), + body: Center( + child: FailureWidget( + title: 'Could not initialize App', + exception: errorMessage.toString(), + onRetry: () async { + await ref + .read(appInitializationServiceProvider.notifier) + .reinitialize(); + }, + ), + ), + ), + ); + }, ); } }