improved error logging and handling

This commit is contained in:
Fabian Freund
2026-01-21 11:07:23 +01:00
parent f648c43441
commit dc4a5a9c85
35 changed files with 242 additions and 115 deletions
@@ -19,6 +19,7 @@
*/ */
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/features/bangs/data/models/bang_group.dart'; import 'package:weblibre/features/bangs/data/models/bang_group.dart';
class BangKey { class BangKey {
@@ -50,7 +51,8 @@ class BangKey {
group: BangGroup.values.firstWhere((g) => g.name == group), group: BangGroup.values.firstWhere((g) => g.name == group),
trigger: trigger, trigger: trigger,
); );
} catch (_) { } catch (e, s) {
logger.w('Failed to parse BangKey from string: "$key"', error: e, stackTrace: s);
return null; return null;
} }
} }
@@ -47,9 +47,14 @@ class SelectedTab extends _$SelectedTab {
}, },
); );
final selectedTabSub = eventSerivce.selectedTabEvents.listen((tabId) { final selectedTabSub = eventSerivce.selectedTabEvents.listen(
state = tabId; (tabId) {
}); state = tabId;
},
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in selected tab events', error: error, stackTrace: stackTrace);
},
);
ref.onDispose(() async { ref.onDispose(() async {
await selectedTabSub.cancel(); await selectedTabSub.cancel();
@@ -41,7 +41,7 @@ final class SelectedTabProvider
} }
} }
String _$selectedTabHash() => r'26d1bb90c777d7011f4f27f7b962584a4a62a0a7'; String _$selectedTabHash() => r'5caa359376e9103767e2d2e94c548d53cba79d0f';
abstract class _$SelectedTab extends $Notifier<String?> { abstract class _$SelectedTab extends $Notifier<String?> {
String? build(); String? build();
@@ -48,13 +48,18 @@ class TabList extends _$TabList {
}, },
); );
final tabListSub = eventService.tabListEvents.listen((tabs) { final tabListSub = eventService.tabListEvents.listen(
final equatableTabs = EquatableValue(tabs); (tabs) {
final equatableTabs = EquatableValue(tabs);
if (equatableTabs != state) { if (equatableTabs != state) {
state = equatableTabs; state = equatableTabs;
} }
}); },
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in tab list events', error: error, stackTrace: stackTrace);
},
);
ref.onDispose(() async { ref.onDispose(() async {
await tabListSub.cancel(); await tabListSub.cancel();
@@ -41,7 +41,7 @@ final class TabListProvider
} }
} }
String _$tabListHash() => r'0b9ca1e089a59895df4cbf28691c2849a7004f08'; String _$tabListHash() => r'88c2598a4d586fee9e3d0f1b418cf88ea5c7da75';
abstract class _$TabList extends $Notifier<EquatableValue<List<String>>> { abstract class _$TabList extends $Notifier<EquatableValue<List<String>>> {
EquatableValue<List<String>> build(); EquatableValue<List<String>> build();
@@ -218,29 +218,64 @@ class TabStates extends _$TabStates {
final eventService = ref.watch(eventServiceProvider); final eventService = ref.watch(eventServiceProvider);
final subscriptions = [ final subscriptions = [
eventService.tabContentEvents.listen((event) async { eventService.tabContentEvents.listen(
await _onTabContentStateChange(event); (event) async {
}), await _onTabContentStateChange(event);
eventService.iconChangeEvents.listen((event) async { },
await _onIconChange(event); onError: (Object error, StackTrace stackTrace) {
}), logger.e('Error in tab content events', error: error, stackTrace: stackTrace);
eventService.thumbnailEvents.listen((event) async { },
await _onThumbnailChange(event); ),
}), eventService.iconChangeEvents.listen(
eventService.securityInfoEvents.listen((event) { (event) async {
_onSecurityInfoStateChange(event); await _onIconChange(event);
}), },
eventService.historyEvents.listen((event) { onError: (Object error, StackTrace stackTrace) {
_onHistoryStateChange(event); logger.e('Error in icon change events', error: error, stackTrace: stackTrace);
}), },
eventService.readerableEvents.listen((event) { ),
_onReaderableStateChange(event); eventService.thumbnailEvents.listen(
}), (event) async {
await _onThumbnailChange(event);
},
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in thumbnail events', error: error, stackTrace: stackTrace);
},
),
eventService.securityInfoEvents.listen(
(event) {
_onSecurityInfoStateChange(event);
},
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in security info events', error: error, stackTrace: stackTrace);
},
),
eventService.historyEvents.listen(
(event) {
_onHistoryStateChange(event);
},
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in history events', error: error, stackTrace: stackTrace);
},
),
eventService.readerableEvents.listen(
(event) {
_onReaderableStateChange(event);
},
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in readerable events', error: error, stackTrace: stackTrace);
},
),
eventService.findResultsEvent eventService.findResultsEvent
.debounceTime(const Duration(milliseconds: 25)) .debounceTime(const Duration(milliseconds: 25))
.listen((event) { .listen(
_onFindResultsChange(event); (event) {
}), _onFindResultsChange(event);
},
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in find results events', error: error, stackTrace: stackTrace);
},
),
]; ];
ref.listen( ref.listen(
@@ -41,7 +41,7 @@ final class TabStatesProvider
} }
} }
String _$tabStatesHash() => r'67b497304b4137ce0115a66f06f3622c86e5727d'; String _$tabStatesHash() => r'985d2eb87c59c3a113b8bbcf9d96267c42a2f543';
abstract class _$TabStates extends $Notifier<Map<String, TabState>> { abstract class _$TabStates extends $Notifier<Map<String, TabState>> {
Map<String, TabState> build(); Map<String, TabState> build();
@@ -23,6 +23,7 @@ import 'dart:ui';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/domain/entities/equatable_image.dart'; import 'package:weblibre/domain/entities/equatable_image.dart';
import 'package:weblibre/features/geckoview/domain/entities/states/web_extension.dart'; import 'package:weblibre/features/geckoview/domain/entities/states/web_extension.dart';
import 'package:weblibre/features/geckoview/domain/providers.dart'; import 'package:weblibre/features/geckoview/domain/providers.dart';
@@ -100,20 +101,40 @@ class WebExtensionsState extends _$WebExtensionsState {
final subscriptions = switch (actionType) { final subscriptions = switch (actionType) {
WebExtensionActionType.browser => [ WebExtensionActionType.browser => [
addonService.browserExtensionStream.listen((event) { addonService.browserExtensionStream.listen(
_onExtensionUpdate(event); (event) {
}), _onExtensionUpdate(event);
addonService.browserIconStream.listen((event) async { },
await _onIconChange(event); onError: (Object error, StackTrace stackTrace) {
}), logger.e('Error in browser extension stream', error: error, stackTrace: stackTrace);
},
),
addonService.browserIconStream.listen(
(event) async {
await _onIconChange(event);
},
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in browser icon stream', error: error, stackTrace: stackTrace);
},
),
], ],
WebExtensionActionType.page => [ WebExtensionActionType.page => [
addonService.pageExtensionStream.listen((event) { addonService.pageExtensionStream.listen(
_onExtensionUpdate(event); (event) {
}), _onExtensionUpdate(event);
addonService.pageIconStream.listen((event) async { },
await _onIconChange(event); onError: (Object error, StackTrace stackTrace) {
}), logger.e('Error in page extension stream', error: error, stackTrace: stackTrace);
},
),
addonService.pageIconStream.listen(
(event) async {
await _onIconChange(event);
},
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in page icon stream', error: error, stackTrace: stackTrace);
},
),
], ],
}; };
@@ -62,7 +62,7 @@ final class WebExtensionsStateProvider
} }
String _$webExtensionsStateHash() => String _$webExtensionsStateHash() =>
r'2664a50bb776197010e9da9b4e3a9379c849ba75'; r'd74f739a2f33395c10e30a81c8031f2f2630e418';
final class WebExtensionsStateFamily extends $Family final class WebExtensionsStateFamily extends $Family
with with
@@ -424,16 +424,21 @@ class TabRepository extends _$TabRepository {
final db = ref.watch(tabDatabaseProvider); final db = ref.watch(tabDatabaseProvider);
final tabAddedSub = eventSerivce.tabAddedStream.listen((tabId) async { final tabAddedSub = eventSerivce.tabAddedStream.listen(
final containerId = ref.read(selectedContainerProvider); (tabId) async {
await db.tabDao.insertTab( final containerId = ref.read(selectedContainerProvider);
tabId, await db.tabDao.insertTab(
parentId: const Value.absent(), tabId,
source: TabSource.addedEvent, parentId: const Value.absent(),
containerId: Value(containerId), source: TabSource.addedEvent,
isPrivate: const Value.absent(), containerId: Value(containerId),
); isPrivate: const Value.absent(),
}); );
},
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in tab added stream', error: error, stackTrace: stackTrace);
},
);
final containerSiteAssignementSub = eventSerivce.siteAssignementEvent.listen(( final containerSiteAssignementSub = eventSerivce.siteAssignementEvent.listen((
event, event,
@@ -522,20 +527,27 @@ class TabRepository extends _$TabRepository {
logger.w('Could not get tab for assignement ${tabState?.url}'); logger.w('Could not get tab for assignement ${tabState?.url}');
} }
} }
}); },
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in container site assignment stream', error: error, stackTrace: stackTrace);
},
);
final tabContentSub = tabContentService.tabContentStream.listen(( final tabContentSub = tabContentService.tabContentStream.listen(
content, (content) async {
) async { await db.tabDao.updateTabContent(
await db.tabDao.updateTabContent( content.tabId,
content.tabId, isProbablyReaderable: content.isProbablyReaderable,
isProbablyReaderable: content.isProbablyReaderable, extractedContentMarkdown: content.extractedContentMarkdown,
extractedContentMarkdown: content.extractedContentMarkdown, extractedContentPlain: content.extractedContentPlain,
extractedContentPlain: content.extractedContentPlain, fullContentMarkdown: content.fullContentMarkdown,
fullContentMarkdown: content.fullContentMarkdown, fullContentPlain: content.fullContentPlain,
fullContentPlain: content.fullContentPlain, );
); },
}); onError: (Object error, StackTrace stackTrace) {
logger.e('Error in tab content stream', error: error, stackTrace: stackTrace);
},
);
ref.listen( ref.listen(
fireImmediately: true, fireImmediately: true,
@@ -545,7 +557,7 @@ class TabRepository extends _$TabRepository {
await db.tabDao.touchTab(tabId, timestamp: DateTime.now()); await db.tabDao.touchTab(tabId, timestamp: DateTime.now());
} }
}, },
onError: (error, stackTrace) { onError: (Object error, StackTrace stackTrace) {
logger.e( logger.e(
'Error listening to selectedTabProvider', 'Error listening to selectedTabProvider',
error: error, error: error,
@@ -565,7 +577,7 @@ class TabRepository extends _$TabRepository {
await db.tabDao.syncTabs(retainTabIds: next.value); await db.tabDao.syncTabs(retainTabIds: next.value);
} }
}, },
onError: (error, stackTrace) { onError: (Object error, StackTrace stackTrace) {
logger.e( logger.e(
'Error listening to tabListProvider', 'Error listening to tabListProvider',
error: error, error: error,
@@ -591,7 +603,7 @@ class TabRepository extends _$TabRepository {
await db.tabDao.updateTabs(debounceStartValue, next); await db.tabDao.updateTabs(debounceStartValue, next);
}); });
}, },
onError: (error, stackTrace) { onError: (Object error, StackTrace stackTrace) {
logger.e( logger.e(
'Error listening to tabStatesProvider', 'Error listening to tabStatesProvider',
error: error, error: error,
@@ -41,7 +41,7 @@ final class TabRepositoryProvider
} }
} }
String _$tabRepositoryHash() => r'763f5ea734abc854dfdc0c08d3f266fbd94bf931'; String _$tabRepositoryHash() => r'ff8ce73a65d408587cfb41e1920332d15ddff55f';
abstract class _$TabRepository extends $Notifier<void> { abstract class _$TabRepository extends $Notifier<void> {
void build(); void build();
@@ -29,6 +29,7 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.dart
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart'; import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/providers/bookmarks.dart'; import 'package:weblibre/features/geckoview/features/bookmarks/domain/providers/bookmarks.dart';
@@ -405,7 +406,8 @@ class BookmarkListScreen extends HookConsumerWidget {
if (context.mounted) { if (context.mounted) {
showInfoMessage(context, 'Imported $count bookmarks successfully'); showInfoMessage(context, 'Imported $count bookmarks successfully');
} }
} catch (e) { } catch (e, s) {
logger.e('Bookmark import failed', error: e, stackTrace: s);
if (context.mounted) { if (context.mounted) {
showErrorMessage(context, 'Import failed: $e'); showErrorMessage(context, 'Import failed: $e');
} }
@@ -454,7 +456,8 @@ class BookmarkListScreen extends HookConsumerWidget {
if (context.mounted) { if (context.mounted) {
showInfoMessage(context, 'Bookmarks exported successfully'); showInfoMessage(context, 'Bookmarks exported successfully');
} }
} catch (e) { } catch (e, s) {
logger.e('Bookmark export failed', error: e, stackTrace: s);
if (context.mounted) { if (context.mounted) {
showErrorMessage(context, 'Export failed: $e'); showErrorMessage(context, 'Export failed: $e');
} }
@@ -22,6 +22,7 @@ import 'dart:async';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/data/models/received_intent_parameter.dart'; import 'package:weblibre/data/models/received_intent_parameter.dart';
import 'package:weblibre/features/app_widget/domain/services/home_widget.dart'; import 'package:weblibre/features/app_widget/domain/services/home_widget.dart';
@@ -67,7 +68,10 @@ class EngineBoundIntentStream extends _$EngineBoundIntentStream {
appWidgetLaunchStream.transform(_contentParserTransformer), appWidgetLaunchStream.transform(_contentParserTransformer),
]).listen( ]).listen(
controller.add, controller.add,
onError: controller.addError, onError: (Object error, StackTrace stackTrace) {
logger.e('Intent stream error', error: error, stackTrace: stackTrace);
controller.addError(error, stackTrace);
},
onDone: controller.close, onDone: controller.close,
); );
@@ -34,7 +34,7 @@ final class EngineBoundIntentStreamProvider
} }
String _$engineBoundIntentStreamHash() => String _$engineBoundIntentStreamHash() =>
r'ab9804f97e0222b2306683d2fcfa71d3d57ecd21'; r'618eb6431da93989de6f1780cba2993458b0014e';
abstract class _$EngineBoundIntentStream abstract class _$EngineBoundIntentStream
extends $StreamNotifier<SharedContent> { extends $StreamNotifier<SharedContent> {
@@ -302,7 +302,7 @@ class _BrowserViewState extends ConsumerState<BrowserView>
await router.push(route.location); await router.push(route.location);
} else { } else {
throw UnimplementedError(); throw UnimplementedError('Unknown quick action shortcut type');
} }
} }
}); });
@@ -21,6 +21,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/clear_site_data_dialog.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/clear_site_data_dialog.dart';
import 'package:weblibre/utils/ui_helper.dart'; import 'package:weblibre/utils/ui_helper.dart';
@@ -163,7 +164,8 @@ class ClearSiteDataSection extends HookConsumerWidget {
if (context.mounted) { if (context.mounted) {
showInfoMessage(context, 'Site data cleared'); showInfoMessage(context, 'Site data cleared');
} }
} catch (e) { } catch (e, s) {
logger.e('Failed to clear site data', error: e, stackTrace: s);
if (context.mounted) { if (context.mounted) {
showErrorMessage(context, 'Failed to clear site data: $e'); showErrorMessage(context, 'Failed to clear site data: $e');
} }
@@ -20,6 +20,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:skeletonizer/skeletonizer.dart'; import 'package:skeletonizer/skeletonizer.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/repositories/tracking_protection.dart'; import 'package:weblibre/features/geckoview/features/browser/domain/repositories/tracking_protection.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/tracking_protection_provider.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/tracking_protection_provider.dart';
@@ -102,7 +103,8 @@ class _TrackingProtectionTile extends ConsumerWidget {
// Reload tab to apply changes // Reload tab to apply changes
await ref.read(selectedTabSessionProvider).reload(); await ref.read(selectedTabSessionProvider).reload();
} catch (e) { } catch (e, s) {
logger.e('Failed to toggle tracking protection', error: e, stackTrace: s);
if (context.mounted) { if (context.mounted) {
showErrorMessage(context, 'Failed to toggle tracking protection: $e'); showErrorMessage(context, 'Failed to toggle tracking protection: $e');
} }
@@ -82,7 +82,7 @@ class _TabDraggable extends HookConsumerWidget {
sourceSearchQuery: switch (entity) { sourceSearchQuery: switch (entity) {
DefaultTabEntity _ => null, DefaultTabEntity _ => null,
final SearchResultTabEntity entity => entity.searchQuery, final SearchResultTabEntity entity => entity.searchQuery,
TabTreeEntity _ => throw UnimplementedError(), TabTreeEntity _ => throw UnimplementedError('TabTreeEntity not implemented in tab grid view'),
}, },
); );
}, [entity.tabId, activeTab, suggestedContainerId]); }, [entity.tabId, activeTab, suggestedContainerId]);
@@ -83,7 +83,7 @@ class _TabDraggable extends HookConsumerWidget {
sourceSearchQuery: switch (entity) { sourceSearchQuery: switch (entity) {
DefaultTabEntity _ => null, DefaultTabEntity _ => null,
final SearchResultTabEntity entity => entity.searchQuery, final SearchResultTabEntity entity => entity.searchQuery,
TabTreeEntity _ => throw UnimplementedError(), TabTreeEntity _ => throw UnimplementedError('TabTreeEntity not implemented in tab list view'),
}, },
); );
}, [entity.tabId, activeTab, suggestedContainerId]); }, [entity.tabId, activeTab, suggestedContainerId]);
@@ -7,6 +7,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/geckoview/domain/providers.dart'; import 'package:weblibre/features/geckoview/domain/providers.dart';
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
@@ -484,7 +485,8 @@ class TabViewHeader extends HookConsumerWidget {
: 'Container data cleared. ${tabs.length} tab(s) closed.', : 'Container data cleared. ${tabs.length} tab(s) closed.',
); );
} }
} catch (e) { } catch (e, s) {
logger.e('Failed to clear container data', error: e, stackTrace: s);
if (context.mounted) { if (context.mounted) {
ui_helper.showErrorMessage( ui_helper.showErrorMessage(
context, context,
@@ -171,7 +171,7 @@ class Section extends MultiSliver {
VisitType.download => const Text('Download'), VisitType.download => const Text('Download'),
VisitType.framedLink => const Text('Frame'), VisitType.framedLink => const Text('Frame'),
VisitType.reload => const Text('Page Reload'), VisitType.reload => const Text('Page Reload'),
VisitType.bookmark => throw UnimplementedError(), VisitType.bookmark => throw UnimplementedError('VisitType.bookmark chip display not implemented'),
}, },
), ),
Chip( Chip(
@@ -374,7 +374,7 @@ class HistoryScreen extends HookConsumerWidget {
VisitType.download => const Text('Downloads'), VisitType.download => const Text('Downloads'),
VisitType.framedLink => const Text('Frames'), VisitType.framedLink => const Text('Frames'),
VisitType.reload => const Text('Page Reloads'), VisitType.reload => const Text('Page Reloads'),
VisitType.bookmark => throw UnimplementedError(), VisitType.bookmark => throw UnimplementedError('VisitType.bookmark filter not implemented'),
}, },
), ),
), ),
@@ -21,6 +21,7 @@ import 'dart:typed_data';
import 'dart:ui'; import 'dart:ui';
import 'package:fast_equatable/hash.dart'; import 'package:fast_equatable/hash.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/domain/entities/equatable_image.dart'; import 'package:weblibre/domain/entities/equatable_image.dart';
import 'package:weblibre/utils/lru_cache.dart'; import 'package:weblibre/utils/lru_cache.dart';
@@ -64,8 +65,8 @@ Future<EquatableImage?> tryDecodeImage(
_cache.set(digest, image); _cache.set(digest, image);
return image; return image;
} }
} catch (e) { } catch (e, s) {
//swallow logger.w('Failed to decode image', error: e, stackTrace: s);
} }
return null; return null;
@@ -22,6 +22,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/repositories/tracking_protection.dart'; import 'package:weblibre/features/geckoview/features/browser/domain/repositories/tracking_protection.dart';
import 'package:weblibre/features/settings/presentation/dialogs/delete_all_exceptions_dialog.dart'; import 'package:weblibre/features/settings/presentation/dialogs/delete_all_exceptions_dialog.dart';
import 'package:weblibre/presentation/widgets/url_icon.dart'; import 'package:weblibre/presentation/widgets/url_icon.dart';
@@ -99,7 +100,8 @@ class TrackingProtectionExceptionsScreen extends HookConsumerWidget {
await ref await ref
.read(trackingProtectionRepositoryProvider.notifier) .read(trackingProtectionRepositoryProvider.notifier)
.removeAllExceptions(); .removeAllExceptions();
} catch (e) { } catch (e, s) {
logger.e('Failed to delete tracking protection exceptions', error: e, stackTrace: s);
if (context.mounted) { if (context.mounted) {
showErrorMessage(context, 'Failed to delete exceptions: $e'); showErrorMessage(context, 'Failed to delete exceptions: $e');
} }
@@ -116,7 +118,8 @@ class TrackingProtectionExceptionsScreen extends HookConsumerWidget {
await ref await ref
.read(trackingProtectionRepositoryProvider.notifier) .read(trackingProtectionRepositoryProvider.notifier)
.removeExceptionByUrl(exception.url); .removeExceptionByUrl(exception.url);
} catch (e) { } catch (e, s) {
logger.e('Failed to remove tracking protection exception', error: e, stackTrace: s);
if (context.mounted) { if (context.mounted) {
showErrorMessage(context, 'Failed to remove exception: $e'); showErrorMessage(context, 'Failed to remove exception: $e');
} }
@@ -111,8 +111,8 @@ class TorProxyService extends _$TorProxyService {
(setting) => (setting) =>
setting.bridge.type == setting.bridge.type ==
switch (torSettings.config) { switch (torSettings.config) {
TorConnectionConfig.auto => throw UnimplementedError(), TorConnectionConfig.auto => throw UnimplementedError('TorConnectionConfig.auto bridge type not supported'),
TorConnectionConfig.direct => throw UnimplementedError(), TorConnectionConfig.direct => throw UnimplementedError('TorConnectionConfig.direct does not use bridges'),
TorConnectionConfig.obfs4 => MoatTransportType.obfs4, TorConnectionConfig.obfs4 => MoatTransportType.obfs4,
TorConnectionConfig.snowflake => TorConnectionConfig.snowflake =>
MoatTransportType.snowflake, MoatTransportType.snowflake,
@@ -33,7 +33,7 @@ final class TorProxyServiceProvider
TorProxyService create() => TorProxyService(); TorProxyService create() => TorProxyService();
} }
String _$torProxyServiceHash() => r'0bf00b61bf7e739fe8c77696cf1b2a151d137671'; String _$torProxyServiceHash() => r'7b430ca32fbfc9ebebb1e52271c0183efdf60971';
abstract class _$TorProxyService extends $StreamNotifier<TorStatus> { abstract class _$TorProxyService extends $StreamNotifier<TorStatus> {
Stream<TorStatus> build(); Stream<TorStatus> build();
@@ -20,6 +20,7 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/features/geckoview/domain/providers.dart'; import 'package:weblibre/features/geckoview/domain/providers.dart';
import 'package:weblibre/features/user/data/providers.dart'; import 'package:weblibre/features/user/data/providers.dart';
@@ -49,11 +50,16 @@ class CacheRepository extends _$CacheRepository {
final db = ref.watch(userDatabaseProvider); final db = ref.watch(userDatabaseProvider);
final sub = eventService.iconUpdateEvents.listen((event) async { final sub = eventService.iconUpdateEvents.listen(
if (Uri.tryParse(event.url) case final Uri url) { (event) async {
await db.cacheDao.cacheIcon(url.origin, event.bytes); if (Uri.tryParse(event.url) case final Uri url) {
} await db.cacheDao.cacheIcon(url.origin, event.bytes);
}); }
},
onError: (Object error, StackTrace stackTrace) {
logger.e('Error in icon update events', error: error, stackTrace: stackTrace);
},
);
ref.onDispose(() async { ref.onDispose(() async {
await sub.cancel(); await sub.cancel();
@@ -41,7 +41,7 @@ final class CacheRepositoryProvider
} }
} }
String _$cacheRepositoryHash() => r'548b7cba9a23c21bba39f0918d7d90acaaf8d8e9'; String _$cacheRepositoryHash() => r'e3cd7461aefe9e034a663169cd81ab7f69c2640e';
abstract class _$CacheRepository extends $Notifier<void> { abstract class _$CacheRepository extends $Notifier<void> {
void build(); void build();
@@ -6,6 +6,7 @@ import 'package:path_provider/path_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:secure_archive/secure_archive.dart'; import 'package:secure_archive/secure_archive.dart';
import 'package:weblibre/core/filesystem.dart'; import 'package:weblibre/core/filesystem.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/domain/entities/profile.dart'; import 'package:weblibre/domain/entities/profile.dart';
import 'package:weblibre/features/user/domain/repositories/profile.dart'; import 'package:weblibre/features/user/domain/repositories/profile.dart';
@@ -95,8 +96,12 @@ class UserBackupService extends _$UserBackupService {
if (await outputDirectory.exists()) { if (await outputDirectory.exists()) {
await outputDirectory.delete(recursive: true); await outputDirectory.delete(recursive: true);
} }
} catch (_) { } catch (e, s) {
// Ignore cleanup errors logger.w(
'Failed to cleanup temporary backup directory: ${outputDirectory.path}',
error: e,
stackTrace: s,
);
} }
} }
} }
@@ -41,7 +41,7 @@ final class UserBackupServiceProvider
} }
} }
String _$userBackupServiceHash() => r'358e4c58703b14cff767184a4f658f7eac0454f6'; String _$userBackupServiceHash() => r'cbeffa04bb52d3c7d50a2eb38678750f2a0362cc';
abstract class _$UserBackupService extends $Notifier<void> { abstract class _$UserBackupService extends $Notifier<void> {
void build(); void build();
@@ -92,7 +92,7 @@ class FeedParser {
tags: tags, tags: tags,
); );
default: default:
throw Exception(); throw Exception('Unknown feed type in readGeneralData: ${_feed.runtimeType}');
} }
} }
@@ -209,7 +209,7 @@ class FeedParser {
); );
}).toList(); }).toList();
default: default:
throw Exception(); throw Exception('Unknown feed type in readArticles: ${_feed.runtimeType}');
} }
} }
} }
+15 -6
View File
@@ -24,6 +24,7 @@ import 'package:country_codes/country_codes.dart';
import 'package:dynamic_color/dynamic_color.dart'; import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart' import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'
show GeckoBrowserService, GeckoLoggingService, LogLevel; show GeckoBrowserService, GeckoLoggingService, LogLevel;
@@ -101,12 +102,20 @@ class _MainWidget extends HookConsumerWidget {
.read(engineSettingsRepositoryProvider.notifier) .read(engineSettingsRepositoryProvider.notifier)
.fetchSettings(); .fetchSettings();
await GeckoBrowserService().initialize( try {
filesystem.relativeProfilePath, await GeckoBrowserService().initialize(
kDebugMode ? LogLevel.debug : LogLevel.warn, filesystem.relativeProfilePath,
engineSettings.contentBlocking, kDebugMode ? LogLevel.debug : LogLevel.warn,
engineSettings.addonCollection, engineSettings.contentBlocking,
); engineSettings.addonCollection,
);
} on PlatformException catch (e, s) {
logger.e('Platform exception during Gecko initialization', error: e, stackTrace: s);
rethrow;
} catch (e, s) {
logger.e('Failed to initialize Gecko browser service', error: e, stackTrace: s);
rethrow;
}
await ref.read(appInitializationServiceProvider.notifier).initialize(); await ref.read(appInitializationServiceProvider.notifier).initialize();
+3 -1
View File
@@ -20,6 +20,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/utils/clipboard.dart'; import 'package:weblibre/utils/clipboard.dart';
/// Creates a floating snackbar. /// Creates a floating snackbar.
@@ -160,7 +161,8 @@ Future<void> launchUrlFeedback(
showErrorMessage(context, 'Could not launch URL ($url)'); showErrorMessage(context, 'Could not launch URL ($url)');
} }
} }
} catch (e) { } catch (e, s) {
logger.e('Failed to launch URL: $url', error: e, stackTrace: s);
if (context.mounted) { if (context.mounted) {
showErrorMessage(context, 'Could not launch URL ($url)'); showErrorMessage(context, 'Could not launch URL ($url)');
} }
@@ -5,6 +5,7 @@
*/ */
import 'dart:async'; import 'dart:async';
import 'dart:developer' as developer;
import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart'; import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart';
@@ -32,7 +33,13 @@ class GeckoContainerProxyService {
return await _apiInstance.healthcheck().timeout( return await _apiInstance.healthcheck().timeout(
const Duration(milliseconds: 250), const Duration(milliseconds: 250),
); );
} catch (_) { } catch (e, s) {
developer.log(
'Container proxy healthcheck failed',
error: e,
stackTrace: s,
name: 'GeckoContainerProxyService',
);
return Future.value(false); return Future.value(false);
} }
} }
@@ -50,7 +50,7 @@ class GeckoHistoryService {
case VisitType.download: case VisitType.download:
return _api.deleteDownload(info.contentId!); return _api.deleteDownload(info.contentId!);
case VisitType.bookmark: case VisitType.bookmark:
throw UnimplementedError(); throw UnimplementedError('VisitType.bookmark deletion not implemented');
} }
} }
@@ -5,6 +5,7 @@
*/ */
import 'dart:async'; import 'dart:async';
import 'dart:developer' as developer;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
@@ -66,7 +67,7 @@ class _GeckoViewState extends State<GeckoView> {
final result = await browserService.showNativeFragment(); final result = await browserService.showNativeFragment();
if (result) { if (result) {
debugPrint('Fragment ATTACHED after $attempt tries'); developer.log('Fragment ATTACHED after $attempt tries', name: 'GeckoView');
return true; return true;
} }
@@ -75,7 +76,7 @@ class _GeckoViewState extends State<GeckoView> {
} }
} }
debugPrint('Fragment FAILED after $maxRetries tries'); developer.log('Fragment FAILED after $maxRetries tries', name: 'GeckoView', level: 900);
return false; return false;
} }