1
0
mirror of https://github.com/go-vikunja/app synced 2025-05-23 18:08:09 +00:00

feat: add sentry reporting (opt-in)

fix: theme is now always applied right away
This commit is contained in:
Benimautner 2024-09-26 13:31:26 +02:00
parent 459171d41a
commit 9800318df7
5 changed files with 157 additions and 41 deletions

View File

@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:vikunja_app/api/task_implementation.dart';
import 'package:vikunja_app/api/client.dart';
import 'package:vikunja_app/service/services.dart';
@ -126,24 +127,21 @@ void main() async {
));
}
final ValueNotifier<bool> updateTheme = ValueNotifier(false);
class ThemeModel with ChangeNotifier {
FlutterThemeMode _themeMode = FlutterThemeMode.light;
FlutterThemeMode get themeMode => _themeMode;
class VikunjaApp extends StatelessWidget {
final Widget home;
final GlobalKey<NavigatorState>? navkey;
void set themeMode(FlutterThemeMode mode) {
_themeMode = mode;
notifyListeners();
}
const VikunjaApp({Key? key, required this.home, this.navkey})
: super(key: key);
void notify() {
notifyListeners();
}
Future<ThemeData> getThemedata() async {
FlutterThemeMode themeMode = FlutterThemeMode.light;
try {
SettingsManager manager = SettingsManager(new FlutterSecureStorage());
themeMode = await manager.getThemeMode();
} catch (e) {
print("Failed to get theme mode: $e");
}
switch (themeMode) {
ThemeData get themeData {
switch (_themeMode) {
case FlutterThemeMode.dark:
return buildVikunjaDarkTheme();
case FlutterThemeMode.materialYouLight:
@ -155,30 +153,94 @@ class VikunjaApp extends StatelessWidget {
}
}
ThemeData getWithColorScheme(
ColorScheme? lightTheme, ColorScheme? darkTheme) {
switch (_themeMode) {
case FlutterThemeMode.dark:
return buildVikunjaDarkTheme().copyWith(colorScheme: darkTheme);
case FlutterThemeMode.materialYouLight:
return buildVikunjaMaterialLightTheme()
.copyWith(colorScheme: lightTheme);
case FlutterThemeMode.materialYouDark:
return buildVikunjaMaterialDarkTheme().copyWith(colorScheme: darkTheme);
default:
return buildVikunjaTheme().copyWith(colorScheme: lightTheme);
}
}
}
ThemeModel themeModel = ThemeModel();
class VikunjaApp extends StatelessWidget {
final Widget home;
final GlobalKey<NavigatorState>? navkey;
bool sentryEnabled = false;
bool sentyInitialized = false;
VikunjaApp({Key? key, required this.home, this.navkey}) : super(key: key);
Future<void> getLaunchData() async {
try {
SettingsManager manager = SettingsManager(new FlutterSecureStorage());
await manager.getThemeMode().then((themeMode) {
themeModel.themeMode = themeMode;
});
sentryEnabled = await manager.getSentryEnabled();
} catch (e) {
print("Failed to get theme mode: $e");
return Future.value(false);
}
return Future.value(true);
}
@override
Widget build(BuildContext context) {
return new ValueListenableBuilder(
valueListenable: updateTheme,
builder: (_, mode, __) {
return FutureBuilder<ThemeData>(
future: getThemedata(),
builder: (BuildContext context, AsyncSnapshot<ThemeData> data) {
return new ListenableBuilder(
listenable: themeModel,
builder: (_, mode) {
return FutureBuilder<void>(
future: getLaunchData(),
builder: (BuildContext context, data) {
if (data.hasData) {
return new DynamicColorBuilder(
builder: (lightTheme, darkTheme) {
ThemeData? themeData = data.data;
if (data.data == FlutterThemeMode.materialYouLight)
themeData = themeData?.copyWith(colorScheme: lightTheme);
else if (data.data == FlutterThemeMode.materialYouDark)
themeData = themeData?.copyWith(colorScheme: darkTheme);
return MaterialApp(
title: 'Vikunja',
theme: themeData,
scaffoldMessengerKey: globalSnackbarKey,
navigatorKey: navkey,
// <= this
home: this.home,
);
if (sentryEnabled) {
if (!sentyInitialized) {
sentyInitialized = true;
print("sentry enabled");
SentryFlutter.init((options) {
options.dsn =
'https://a09618e3bb30e03b93233c21973df869@o1047380.ingest.us.sentry.io/4507995557134336';
options.tracesSampleRate = 1.0;
options.profilesSampleRate = 1.0;
}).then((_) {
FlutterError.onError = (details) async {
print("sending to sentry");
await Sentry.captureException(
details.exception,
stackTrace: details.stack,
);
FlutterError.presentError(details);
};
PlatformDispatcher.instance.onError = (error, stack) {
print("sending to sentry (platform)");
Sentry.captureException(error, stackTrace: stack);
FlutterError.presentError(FlutterErrorDetails(
exception: error, stack: stack));
return false;
};
});
}
return SentryWidget(
child: buildMaterialApp(themeModel.getWithColorScheme(
lightTheme, darkTheme)));
} else {
sentyInitialized = false;
}
return buildMaterialApp(
themeModel.getWithColorScheme(lightTheme, darkTheme));
});
} else {
return Center(child: CircularProgressIndicator());
@ -186,4 +248,15 @@ class VikunjaApp extends StatelessWidget {
});
});
}
Widget buildMaterialApp(ThemeData? themeData) {
return MaterialApp(
title: 'Vikunja',
theme: themeData,
scaffoldMessengerKey: globalSnackbarKey,
navigatorKey: navkey,
// <= this
home: this.home,
);
}
}

View File

@ -17,6 +17,7 @@ class SettingsPageState extends State<SettingsPage> {
List<Project>? projectList;
int? defaultProject;
bool? ignoreCertificates;
bool? sentryEnabled;
bool? getVersionNotifications;
String? versionTag, newestVersionTag;
late TextEditingController durationTextController;
@ -32,14 +33,15 @@ class SettingsPageState extends State<SettingsPage> {
.getAll()
.then((value) => setState(() => projectList = value));
//VikunjaGlobal.of(context).projectService.getDefaultList().then((value) =>
// setState(
// () => defaultProject = value == null ? null : int.tryParse(value)));
VikunjaGlobal.of(context).settingsManager.getIgnoreCertificates().then(
(value) =>
setState(() => ignoreCertificates = value == "1" ? true : false));
VikunjaGlobal.of(context)
.settingsManager
.getSentryEnabled()
.then((value) => setState(() => sentryEnabled = value));
VikunjaGlobal.of(context).settingsManager.getVersionNotifications().then(
(value) => setState(
() => getVersionNotifications = value == "1" ? true : false));
@ -71,7 +73,6 @@ class SettingsPageState extends State<SettingsPage> {
@override
Widget build(BuildContext context) {
final global = VikunjaGlobal.of(context);
if (!initialized) init();
return new Scaffold(
appBar: AppBar(
@ -160,7 +161,7 @@ class SettingsPageState extends State<SettingsPage> {
onChanged: (FlutterThemeMode? value) {
VikunjaGlobal.of(context).settingsManager.setThemeMode(value!);
setState(() => themeMode = value);
updateTheme.value = true;
if (themeMode != null) themeModel.themeMode = themeMode!;
},
),
),
@ -175,6 +176,22 @@ class SettingsPageState extends State<SettingsPage> {
})
: ListTile(title: Text("...")),
Divider(),
sentryEnabled != null
? CheckboxListTile(
title: Text("Enable Sentry"),
subtitle: Text(
"Help us debug errors better and faster by sending bug reports to us directly. This is completely anonymous."),
value: sentryEnabled,
onChanged: (value) {
if (value == null) return;
setState(() => sentryEnabled = value);
VikunjaGlobal.of(context)
.settingsManager
.setSentryEnabled(value)
.then((_) => themeModel.notify());
})
: ListTile(title: Text("...")),
Divider(),
Padding(
padding: EdgeInsets.only(left: 15, right: 15),
child: Row(children: [

View File

@ -243,7 +243,8 @@ class SettingsManager {
"workmanager-duration": "0",
"recent-servers": "[\"https://try.vikunja.io\"]",
"theme_mode": "system",
"landing-page-due-date-tasks": "1"
"landing-page-due-date-tasks": "1",
"sentry-enabled": "0",
};
void applydefaults() {
@ -268,6 +269,14 @@ class SettingsManager {
_storage.write(key: "ignore-certificates", value: value ? "1" : "0");
}
Future<bool> getSentryEnabled() {
return _storage.read(key: "sentry-enabled").then((value) => value == "1");
}
Future<void> setSentryEnabled(bool value) {
return _storage.write(key: "sentry-enabled", value: value ? "1" : "0");
}
Future<bool> getLandingPageOnlyDueDateTasks() {
return _storage
.read(key: "landing-page-due-date-tasks")

View File

@ -1141,6 +1141,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.27.7"
sentry:
dependency: transitive
description:
name: sentry
sha256: "033287044a6644a93498969449d57c37907e56f5cedb17b88a3ff20a882261dd"
url: "https://pub.dev"
source: hosted
version: "8.9.0"
sentry_flutter:
dependency: "direct main"
description:
name: sentry_flutter
sha256: "3780b5a0bb6afd476857cfbc6c7444d969c29a4d9bd1aa5b6960aa76c65b737a"
url: "https://pub.dev"
source: hosted
version: "8.9.0"
shelf:
dependency: transitive
description:

View File

@ -42,6 +42,7 @@ dependencies:
cronet_http: ^1.2.0
package_info_plus: ^4.2.0
html_editor_enhanced: ^2.6.0
sentry_flutter: ^8.9.0
dev_dependencies:
flutter_test: