diff --git a/lib/inventree/sentry.dart b/lib/inventree/sentry.dart new file mode 100644 index 00000000..ba3c1b2b --- /dev/null +++ b/lib/inventree/sentry.dart @@ -0,0 +1,140 @@ +import 'dart:io'; + +import 'package:device_info/device_info.dart'; +import 'package:package_info/package_info.dart'; + +import 'package:sentry_flutter/sentry_flutter.dart'; + +import '../api.dart'; + +Future> getDeviceInfo() async { + + // Extract device information + final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + + Map device_info = {}; + + // Extract some platform information + if (Platform.isIOS) { + final iosDeviceInfo = await deviceInfo.iosInfo; + + device_info = { + 'name': iosDeviceInfo.name, + 'model': iosDeviceInfo.model, + 'systemName': iosDeviceInfo.systemName, + 'systemVersion': iosDeviceInfo.systemVersion, + 'localizedModel': iosDeviceInfo.localizedModel, + 'utsname': iosDeviceInfo.utsname.sysname, + 'identifierForVendor': iosDeviceInfo.identifierForVendor, + 'isPhysicalDevice': iosDeviceInfo.isPhysicalDevice, + }; + + } else if (Platform.isAndroid) { + final androidDeviceInfo = await deviceInfo.androidInfo; + + device_info = { + 'type': androidDeviceInfo.type, + 'model': androidDeviceInfo.model, + 'device': androidDeviceInfo.device, + 'id': androidDeviceInfo.id, + 'androidId': androidDeviceInfo.androidId, + 'brand': androidDeviceInfo.brand, + 'display': androidDeviceInfo.display, + 'hardware': androidDeviceInfo.hardware, + 'manufacturer': androidDeviceInfo.manufacturer, + 'product': androidDeviceInfo.product, + 'version': androidDeviceInfo.version.release, + 'supported32BitAbis': androidDeviceInfo.supported32BitAbis, + 'supported64BitAbis': androidDeviceInfo.supported64BitAbis, + 'supportedAbis': androidDeviceInfo.supportedAbis, + 'isPhysicalDevice': androidDeviceInfo.isPhysicalDevice, + }; + } + + return device_info; +} + + +Map getServerInfo() => { + "version": InvenTreeAPI().version, +}; + + +Future> getAppInfo() async { + // Add app info + final package_info = await PackageInfo.fromPlatform(); + + return { + "name": package_info.appName, + "build": package_info.buildNumber, + "version": package_info.version, + "package": package_info.packageName, + }; +} + + +bool isInDebugMode() { + bool inDebugMode = false; + + assert(inDebugMode = true); + + return inDebugMode; +} + + +Future sentryReportError(dynamic error, dynamic stackTrace) async { + + print('Caught error: $error'); + + // Errors thrown in development mode are unlikely to be interesting. You can + // check if you are running in dev mode using an assertion and omit sending + // the report. + if (isInDebugMode()) { + print(stackTrace); + print('In dev mode. Not sending report to Sentry.io.'); + return; + } + + print('Reporting to Sentry.io...'); + + final server_info = getServerInfo(); + final app_info = await getAppInfo(); + final device_info = await getDeviceInfo(); + + Sentry.configureScope((scope) { + scope.setExtra("server", server_info); + scope.setExtra("app", app_info); + scope.setExtra("device", device_info); + }); + + Sentry.captureException(error, stackTrace: stackTrace).catchError((error) { + print("Error uploading information to Sentry.io:"); + print(error); + }).then((response) { + print("Uploaded information to Sentry.io : ${response.toString()}"); + }); +} + + +Future sentryReportMessage(String message) async { + + final server_info = getServerInfo(); + final app_info = await getAppInfo(); + final device_info = await getDeviceInfo(); + + print("Sending user message to Sentry"); + + Sentry.configureScope((scope) { + scope.setExtra("server", server_info); + scope.setExtra("app", app_info); + scope.setExtra("device", device_info); + }); + + final sentryId = await Sentry.captureMessage(message).catchError((error) { + print("Error uploading sentry messages..."); + print(error); + return null; + }); + + return sentryId != null; +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 9d8ffb9e..6d12c06f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:InvenTree/inventree/sentry.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -9,106 +10,13 @@ import 'package:InvenTree/widget/home.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:device_info/device_info.dart'; -import 'package:package_info/package_info.dart'; - import 'dsn.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -bool isInDebugMode() { - bool inDebugMode = false; - assert(inDebugMode = true); - return inDebugMode; -} -Future _reportError(dynamic error, dynamic stackTrace) async { - - print('Caught error: $error'); - - // Errors thrown in development mode are unlikely to be interesting. You can - // check if you are running in dev mode using an assertion and omit sending - // the report. - if (isInDebugMode()) { - print(stackTrace); - print('In dev mode. Not sending report to Sentry.io.'); - return; - } - - print('Reporting to Sentry.io...'); - - // Extract device information - final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); - - Map device_info = {}; - - // Extract some platform information - if (Platform.isIOS) { - final iosDeviceInfo = await deviceInfo.iosInfo; - - device_info = { - 'name': iosDeviceInfo.name, - 'model': iosDeviceInfo.model, - 'systemName': iosDeviceInfo.systemName, - 'systemVersion': iosDeviceInfo.systemVersion, - 'localizedModel': iosDeviceInfo.localizedModel, - 'utsname': iosDeviceInfo.utsname.sysname, - 'identifierForVendor': iosDeviceInfo.identifierForVendor, - 'isPhysicalDevice': iosDeviceInfo.isPhysicalDevice, - }; - - } else if (Platform.isAndroid) { - final androidDeviceInfo = await deviceInfo.androidInfo; - - device_info = { - 'type': androidDeviceInfo.type, - 'model': androidDeviceInfo.model, - 'device': androidDeviceInfo.device, - 'id': androidDeviceInfo.id, - 'androidId': androidDeviceInfo.androidId, - 'brand': androidDeviceInfo.brand, - 'display': androidDeviceInfo.display, - 'hardware': androidDeviceInfo.hardware, - 'manufacturer': androidDeviceInfo.manufacturer, - 'product': androidDeviceInfo.product, - 'version': androidDeviceInfo.version.release, - 'supported32BitAbis': androidDeviceInfo.supported32BitAbis, - 'supported64BitAbis': androidDeviceInfo.supported64BitAbis, - 'supportedAbis': androidDeviceInfo.supportedAbis, - 'isPhysicalDevice': androidDeviceInfo.isPhysicalDevice, - }; - } - - // Add app info - final package_info = await PackageInfo.fromPlatform(); - - Map app_version_info = { - "name": package_info.appName, - "build": package_info.buildNumber, - "version": package_info.version, - "package": package_info.packageName, - }; - - // Add server info (anonymized) - Map server_info = { - "version": InvenTreeAPI().version, - }; - - Sentry.configureScope((scope) { - scope.setExtra("server", server_info); - scope.setExtra("app", app_version_info); - scope.setExtra("device", device_info); - }); - - Sentry.captureException(error, stackTrace: stackTrace).catchError((error) { - print("Error uploading information to Sentry.io:"); - print(error); - }).then((response) { - print("Uploaded information to Sentry.io : ${response.toString()}"); - }); -} void main() async { @@ -138,7 +46,7 @@ void main() async { runApp(InvenTreeApp()); }, (Object error, StackTrace stackTrace) { - _reportError(error, stackTrace); + sentryReportError(error, stackTrace); }); } diff --git a/lib/settings/settings.dart b/lib/settings/settings.dart index 6610bd24..96bd03c0 100644 --- a/lib/settings/settings.dart +++ b/lib/settings/settings.dart @@ -1,16 +1,16 @@ +import 'package:InvenTree/inventree/sentry.dart'; import 'package:InvenTree/settings/about.dart'; import 'package:InvenTree/settings/login.dart'; -import 'package:InvenTree/settings/release.dart'; import 'package:InvenTree/user_profile.dart'; -import 'package:InvenTree/preferences.dart'; +import 'package:InvenTree/widget/dialogs.dart'; +import 'package:InvenTree/widget/snacks.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; - +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:InvenTree/api.dart'; import 'login.dart'; import 'package:package_info/package_info.dart'; @@ -25,9 +25,14 @@ class InvenTreeSettingsWidget extends StatefulWidget { class _InvenTreeSettingsState extends State { + + final _scaffoldKey = GlobalKey(); + final _bugKey = GlobalKey(); + @override Widget build(BuildContext context) { return Scaffold( + key: _scaffoldKey, appBar: AppBar( title: Text(I18N.of(context).settings), ), @@ -53,7 +58,7 @@ class _InvenTreeSettingsState extends State { title: Text(I18N.of(context).reportBug), subtitle: Text("Report bug or suggest new feature"), leading: FaIcon(FontAwesomeIcons.bug), - onTap: null, + onTap: _reportBug, ), ] ).toList() @@ -76,4 +81,42 @@ class _InvenTreeSettingsState extends State { MaterialPageRoute(builder: (context) => InvenTreeAboutWidget(info))); }); } + + void _sendReport(String message) async { + + bool result = await sentryReportMessage(message); + + if (result) { + showSnackIcon(_scaffoldKey, "Uploaded report", success: true); + } else { + showSnackIcon(_scaffoldKey, "Report upload failed", success: false); + } + } + + void _reportBug() async { + + TextEditingController _controller = TextEditingController(); + + _controller.clear(); + + showFormDialog( + context, + "Upload Bug Report", + key: _bugKey, + callback: () { + _sendReport(_controller.text); + }, + fields: [ + TextField( + decoration: InputDecoration( + hintText: "Enter bug report details", + ), + keyboardType: TextInputType.multiline, + maxLines: null, + controller: _controller + ), + ] + ); + + } } \ No newline at end of file