diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..db332bb --- /dev/null +++ b/.env.sample @@ -0,0 +1,2 @@ +SENTRY_DSN= +ENABLE_SENTRY=false \ No newline at end of file diff --git a/.gitignore b/.gitignore index 29a3a50..92658fc 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +# Env file +.env \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 0126911..6dba66d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,15 @@ import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:linkdy/constants/global_keys.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:linkdy/providers/app_status_provider.dart'; @@ -26,21 +29,36 @@ void main() async { HttpOverrides.global = MyHttpOverrides(); + await dotenv.load(fileName: '.env'); + final sharedPreferences = await SharedPreferences.getInstance(); final appInfo = await PackageInfo.fromPlatform(); - runApp( - TranslationProvider( - child: ProviderScope( - overrides: [ - sharedPreferencesProvider.overrideWithValue(sharedPreferences), - appInfoProvider.overrideWithValue(appInfo), - ], - child: const MyApp(), - ), - ), - ); + void startApp() => runApp( + TranslationProvider( + child: ProviderScope( + overrides: [ + sharedPreferencesProvider.overrideWithValue(sharedPreferences), + appInfoProvider.overrideWithValue(appInfo), + ], + child: const MyApp(), + ), + ), + ); + + if ((kReleaseMode && (dotenv.env['SENTRY_DSN'] != null && dotenv.env['SENTRY_DSN'] != "")) || + (dotenv.env['ENABLE_SENTRY'] == "true" && (dotenv.env['SENTRY_DSN'] != null && dotenv.env['SENTRY_DSN'] != ""))) { + SentryFlutter.init( + (options) { + options.dsn = dotenv.env['SENTRY_DSN']; + options.sendDefaultPii = false; + }, + appRunner: () => startApp(), + ); + } else { + startApp(); + } } class MyApp extends ConsumerWidget { diff --git a/lib/screens/settings/ui/settings.dart b/lib/screens/settings/ui/settings.dart index a78349a..e41b341 100644 --- a/lib/screens/settings/ui/settings.dart +++ b/lib/screens/settings/ui/settings.dart @@ -12,7 +12,6 @@ import 'package:linkdy/utils/open_url.dart'; import 'package:linkdy/constants/strings.dart'; import 'package:linkdy/constants/urls.dart'; import 'package:linkdy/i18n/strings.g.dart'; -import 'package:linkdy/providers/api_client_provider.dart'; class Settings extends ConsumerWidget { const Settings({Key? key}) : super(key: key); diff --git a/lib/services/api_client.dart b/lib/services/api_client.dart index 86f6527..c1e14ec 100644 --- a/lib/services/api_client.dart +++ b/lib/services/api_client.dart @@ -1,4 +1,5 @@ import 'package:dio/dio.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:linkdy/constants/enums.dart'; import 'package:linkdy/models/api_response.dart'; @@ -46,6 +47,7 @@ class ApiClientService { content: BookmarksResponse.fromJson(response.data), ); } catch (e, stackTrace) { + Sentry.captureException(e, stackTrace: stackTrace); return const ApiResponse(successful: false); } } @@ -63,6 +65,7 @@ class ApiClientService { content: CheckBookmark.fromJson(response.data), ); } catch (e, stackTrace) { + Sentry.captureException(e, stackTrace: stackTrace); return const ApiResponse(successful: false); } } @@ -78,6 +81,7 @@ class ApiClientService { content: Bookmark.fromJson(response.data), ); } catch (e, stackTrace) { + Sentry.captureException(e, stackTrace: stackTrace); return const ApiResponse(successful: false); } } @@ -97,6 +101,7 @@ class ApiClientService { content: TagsResponse.fromJson(response.data), ); } catch (e, stackTrace) { + Sentry.captureException(e, stackTrace: stackTrace); return const ApiResponse(successful: false); } } diff --git a/lib/utils/open_url.dart b/lib/utils/open_url.dart index 9a2e3f9..013699b 100644 --- a/lib/utils/open_url.dart +++ b/lib/utils/open_url.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:flutter_custom_tabs/flutter_custom_tabs.dart' as flutter_custom_tabs; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:url_launcher/url_launcher.dart' as url_launcher; void openUrl(String url) async { @@ -19,13 +20,13 @@ void openUrl(String url) async { ), ); } catch (e, stackTrace) { - // Sentry.captureException(e, stackTrace: stackTrace); + Sentry.captureException(e, stackTrace: stackTrace); } } else { try { url_launcher.launchUrl(Uri.parse(url)); } catch (e, stackTrace) { - // Sentry.captureException(e, stackTrace: stackTrace); + Sentry.captureException(e, stackTrace: stackTrace); } } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index fe56f8d..2528735 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,12 +7,16 @@ #include "generated_plugin_registrant.h" #include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) dynamic_color_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); + g_autoptr(FlPluginRegistrar) sentry_flutter_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin"); + sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 1836621..b942fff 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color + sentry_flutter url_launcher_linux ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8ac780d..522ca2c 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,6 +9,7 @@ import dynamic_color import flutter_inappwebview_macos import package_info_plus import path_provider_foundation +import sentry_flutter import share_plus import shared_preferences_foundation import url_launcher_macos @@ -18,6 +19,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) diff --git a/pubspec.lock b/pubspec.lock index ccdb571..9a3283c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -382,6 +382,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + flutter_dotenv: + dependency: "direct main" + description: + name: flutter_dotenv + sha256: "9357883bdd153ab78cbf9ffa07656e336b8bbb2b5a3ca596b0b27e119f7c7d77" + url: "https://pub.dev" + source: hosted + version: "5.1.0" flutter_inappwebview: dependency: "direct main" description: @@ -869,6 +877,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + sentry: + dependency: transitive + description: + name: sentry + sha256: d2ee9c850d876d285f22e2e662f400ec2438df9939fe4acd5d780df9841794ce + url: "https://pub.dev" + source: hosted + version: "7.16.1" + sentry_flutter: + dependency: "direct main" + description: + name: sentry_flutter + sha256: "5b428c189c825f16fb14e9166529043f06b965d5b59bfc3a1415e39c082398c0" + url: "https://pub.dev" + source: hosted + version: "7.16.1" share_plus: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index a216f75..2606b59 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,6 +52,8 @@ dependencies: package_info_plus: ^5.0.1 expandable: ^5.0.1 easy_autocomplete: ^1.6.0 + sentry_flutter: ^7.16.1 + flutter_dotenv: ^5.1.0 dev_dependencies: build_runner: ^2.4.8 @@ -97,6 +99,7 @@ flutter: - assets/icon/icon1024.png - assets/icon/icon-splash.png - assets/icon/icon1024-background.png + - .env # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 1a40a84..3d1dcc5 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,12 +7,15 @@ #include "generated_plugin_registrant.h" #include +#include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { DynamicColorPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); + SentryFlutterPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SentryFlutterPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 9feb042..f9947fb 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color + sentry_flutter share_plus url_launcher_windows )