diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index f371a673..721e693e 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -64,6 +64,7 @@ Future lazyBootstrap( options.enableNdkScopeSync = true; options.attachThreads = true; options.tracesSampleRate = 0.25; + options.enableUserInteractionTracing = true; options.addIntegration(sentryLogger); }, appRunner: () => _lazyBootstrap(widgetsBinding, container, env), @@ -100,6 +101,7 @@ Future _lazyBootstrap( runApp( ProviderScope( parent: container, + observers: [SentryRiverpodObserver()], child: SentryUserInteractionWidget( child: const AppView(), ), diff --git a/lib/features/home/widgets/connection_button.dart b/lib/features/home/widgets/connection_button.dart index a0f4cb61..afef3330 100644 --- a/lib/features/home/widgets/connection_button.dart +++ b/lib/features/home/widgets/connection_button.dart @@ -106,6 +106,7 @@ class _ConnectionButton extends StatelessWidget { width: 148, height: 148, child: Material( + key: const ValueKey("home_connection_button"), shape: const CircleBorder(), color: Colors.white, child: InkWell( diff --git a/lib/features/profiles/view/add_profile_modal.dart b/lib/features/profiles/view/add_profile_modal.dart index 2cadd71d..2dd47ac7 100644 --- a/lib/features/profiles/view/add_profile_modal.dart +++ b/lib/features/profiles/view/add_profile_modal.dart @@ -96,6 +96,7 @@ class AddProfileModal extends HookConsumerWidget { child: Row( children: [ _Button( + key: const ValueKey("add_from_clipboard_button"), label: t.profile.add.fromClipboard, icon: Icons.content_paste, size: buttonWidth, @@ -124,6 +125,7 @@ class AddProfileModal extends HookConsumerWidget { const Gap(buttonsGap), if (!PlatformUtils.isDesktop) _Button( + key: const ValueKey("add_by_qr_code_button"), label: t.profile.add.scanQr, icon: Icons.qr_code_scanner, size: buttonWidth, @@ -154,6 +156,7 @@ class AddProfileModal extends HookConsumerWidget { ) else _Button( + key: const ValueKey("add_manually_button"), label: t.profile.add.manually, icon: Icons.add, size: buttonWidth, @@ -176,6 +179,7 @@ class AddProfileModal extends HookConsumerWidget { child: SizedBox( height: 36, child: Material( + key: const ValueKey("add_manually_button"), elevation: 8, color: theme.colorScheme.surface, surfaceTintColor: theme.colorScheme.surfaceTint, @@ -225,6 +229,7 @@ class AddProfileModal extends HookConsumerWidget { class _Button extends StatelessWidget { const _Button({ + super.key, required this.label, required this.icon, required this.size, diff --git a/lib/utils/sentry_riverpod_observer.dart b/lib/utils/sentry_riverpod_observer.dart new file mode 100644 index 00000000..c9495196 --- /dev/null +++ b/lib/utils/sentry_riverpod_observer.dart @@ -0,0 +1,44 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; + +class SentryRiverpodObserver extends ProviderObserver { + void addBreadcrumb(String message, {Map? data}) { + Sentry.addBreadcrumb( + Breadcrumb( + category: "Provider", + message: message, + data: data, + ), + ); + } + + @override + void didAddProvider( + ProviderBase provider, + Object? value, + ProviderContainer container, + ) { + super.didAddProvider(provider, value, container); + addBreadcrumb( + 'Provider [${provider.name ?? provider.runtimeType}] was ADDED', + data: value != null ? {"initial-value": value} : null, + ); + } + + @override + void didUpdateProvider( + ProviderBase provider, + Object? previousValue, + Object? newValue, + ProviderContainer container, + ) { + super.didUpdateProvider(provider, previousValue, newValue, container); + addBreadcrumb( + 'Provider [${provider.name ?? provider.runtimeType}] was UPDATED', + data: { + "new-value": newValue, + "old-value": previousValue, + }, + ); + } +} diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 3e28a1fa..76320bd1 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -11,6 +11,7 @@ export 'number_formatters.dart'; export 'placeholders.dart'; export 'platform_utils.dart'; export 'sentry_loggy_integration.dart'; +export 'sentry_riverpod_observer.dart'; export 'text_utils.dart'; export 'uri_utils.dart'; export 'validators.dart';