feat: add sentry
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,6 +3,8 @@ import 'package:dartx/dartx.dart';
|
||||
enum Environment {
|
||||
prod,
|
||||
dev;
|
||||
|
||||
static const sentryDSN = String.fromEnvironment("sentry_dsn");
|
||||
}
|
||||
|
||||
enum Release {
|
||||
|
||||
@@ -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';
|
||||
|
||||
87
lib/features/common/general_pref_tiles.dart
Normal file
87
lib/features/common/general_pref_tiles.dart
Normal 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);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user