From 0edbf56fb43a2815ad58d5775abf218bec95fd39 Mon Sep 17 00:00:00 2001 From: franzap <_@franzap.com> Date: Fri, 22 Nov 2024 17:46:58 -0300 Subject: [PATCH] Send system info, closes #149 --- lib/models/nostr_adapter.dart | 3 +- lib/screens/settings_screen.dart | 91 +++++++++++++++++++++---------- lib/utils/system_info.dart | 53 ++++++++++++------ lib/widgets/drawer_container.dart | 2 +- lib/widgets/error_container.dart | 7 +-- 5 files changed, 105 insertions(+), 51 deletions(-) diff --git a/lib/models/nostr_adapter.dart b/lib/models/nostr_adapter.dart index 3a9587c..0843631 100644 --- a/lib/models/nostr_adapter.dart +++ b/lib/models/nostr_adapter.dart @@ -115,7 +115,8 @@ mixin NostrAdapter> on Adapter { if (['apps', 'fileMetadatas'].contains(internalType)) { if (Platform.isAndroid) { - final info = await ref.read(systemInfoProvider.future); + final info = + await ref.read(systemInfoNotifierProvider.notifier).fetch(); params['#f'] = info.androidInfo.supportedAbis.map((a) => 'android-$a'); } } diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 03646a4..952cdaa 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:async_button_builder/async_button_builder.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_data/flutter_data.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_phoenix/flutter_phoenix.dart'; @@ -13,19 +12,20 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:path_provider/path_provider.dart'; import 'package:zapstore/main.data.dart'; -import 'package:zapstore/models/app.dart'; import 'package:zapstore/models/feedback.dart'; import 'package:zapstore/models/settings.dart'; import 'package:zapstore/utils/extensions.dart'; import 'package:zapstore/utils/system_info.dart'; import 'package:zapstore/widgets/app_drawer.dart'; +import 'package:http/http.dart' as http; class SettingsScreen extends HookConsumerWidget { const SettingsScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final systemInfoState = ref.watch(systemInfoProvider); + final systemInfoState = ref.watch(systemInfoNotifierProvider); + final controller = useTextEditingController(); final user = ref.settings .watchOne('_', alsoWatch: (_) => {_.user}) @@ -65,8 +65,22 @@ class SettingsScreen extends HookConsumerWidget { final text = '${controller.text.trim()} [from ${user.npub} on ${DateFormat('MMMM d, y').format(DateTime.now())}]'; final event = AppFeedback(content: text).sign(kI); - await ref.apps.nostrAdapter.relay.publish(event); - controller.clear(); + try { + await http.post(Uri.parse('https://relay.zapstore.dev/'), + body: jsonEncode(event.toMap()), + headers: {'Content-Type': 'application/json'}); + if (context.mounted) { + context.showInfo('Thank you', + description: 'Message sent successfully'); + } + controller.clear(); + } catch (e, stack) { + if (context.mounted) { + context.showError( + title: (e as dynamic).message ?? e.toString(), + description: stack.toString()); + } + } } }, builder: (context, child, callback, state) { @@ -123,18 +137,53 @@ class SettingsScreen extends HookConsumerWidget { child: Text('Delete local cache'), ), Gap(40), - Text( - 'System information', - style: context.theme.textTheme.headlineLarge! - .copyWith(fontWeight: FontWeight.bold), + Row( + children: [ + Text( + 'System info', + style: context.theme.textTheme.headlineLarge! + .copyWith(fontWeight: FontWeight.bold), + ), + Gap(20), + ElevatedButton( + onPressed: () { + ref.read(systemInfoNotifierProvider.notifier).fetch(); + }, + child: Text('Refresh'), + ), + ], ), Gap(20), GestureDetector( - onTap: () { - if (systemInfoState.hasValue) { - Clipboard.setData( - ClipboardData(text: systemInfoState.value!.toString())); - context.showInfo('Copied system information'); + onLongPress: () async { + final info = + await ref.read(systemInfoNotifierProvider.notifier).fetch(); + final dir = await getApplicationDocumentsDirectory(); + final errors = jsonDecode( + await File('${dir.path}/errors.json').readAsString()); + errors as Map; + errors['_'] = info.toString(); + errors['_user'] = + ref.settings.findOneLocalById('_')!.user.value?.npub; + try { + await http.post( + Uri.parse('https://cdn.zapstore.dev/upload'), + body: utf8.encode(jsonEncode(errors)), + headers: { + 'Content-Type': 'application/json', + 'X-Filename': 'errors.json', + }, + ); + if (context.mounted) { + context.showInfo('System info sent', + description: 'Thank you'); + } + } catch (e) { + if (context.mounted) { + context.showError( + title: 'Unable to send system info', + description: e.toString()); + } } }, child: Text(switch (systemInfoState) { @@ -142,20 +191,6 @@ class SettingsScreen extends HookConsumerWidget { _ => '', }), ), - HookBuilder( - builder: (context) { - final snapshot = useFuture(useMemoized(() async { - final dir = await getApplicationDocumentsDirectory(); - return jsonDecode( - await File('${dir.path}/errors.json').readAsString()); - })); - if (snapshot.hasData) { - return Text( - 'Entries in errors.json: ${(snapshot.data as Map).length}'); - } - return Container(); - }, - ), ], ), ); diff --git a/lib/utils/system_info.dart b/lib/utils/system_info.dart index 27f6108..39095e5 100644 --- a/lib/utils/system_info.dart +++ b/lib/utils/system_info.dart @@ -10,28 +10,16 @@ import 'package:zapstore/utils/extensions.dart'; final deviceInfoPlugin = DeviceInfoPlugin(); final packageManager = AndroidPackageManager(); -final systemInfoProvider = FutureProvider((ref) async { - final zsInfo = - await packageManager.getPackageInfo(packageName: kZapstoreAppIdentifier); - final dbInfo = { - 'apps': ref.apps.countLocal, - 'releases': ref.releases.countLocal, - 'metadata': ref.fileMetadata.countLocal, - }; - return SystemInfo( - androidInfo: await deviceInfoPlugin.androidInfo, - zsInfo: zsInfo!, - dbInfo: dbInfo, - ); -}); - class SystemInfo { final AndroidDeviceInfo androidInfo; final PackageInfo zsInfo; final Map dbInfo; - SystemInfo( - {required this.androidInfo, required this.zsInfo, required this.dbInfo}); + SystemInfo({ + required this.androidInfo, + required this.zsInfo, + required this.dbInfo, + }); @override String toString() { @@ -54,3 +42,34 @@ Low RAM device? ${androidInfo.isLowRamDevice} '''; } } + +class SystemInfoNotifier extends StateNotifier> { + final Ref ref; + + SystemInfoNotifier(this.ref) : super(AsyncLoading()) { + fetch(); + } + + Future fetch() async { + final zsInfo = await packageManager.getPackageInfo( + packageName: kZapstoreAppIdentifier); + final dbInfo = { + 'apps': ref.apps.countLocal, + 'releases': ref.releases.countLocal, + 'metadata': ref.fileMetadata.countLocal, + }; + + final info = SystemInfo( + androidInfo: await deviceInfoPlugin.androidInfo, + zsInfo: zsInfo!, + dbInfo: dbInfo, + ); + + state = AsyncData(info); + return info; + } +} + +final systemInfoNotifierProvider = + StateNotifierProvider>( + SystemInfoNotifier.new); diff --git a/lib/widgets/drawer_container.dart b/lib/widgets/drawer_container.dart index d3372eb..98a4645 100644 --- a/lib/widgets/drawer_container.dart +++ b/lib/widgets/drawer_container.dart @@ -20,7 +20,7 @@ class DrawerContainer extends StatelessWidget { Expanded(child: LoginContainer()), Consumer( builder: (context, ref, _) { - final state = ref.watch(systemInfoProvider); + final state = ref.watch(systemInfoNotifierProvider); return switch (state) { AsyncData(:final value) => Text( 'Version: ${value.zsInfo.versionName} (${value.zsInfo.versionCode}, $kDbVersion)', diff --git a/lib/widgets/error_container.dart b/lib/widgets/error_container.dart index 957f9ea..e54eb61 100644 --- a/lib/widgets/error_container.dart +++ b/lib/widgets/error_container.dart @@ -139,7 +139,8 @@ Widget materialErrorBuilder(BuildContext context, Widget? widget) { Future _sendErrorReport( WidgetRef ref, Object exception, StackTrace? stack) async { - final systemInfo = await ref.read(systemInfoProvider.future); + final systemInfo = + await ref.read(systemInfoNotifierProvider.notifier).fetch(); var map = { 'e': exception.toString(), @@ -147,10 +148,8 @@ Future _sendErrorReport( 'info': systemInfo.androidInfo.toString() }; - final client = http.Client(); final event = AppFeedback(content: jsonEncode(map)).sign(kI); - await client.post(Uri.parse('https://relay.zapstore.dev/'), + await http.post(Uri.parse('https://relay.zapstore.dev/'), body: jsonEncode(event.toMap()), headers: {'Content-Type': 'application/json'}); - client.close(); }