From 17f04c7b117b4256e0f2a99b123829a3657c533b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Chiotti?= <44336112+maelchiotti@users.noreply.github.com> Date: Fri, 5 Apr 2024 00:21:36 +0200 Subject: [PATCH] Fixes (#19) * feat: improve error handling and snackbar messages for export and import * fix: add note action crashing when the current route was not the notes list and when app was closed * fix: remove useless editor FocusNode * fix: action of quick action * fix: remove prints --- lib/app.dart | 9 +++++- lib/common/actions/add.dart | 7 ++--- lib/l10n/app_en.arb | 3 -- lib/l10n/app_fr.arb | 5 +--- lib/l10n/app_localizations.g.dart | 18 ------------ lib/l10n/app_localizations_en.g.dart | 13 -------- lib/l10n/app_localizations_fr.g.dart | 13 -------- lib/pages/editor/editor_page.dart | 2 -- lib/pages/notes/notes_page.dart | 3 -- lib/pages/settings/interactions.dart | 30 +++++++++---------- lib/utils/database_manager.dart | 20 ++++++++----- lib/utils/quick_actions_manager.dart | 23 +++++++++++++-- pubspec.lock | 44 ++++++++++++++++------------ pubspec.yaml | 1 + 14 files changed, 86 insertions(+), 105 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index 1a5f6760..cc9e1902 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:after_layout/after_layout.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -7,6 +8,7 @@ import 'package:localmaterialnotes/common/routing/router.dart'; import 'package:localmaterialnotes/l10n/app_localizations.g.dart'; import 'package:localmaterialnotes/utils/constants/constants.dart'; import 'package:localmaterialnotes/utils/locale_manager.dart'; +import 'package:localmaterialnotes/utils/quick_actions_manager.dart'; import 'package:localmaterialnotes/utils/share_manager.dart'; import 'package:localmaterialnotes/utils/theme_manager.dart'; @@ -15,7 +17,7 @@ class App extends ConsumerStatefulWidget { ConsumerState createState() => _AppState(); } -class _AppState extends ConsumerState { +class _AppState extends ConsumerState with AfterLayoutMixin { late StreamSubscription _stream; @override @@ -26,6 +28,11 @@ class _AppState extends ConsumerState { _stream = listenSharedData(ref); } + @override + void afterFirstLayout(BuildContext context) { + QuickActionsManager().init(navigatorKey.currentContext!, ref); + } + @override void dispose() { _stream.cancel(); diff --git a/lib/common/actions/add.dart b/lib/common/actions/add.dart index 04d155cf..d3dca1e0 100644 --- a/lib/common/actions/add.dart +++ b/lib/common/actions/add.dart @@ -8,7 +8,6 @@ import 'package:localmaterialnotes/models/note/note.dart'; import 'package:localmaterialnotes/providers/current_note/current_note_provider.dart'; Future addNote(BuildContext context, WidgetRef ref, {String? content}) async { - unselectAll(ref); exitSelectionMode(ref); final note = content == null ? Note.empty() : Note.content(content); @@ -17,11 +16,11 @@ Future addNote(BuildContext context, WidgetRef ref, {String? content}) asy if (!context.mounted) return; - final route = RouterRoute.editor.fullPath!; + final editorRoute = RouterRoute.editor.fullPath!; final editorParameters = EditorParameters.from({'readonly': false, 'autofocus': true}); // If the editor is already opened with another note, replace the route with the new editor RouterRoute.isEditor - ? context.pushReplacement(route, extra: editorParameters) - : context.push(route, extra: editorParameters); + ? context.pushReplacement(editorRoute, extra: editorParameters) + : context.push(editorRoute, extra: editorParameters); } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 20578656..a593a0f9 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -28,11 +28,9 @@ "settings_export_json_description": "Export notes to a JSON file (bin included) that can be imported back", "settings_export_markdown_description": "Export notes to a Markdown file (bin included)", "settings_export_success": "The notes were successfully exported.", - "settings_export_fail": "The export failed: {error}.", "settings_import": "Import", "settings_import_description": "Import notes from a JSON file", "settings_import_success": "The notes were successfully imported.", - "settings_import_fail": "The import failed: {error}.", "settings_about": "About", "settings_github": "GitHub", "settings_github_description": "Take a look at the source code", @@ -40,7 +38,6 @@ "settings_licence_description": "AGPL-3.0", "settings_issue": "Report a bug", "settings_issue_description": "Report a bug by creating an issue on GitHub", - "action_add_note": "Add a note", "hint_title": "Title", "hint_note": "Note", "tooltip_fab_add_note": "Add a note", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 2d279551..0b1a40c3 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -28,19 +28,16 @@ "settings_export_json_description": "Exporter les notes dans un fichier JSON (corbeille incluse) qui peut être réimporté", "settings_export_markdown_description": "Exporter les notes dans un fichier Markdown (corbeille incluse)", "settings_export_success": "Les notes ont bien été exportées.", - "settings_export_fail": "L''export a échoué : {error}.", "settings_import": "Importer", "settings_import_description": "Importer les notes depuis un fichier JSON", "settings_import_success": "Les notes ont bien été importées.", - "settings_import_fail": "L''import a échoué : {error}.", "settings_about": "À propos", "settings_github": "GitHub", "settings_github_description": "Jeter un coup d''œil au code source", "settings_licence": "License", + "settings_licence_description": "AGPL-3.0", "settings_issue": "Signaler un bug", "settings_issue_description": "Signaler un bug en créant une issue sur GitHub", - "action_add_note": "Ajouter une note", - "settings_licence_description": "AGPL-3.0", "hint_title": "Titre", "hint_note": "Note", "tooltip_fab_add_note": "Ajouter une note", diff --git a/lib/l10n/app_localizations.g.dart b/lib/l10n/app_localizations.g.dart index aa229b5d..e92bb6fc 100644 --- a/lib/l10n/app_localizations.g.dart +++ b/lib/l10n/app_localizations.g.dart @@ -264,12 +264,6 @@ abstract class AppLocalizations { /// **'The notes were successfully exported.'** String get settings_export_success; - /// No description provided for @settings_export_fail. - /// - /// In en, this message translates to: - /// **'The export failed: {error}.'** - String settings_export_fail(Object error); - /// No description provided for @settings_import. /// /// In en, this message translates to: @@ -288,12 +282,6 @@ abstract class AppLocalizations { /// **'The notes were successfully imported.'** String get settings_import_success; - /// No description provided for @settings_import_fail. - /// - /// In en, this message translates to: - /// **'The import failed: {error}.'** - String settings_import_fail(Object error); - /// No description provided for @settings_about. /// /// In en, this message translates to: @@ -336,12 +324,6 @@ abstract class AppLocalizations { /// **'Report a bug by creating an issue on GitHub'** String get settings_issue_description; - /// No description provided for @action_add_note. - /// - /// In en, this message translates to: - /// **'Add a note'** - String get action_add_note; - /// No description provided for @hint_title. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_en.g.dart b/lib/l10n/app_localizations_en.g.dart index d6253e01..349af062 100644 --- a/lib/l10n/app_localizations_en.g.dart +++ b/lib/l10n/app_localizations_en.g.dart @@ -95,11 +95,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get settings_export_success => 'The notes were successfully exported.'; - @override - String settings_export_fail(Object error) { - return 'The export failed: $error.'; - } - @override String get settings_import => 'Import'; @@ -109,11 +104,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get settings_import_success => 'The notes were successfully imported.'; - @override - String settings_import_fail(Object error) { - return 'The import failed: $error.'; - } - @override String get settings_about => 'About'; @@ -135,9 +125,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get settings_issue_description => 'Report a bug by creating an issue on GitHub'; - @override - String get action_add_note => 'Add a note'; - @override String get hint_title => 'Title'; diff --git a/lib/l10n/app_localizations_fr.g.dart b/lib/l10n/app_localizations_fr.g.dart index 1d2dcafb..16224711 100644 --- a/lib/l10n/app_localizations_fr.g.dart +++ b/lib/l10n/app_localizations_fr.g.dart @@ -97,11 +97,6 @@ class AppLocalizationsFr extends AppLocalizations { @override String get settings_export_success => 'Les notes ont bien été exportées.'; - @override - String settings_export_fail(Object error) { - return 'L\'export a échoué : $error.'; - } - @override String get settings_import => 'Importer'; @@ -111,11 +106,6 @@ class AppLocalizationsFr extends AppLocalizations { @override String get settings_import_success => 'Les notes ont bien été importées.'; - @override - String settings_import_fail(Object error) { - return 'L\'import a échoué : $error.'; - } - @override String get settings_about => 'À propos'; @@ -137,9 +127,6 @@ class AppLocalizationsFr extends AppLocalizations { @override String get settings_issue_description => 'Signaler un bug en créant une issue sur GitHub'; - @override - String get action_add_note => 'Ajouter une note'; - @override String get hint_title => 'Titre'; diff --git a/lib/pages/editor/editor_page.dart b/lib/pages/editor/editor_page.dart index 05a489b5..a122a969 100644 --- a/lib/pages/editor/editor_page.dart +++ b/lib/pages/editor/editor_page.dart @@ -29,7 +29,6 @@ class EditorPage extends ConsumerStatefulWidget { class _EditorState extends ConsumerState { final titleController = TextEditingController(); late FleatherController fleatherController; - final fleatherFocusNode = FocusNode(); void _synchronizeTitle(Note note, String? newTitle) { if (newTitle == null) return; @@ -81,7 +80,6 @@ class _EditorState extends ConsumerState { Expanded( child: FleatherField( controller: fleatherController, - focusNode: fleatherFocusNode, autofocus: widget._autofocus, readOnly: widget._readOnly, expands: true, diff --git a/lib/pages/notes/notes_page.dart b/lib/pages/notes/notes_page.dart index b1d17c0a..b8a1bff1 100644 --- a/lib/pages/notes/notes_page.dart +++ b/lib/pages/notes/notes_page.dart @@ -9,7 +9,6 @@ import 'package:localmaterialnotes/utils/constants/paddings.dart'; import 'package:localmaterialnotes/utils/constants/separators.dart'; import 'package:localmaterialnotes/utils/preferences/preference_key.dart'; import 'package:localmaterialnotes/utils/preferences/preferences_manager.dart'; -import 'package:localmaterialnotes/utils/quick_actions_manager.dart'; class NotesPage extends ConsumerStatefulWidget { const NotesPage(); @@ -21,8 +20,6 @@ class NotesPage extends ConsumerStatefulWidget { class _NotesPageState extends ConsumerState { @override Widget build(BuildContext context) { - QuickActionsManager().init(context, ref); - return ref.watch(notesProvider).when( data: (notes) { if (notes.isEmpty) return EmptyPlaceholder.notes(); diff --git a/lib/pages/settings/interactions.dart b/lib/pages/settings/interactions.dart index 06c30e38..a4f947da 100644 --- a/lib/pages/settings/interactions.dart +++ b/lib/pages/settings/interactions.dart @@ -126,38 +126,38 @@ class Interactions { Future backupAsJson(BuildContext context) async { try { - await DatabaseManager().exportAsJson(); + if (await DatabaseManager().exportAsJson()) { + SnackBarManager.info(localizations.settings_export_success).show(); + } } catch (exception, stackTrace) { log(exception.toString(), stackTrace: stackTrace); - SnackBarManager.info(localizations.settings_export_fail(exception.toString())).show(); - return; - } - SnackBarManager.info(localizations.settings_export_success).show(); + SnackBarManager.info(exception.toString()).show(); + } } Future backupAsMarkdown(BuildContext context) async { try { - await DatabaseManager().exportAsMarkdown(); + if (await DatabaseManager().exportAsMarkdown()) { + SnackBarManager.info(localizations.settings_export_success).show(); + } } catch (exception, stackTrace) { log(exception.toString(), stackTrace: stackTrace); - SnackBarManager.info(localizations.settings_export_fail(exception.toString())).show(); - return; - } - SnackBarManager.info(localizations.settings_export_success).show(); + SnackBarManager.info(exception.toString()).show(); + } } Future restore(BuildContext context) async { try { - await DatabaseManager().import(); + if (await DatabaseManager().import()) { + SnackBarManager.info(localizations.settings_import_success).show(); + } } catch (exception, stackTrace) { log(exception.toString(), stackTrace: stackTrace); - SnackBarManager.info(localizations.settings_import_fail(exception.toString())).show(); - return; - } - SnackBarManager.info(localizations.settings_import_success).show(); + SnackBarManager.info(exception.toString()).show(); + } } Future showAbout(BuildContext context) async { diff --git a/lib/utils/database_manager.dart b/lib/utils/database_manager.dart index be26b94f..27d207cb 100644 --- a/lib/utils/database_manager.dart +++ b/lib/utils/database_manager.dart @@ -88,14 +88,14 @@ class DatabaseManager { }); } - Future exportAsJson() async { + Future exportAsJson() async { final notes = await getAll(); final notesAsJson = jsonEncode(notes); - await _export('application/json', 'json', notesAsJson); + return await _export('application/json', 'json', notesAsJson); } - Future exportAsMarkdown() async { + Future exportAsMarkdown() async { final notes = await getAll(); final StringBuffer notesAsMarkdown = StringBuffer('# Material Notes\n\n'); for (final note in notes) { @@ -103,13 +103,13 @@ class DatabaseManager { .writeln('## ${note.title}${note.contentDisplay.isNotEmpty ? '\n\n' : ''}${note.contentDisplay}\n'); } - await _export('text/markdown', 'md', notesAsMarkdown.toString().trim()); + return await _export('text/markdown', 'md', notesAsMarkdown.toString().trim()); } - Future _export(String mimeType, String extension, String notesAsString) async { + Future _export(String mimeType, String extension, String notesAsString) async { final exportDirectory = await saf.openDocumentTree(); - if (exportDirectory == null) throw Exception(localizations.error_permission); + if (exportDirectory == null) return false; final timestamp = DateTime.timestamp(); @@ -119,15 +119,17 @@ class DatabaseManager { displayName: 'materialnotes_export_${timestamp.filename}.$extension', bytes: Uint8List.fromList(utf8.encode(notesAsString)), ); + + return true; } - Future import() async { + Future import() async { final importFiles = await saf.openDocument( grantWritePermission: false, mimeType: 'application/json', ); - if (importFiles == null || importFiles.isEmpty) throw Exception(localizations.error_permission); + if (importFiles == null || importFiles.isEmpty) return false; final importedData = await saf.getDocumentContent(importFiles.first); @@ -138,6 +140,8 @@ class DatabaseManager { final notes = notesJson.map((e) => Note.fromJson(e as Map)).toList(); await addAll(notes); + + return true; } /// Hardcode the welcome note translations here because no context is available when it's used diff --git a/lib/utils/quick_actions_manager.dart b/lib/utils/quick_actions_manager.dart index 1bbe9135..530f1840 100644 --- a/lib/utils/quick_actions_manager.dart +++ b/lib/utils/quick_actions_manager.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:localmaterialnotes/common/actions/add.dart'; -import 'package:localmaterialnotes/l10n/app_localizations.g.dart'; +import 'package:localmaterialnotes/utils/locale_manager.dart'; import 'package:quick_actions/quick_actions.dart'; class QuickActionsManager { @@ -19,9 +19,26 @@ class QuickActionsManager { quickActions.setShortcutItems([ ShortcutItem( type: 'add_note', - localizedTitle: AppLocalizations.of(context)!.action_add_note, - icon: 'launcher_icon', + localizedTitle: _actionAddNoteTitle, + icon: 'ic_launcher', ), ]); } + + /// Hardcode the action title translations here because no context is available when it's used + String get _actionAddNoteTitle { + final locale = LocaleManager().locale; + + final String title; + + if (locale == const Locale('en')) { + title = 'Add a note'; + } else if (locale == const Locale('fr')) { + title = 'Ajouter une note'; + } else { + throw Exception('Missing title of add note action for locale: $locale'); + } + + return title; + } } diff --git a/pubspec.lock b/pubspec.lock index 44b7907f..c548049b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "61.0.0" + after_layout: + dependency: "direct main" + description: + name: after_layout + sha256: "95a1cb2ca1464f44f14769329fbf15987d20ab6c88f8fc5d359bd362be625f29" + url: "https://pub.dev" + source: hosted + version: "1.2.0" analyzer: dependency: transitive description: @@ -125,10 +133,10 @@ packages: dependency: transitive description: name: built_value - sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6 + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.9.0" + version: "8.9.2" characters: dependency: transitive description: @@ -317,10 +325,10 @@ packages: dependency: transitive description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" file: dependency: transitive description: @@ -418,10 +426,10 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" glob: dependency: transitive description: @@ -450,10 +458,10 @@ packages: dependency: transitive description: name: hotreloader - sha256: "94ee21a60ea2836500799f3af035dc3212b1562027f1e0031c14e087f0231449" + sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.2.0" html: dependency: transitive description: @@ -1143,18 +1151,18 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f" + sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.3.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" + sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" url: "https://pub.dev" source: hosted - version: "6.2.4" + version: "6.2.5" url_launcher_linux: dependency: transitive description: @@ -1175,10 +1183,10 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" url_launcher_web: dependency: transitive description: @@ -1239,18 +1247,18 @@ packages: dependency: transitive description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + sha256: "1d8e795e2a8b3730c41b8a98a2dff2e0fb57ae6f0764a1c46ec5915387d257b2" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.4" win32: dependency: transitive description: name: win32 - sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" + sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a" url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "5.4.0" win32_registry: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b36fe591..ac7f92c2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,6 +10,7 @@ environment: sdk: ">=3.3.1 <4.0.0" dependencies: + after_layout: ^1.2.0 collection: ^1.18.0 cupertino_icons: ^1.0.6 device_info_plus: ^10.0.1