feat: add sentry

This commit is contained in:
problematicconsumer
2023-09-17 00:23:31 +03:30
parent 2668684f15
commit f4177da9f9
23 changed files with 205 additions and 332 deletions

View File

@@ -1,8 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
@@ -21,18 +19,17 @@ import 'package:hiddify/services/service_providers.dart';
import 'package:hiddify/utils/utils.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:loggy/loggy.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:window_manager/window_manager.dart';
final _loggy = Loggy('bootstrap');
final _stopWatch = Stopwatch();
const _testCrashReport = false;
Future<void> lazyBootstrap(
WidgetsBinding widgetsBinding,
Environment env,
) async {
_stopWatch.start();
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
if (PlatformUtils.isDesktop) await windowManager.ensureInitialized();
@@ -45,11 +42,28 @@ Future<void> lazyBootstrap(
],
);
if (container.read(autoCrashReportProvider) && !kDebugMode) {
_loggy.debug("initializing crashlytics");
await initCrashlytics();
}
final enableAnalytics = container.read(enableAnalyticsProvider);
await SentryFlutter.init(
(options) {
if ((enableAnalytics && !kDebugMode) || _testCrashReport) {
options.dsn = Environment.sentryDSN;
} else {
options.dsn = "";
}
options.environment = env.toString();
options.debug = kDebugMode;
},
appRunner: () => _lazyBootstrap(widgetsBinding, container, env),
);
}
Future<void> _lazyBootstrap(
WidgetsBinding widgetsBinding,
ProviderContainer container,
Environment env,
) async {
final debug = container.read(debugModeNotifierProvider) || kDebugMode;
final filesEditor = container.read(filesEditorServiceProvider);
@@ -59,7 +73,6 @@ Future<void> lazyBootstrap(
_loggy.info(
"os: [${Platform.operatingSystem}](${Platform.operatingSystemVersion}), processor count [${Platform.numberOfProcessors}]",
);
_loggy.info("basic setup took [${_stopWatch.elapsedMilliseconds}]ms");
final silentStart = container.read(silentStartNotifierProvider);
if (silentStart) {
@@ -81,8 +94,6 @@ Future<void> lazyBootstrap(
);
if (!silentStart) FlutterNativeSplash.remove();
_stopWatch.stop();
_loggy.info("bootstrapping took [${_stopWatch.elapsedMilliseconds}]ms");
}
void initLoggers(
@@ -102,18 +113,6 @@ void initLoggers(
);
}
Future<void> initCrashlytics() async {
switch (Platform.operatingSystem) {
case "android" || "ios" || "macos":
await Firebase.initializeApp();
FlutterError.onError =
FirebaseCrashlytics.instance.recordFlutterFatalError;
default:
_loggy.debug("platform is not supported for crashlytics");
return;
}
}
Future<void> initAppServices(
Result Function<Result>(ProviderListenable<Result>) read,
) async {

View File

@@ -7,6 +7,23 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'general_prefs.g.dart';
@Riverpod(keepAlive: true)
class IntroCompleted extends _$IntroCompleted {
late final _pref = Pref(
ref.watch(sharedPreferencesProvider),
"intro_completed",
false,
);
@override
bool build() => _pref.getValue();
Future<void> update(bool value) {
state = value;
return _pref.update(value);
}
}
@Riverpod(keepAlive: true)
class SilentStartNotifier extends _$SilentStartNotifier {
late final _pref =
@@ -22,11 +39,11 @@ class SilentStartNotifier extends _$SilentStartNotifier {
}
@Riverpod(keepAlive: true)
class AutoCrashReport extends _$AutoCrashReport {
class EnableAnalytics extends _$EnableAnalytics {
late final _pref = Pref(
ref.watch(sharedPreferencesProvider),
"auto_crash_report",
false,
"enable_analytics",
true,
);
@override

View File

@@ -3,6 +3,8 @@ import 'package:dartx/dartx.dart';
enum Environment {
prod,
dev;
static const sentryDSN = String.fromEnvironment("sentry_dsn");
}
enum Release {

View File

@@ -1,5 +1,6 @@
export 'app_update_notifier.dart';
export 'confirmation_dialogs.dart';
export 'custom_app_bar.dart';
export 'general_pref_tiles.dart';
export 'profile_tile.dart';
export 'qr_code_scanner_screen.dart';

View File

@@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
import 'package:go_router/go_router.dart';
import 'package:hiddify/core/core_providers.dart';
import 'package:hiddify/core/prefs/prefs.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class LocalePrefTile extends HookConsumerWidget {
const LocalePrefTile({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final t = ref.watch(translationsProvider);
final locale = ref.watch(localeNotifierProvider);
return ListTile(
title: Text(t.settings.general.locale),
subtitle: Text(
LocaleNamesLocalizationsDelegate.nativeLocaleNames[locale.name] ??
locale.name,
),
leading: const Icon(Icons.language),
onTap: () async {
final selectedLocale = await showDialog<AppLocale>(
context: context,
builder: (context) {
return SimpleDialog(
title: Text(t.settings.general.locale),
children: AppLocale.values
.map(
(e) => RadioListTile(
title: Text(
LocaleNamesLocalizationsDelegate
.nativeLocaleNames[e.name] ??
e.name,
),
value: e,
groupValue: locale,
onChanged: (e) => context.pop(e),
),
)
.toList(),
);
},
);
if (selectedLocale != null) {
await ref
.read(localeNotifierProvider.notifier)
.update(selectedLocale);
}
},
);
}
}
class EnableAnalyticsPrefTile extends HookConsumerWidget {
const EnableAnalyticsPrefTile({
super.key,
this.onChanged,
});
final ValueChanged<bool>? onChanged;
@override
Widget build(BuildContext context, WidgetRef ref) {
final t = ref.watch(translationsProvider);
final autoReport = ref.watch(enableAnalyticsProvider);
return SwitchListTile(
title: Text(t.settings.general.enableAnalytics),
subtitle: Text(
t.settings.general.enableAnalyticsMsg,
style: Theme.of(context).textTheme.bodySmall,
),
secondary: const Icon(Icons.bug_report),
value: autoReport,
onChanged: (value) async {
if (onChanged != null) {
return onChanged!(value);
}
return ref.read(enableAnalyticsProvider.notifier).update(value);
},
);
}
}

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
import 'package:go_router/go_router.dart';
import 'package:hiddify/core/core_providers.dart';
import 'package:hiddify/core/prefs/prefs.dart';
import 'package:hiddify/features/common/common.dart';
import 'package:hiddify/features/settings/widgets/theme_mode_switch_button.dart';
import 'package:hiddify/services/auto_start_service.dart';
import 'package:hiddify/utils/utils.dart';
@@ -15,47 +15,31 @@ class GeneralSettingTiles extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final t = ref.watch(translationsProvider);
final locale = ref.watch(localeNotifierProvider);
final theme = ref.watch(themeProvider);
return Column(
children: [
ListTile(
title: Text(t.settings.general.locale),
subtitle: Text(
LocaleNamesLocalizationsDelegate.nativeLocaleNames[locale.name] ??
locale.name,
),
leading: const Icon(Icons.language),
onTap: () async {
final selectedLocale = await showDialog<AppLocale>(
const LocalePrefTile(),
EnableAnalyticsPrefTile(
onChanged: (value) async {
await showDialog<bool>(
context: context,
builder: (context) {
return SimpleDialog(
title: Text(t.settings.general.locale),
children: AppLocale.values
.map(
(e) => RadioListTile(
title: Text(
LocaleNamesLocalizationsDelegate
.nativeLocaleNames[e.name] ??
e.name,
),
value: e,
groupValue: locale,
onChanged: (e) => context.pop(e),
),
)
.toList(),
return AlertDialog(
title: Text(t.settings.general.enableAnalytics),
content: Text(t.settings.requiresRestartMsg),
actions: [
TextButton(
onPressed: () => context.pop(true),
child: Text(
MaterialLocalizations.of(context).okButtonLabel,
),
),
],
);
},
);
if (selectedLocale != null) {
await ref
.read(localeNotifierProvider.notifier)
.update(selectedLocale);
}
return ref.read(enableAnalyticsProvider.notifier).update(value);
},
),
ListTile(