From 73f9db5fabd6ff85c13c207f24894474bfc61c1e Mon Sep 17 00:00:00 2001 From: Vishesh Handa Date: Mon, 17 May 2021 18:15:47 +0200 Subject: [PATCH] Core: Null Safety++ This is a rather huge change. I hope I haven't broken anything. --- lib/core/note.dart | 101 +++++++++++++++++----------------- lib/core/note_notifier.dart | 41 +++++++------- lib/core/note_serializer.dart | 8 +-- lib/core/notes_folder_fs.dart | 21 +++---- lib/core/sorting_mode.dart | 10 ++-- lib/error_reporting.dart | 23 ++++---- lib/folder_views/common.dart | 8 +-- lib/settings.dart | 2 +- 8 files changed, 100 insertions(+), 114 deletions(-) diff --git a/lib/core/note.dart b/lib/core/note.dart index 50f4e27b..98595c56 100644 --- a/lib/core/note.dart +++ b/lib/core/note.dart @@ -1,5 +1,3 @@ -// @dart=2.9 - /* Copyright 2020-2021 Vishesh Handa @@ -86,31 +84,31 @@ enum NoteFileFormat { class Note with NotesNotifier { NotesFolderFS parent; - String _filePath; + String? _filePath; String _title = ""; - DateTime _created; - DateTime _modified; + DateTime? _created; + DateTime? _modified; String _body = ""; NoteType _type = NoteType.Unknown; Set _tags = {}; Map _extraProps = {}; - NoteFileFormat _fileFormat; + NoteFileFormat? _fileFormat; MdYamlDoc _data = MdYamlDoc(); - NoteSerializer noteSerializer; + late NoteSerializer noteSerializer; - DateTime fileLastModified; + DateTime? fileLastModified; var _loadState = NoteLoadState.None; var _serializer = MarkdownYAMLCodec(); // Computed from body - String _summary; - List _links; - Set _inlineTags; - Set _images; + String? _summary; + List? _links; + Set? _inlineTags; + Set? _images; static final _mdYamlDocLoader = MdYamlDocLoader(); static final _linksLoader = LinksLoader(); @@ -150,58 +148,61 @@ class Note with NotesNotifier { String get filePath { if (_filePath == null) { + var fp = ""; try { - _filePath = p.join(parent.folderPath, _buildFileName()); + fp = p.join(parent.folderPath, _buildFileName()); } catch (e, stackTrace) { Log.e("_buildFileName: $e"); logExceptionWarning(e, stackTrace); - _filePath = p.join(parent.folderPath, const Uuid().v4()); + fp = p.join(parent.folderPath, const Uuid().v4()); } switch (_fileFormat) { case NoteFileFormat.OrgMode: - if (!_filePath.toLowerCase().endsWith('.org')) { - _filePath += '.org'; + if (!fp.toLowerCase().endsWith('.org')) { + fp += '.org'; } break; case NoteFileFormat.Txt: - if (!_filePath.toLowerCase().endsWith('.txt')) { - _filePath += '.txt'; + if (!fp.toLowerCase().endsWith('.txt')) { + fp += '.txt'; } break; case NoteFileFormat.Markdown: default: - if (!_filePath.toLowerCase().endsWith('.md')) { - _filePath += '.md'; + if (!fp.toLowerCase().endsWith('.md')) { + fp += '.md'; } break; } + + _filePath = fp; } - return _filePath; + return _filePath as String; } String get fileName { return p.basename(filePath); } - DateTime get created { + DateTime? get created { return _created; } - set created(DateTime dt) { + set created(DateTime? dt) { if (!canHaveMetadata) return; _created = dt; _notifyModified(); } - DateTime get modified { + DateTime? get modified { return _modified; } - set modified(DateTime dt) { + set modified(DateTime? dt) { if (!canHaveMetadata) return; _modified = dt; @@ -255,7 +256,6 @@ class Note with NotesNotifier { } set tags(Set tags) { - assert(tags != null); if (!canHaveMetadata) return; _tags = tags; @@ -270,16 +270,16 @@ class Note with NotesNotifier { var p = InlineTagsProcessor(tagPrefixes: tagPrefixes); _inlineTags = p.extractTags(body); } - return _inlineTags; + return _inlineTags!; } Set get images { if (_loadState != NoteLoadState.Loaded) return {}; var p = ImageExtractor(); - _images ??= p.extract(body); + _images = p.extract(body); - return _images; + return _images!; } Map get extraProps { @@ -317,7 +317,7 @@ class Note with NotesNotifier { return body.isEmpty; } - String get summary { + String? get summary { if (_loadState != NoteLoadState.Loaded) return ""; _summary ??= stripMarkdownFormatting(body); @@ -330,13 +330,13 @@ class Note with NotesNotifier { Future load() async { assert(_filePath != null); - assert(_filePath.isNotEmpty); + assert(_filePath!.isNotEmpty); if (_loadState == NoteLoadState.Loading) { return _loadState; } - final file = File(_filePath); + final file = File(_filePath!); if (_loadState == NoteLoadState.Loaded) { try { var fileLastModified = file.lastModifiedSync(); @@ -346,7 +346,7 @@ class Note with NotesNotifier { this.fileLastModified = fileLastModified; } catch (e, stackTrace) { if (e is FileSystemException && - e.osError.errorCode == 2 /* File Not Found */) { + e.osError!.errorCode == 2 /* File Not Found */) { _loadState = NoteLoadState.NotExists; _notifyModified(); return _loadState; @@ -360,14 +360,15 @@ class Note with NotesNotifier { Log.d("Note modified: $_filePath"); } - var fpLowerCase = _filePath.toLowerCase(); + var fpLowerCase = _filePath!.toLowerCase(); var isMarkdown = fpLowerCase.endsWith('.md'); var isTxt = fpLowerCase.endsWith('.txt'); var isOrg = fpLowerCase.endsWith('.org'); if (isMarkdown) { try { - data = await _mdYamlDocLoader.loadDoc(_filePath); + var dataMaybe = await _mdYamlDocLoader.loadDoc(_filePath!); + data = dataMaybe!; _fileFormat = NoteFileFormat.Markdown; } on MdYamlDocNotFoundException catch (_) { _loadState = NoteLoadState.NotExists; @@ -382,7 +383,7 @@ class Note with NotesNotifier { } } else if (isTxt) { try { - body = await File(_filePath).readAsString(); + body = await File(_filePath!).readAsString(); _fileFormat = NoteFileFormat.Txt; } catch (e, stackTrace) { logExceptionWarning(e, stackTrace); @@ -393,7 +394,7 @@ class Note with NotesNotifier { } } else if (isOrg) { try { - body = await File(_filePath).readAsString(); + body = await File(_filePath!).readAsString(); _fileFormat = NoteFileFormat.OrgMode; } catch (e, stackTrace) { logExceptionWarning(e, stackTrace); @@ -417,10 +418,6 @@ class Note with NotesNotifier { // FIXME: What about error handling? Future save() async { - assert(_data != null); - assert(_data.body != null); - assert(_data.props != null); - var file = File(filePath); var contents = _serializer.encode(data); // Make sure all docs end with a \n @@ -439,7 +436,7 @@ class Note with NotesNotifier { Future remove() async { assert(_filePath != null); - var file = File(_filePath); + var file = File(filePath); await file.delete(); } @@ -537,8 +534,12 @@ class Note with NotesNotifier { if (imageSpec == '.') { baseFolder = parent.folderPath; } else { - baseFolder = parent.rootFolder.getFolderWithSpec(imageSpec).folderPath; - baseFolder ??= parent.folderPath; + var folder = parent.rootFolder.getFolderWithSpec(imageSpec); + if (folder != null) { + baseFolder = folder.folderPath; + } else { + baseFolder = parent.folderPath; + } } var imageFileName = p.basename(file.path); @@ -567,9 +568,6 @@ class Note with NotesNotifier { } String pathSpec() { - if (parent == null) { - return fileName; - } return p.join(parent.pathSpec(), fileName); } @@ -590,7 +588,6 @@ class Note with NotesNotifier { } else { return toSimpleDateTime(date); } - break; case NoteFileNameFormat.Iso8601: return toIso8601(date); case NoteFileNameFormat.Iso8601WithTimeZone: @@ -606,20 +603,20 @@ class Note with NotesNotifier { return date.toString(); } - Future> fetchLinks() async { + Future?> fetchLinks() async { if (_links != null) { return _links; } - _links = await _linksLoader.parseLinks(body: _body, filePath: _filePath); + _links = await _linksLoader.parseLinks(body: _body, filePath: _filePath!); return _links; } - List links() { + List? links() { return _links; } - NoteFileFormat get fileFormat { + NoteFileFormat? get fileFormat { return _fileFormat; } } diff --git a/lib/core/note_notifier.dart b/lib/core/note_notifier.dart index 042eb676..9dc444f5 100644 --- a/lib/core/note_notifier.dart +++ b/lib/core/note_notifier.dart @@ -1,5 +1,3 @@ -// @dart=2.9 - import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -9,25 +7,27 @@ typedef NoteModificationCallback = void Function(Note note); typedef NoteRenameCallback = void Function(Note note, String oldPath); class NotesNotifier implements ChangeNotifier { - var _modListeners = ObserverList(); - var _renameListeners = ObserverList(); + ObserverList? _modListeners = + ObserverList(); + ObserverList? _renameListeners = + ObserverList(); void addModifiedListener(NoteModificationCallback listener) { - _modListeners.add(listener); + _modListeners?.add(listener); } void removeModifiedListener(NoteModificationCallback listener) { - assert(_modListeners.contains(listener)); - _modListeners.remove(listener); + assert(_modListeners!.contains(listener)); + _modListeners?.remove(listener); } void addRenameListener(NoteRenameCallback listener) { - _renameListeners.add(listener); + _renameListeners?.add(listener); } void removeRenameListener(NoteRenameCallback listener) { - assert(_renameListeners.contains(listener)); - _renameListeners.remove(listener); + assert(_renameListeners!.contains(listener)); + _renameListeners?.remove(listener); } @mustCallSuper @@ -42,7 +42,7 @@ class NotesNotifier implements ChangeNotifier { // // ChangeNotifier implementation - How to not duplicate this? // - ObserverList _listeners = ObserverList(); + ObserverList? _listeners = ObserverList(); bool _debugAssertNotDisposed() { assert(() { @@ -77,7 +77,7 @@ class NotesNotifier implements ChangeNotifier { @override bool get hasListeners { assert(_debugAssertNotDisposed()); - return _listeners.isNotEmpty; + return _listeners!.isNotEmpty; } /// Register a closure to be called when the object changes. @@ -86,7 +86,7 @@ class NotesNotifier implements ChangeNotifier { @override void addListener(VoidCallback listener) { assert(_debugAssertNotDisposed()); - _listeners.add(listener); + _listeners!.add(listener); } /// Remove a previously registered closure from the list of closures that are @@ -111,7 +111,7 @@ class NotesNotifier implements ChangeNotifier { @override void removeListener(VoidCallback listener) { assert(_debugAssertNotDisposed()); - _listeners.remove(listener); + _listeners!.remove(listener); } /// Call all the registered listeners. @@ -136,10 +136,10 @@ class NotesNotifier implements ChangeNotifier { assert(_debugAssertNotDisposed()); if (_listeners != null) { final List localListeners = - List.from(_listeners); + List.from(_listeners!); for (VoidCallback listener in localListeners) { try { - if (_listeners.contains(listener)) { + if (_listeners!.contains(listener)) { listener(); } } catch (exception, stack) { @@ -165,10 +165,11 @@ class NotesNotifier implements ChangeNotifier { void notifyModifiedListeners(Note note) { assert(_debugAssertNotDisposed()); if (_modListeners != null) { - final localListeners = List.from(_modListeners); + final localListeners = + List.from(_modListeners!); for (var listener in localListeners) { try { - if (_modListeners.contains(listener)) { + if (_modListeners!.contains(listener)) { listener(note); } } catch (exception, stack) { @@ -194,10 +195,10 @@ class NotesNotifier implements ChangeNotifier { void notifyRenameListeners(Note note, String oldPath) { assert(_debugAssertNotDisposed()); if (_renameListeners != null) { - final localListeners = List.from(_renameListeners); + final localListeners = List.from(_renameListeners!); for (var listener in localListeners) { try { - if (_renameListeners.contains(listener)) { + if (_renameListeners!.contains(listener)) { listener(note, oldPath); } } catch (exception, stack) { diff --git a/lib/core/note_serializer.dart b/lib/core/note_serializer.dart index 5e03676b..1d10f852 100644 --- a/lib/core/note_serializer.dart +++ b/lib/core/note_serializer.dart @@ -1,5 +1,3 @@ -// @dart=2.9 - /* Copyright 2020-2021 Vishesh Handa @@ -65,18 +63,18 @@ class NoteSerializer implements NoteSerializerInterface { data.body = emojiParser.unemojify(note.body); if (note.created != null) { - data.props[settings.createdKey] = toIso8601WithTimezone(note.created); + data.props[settings.createdKey] = toIso8601WithTimezone(note.created!); } else { data.props.remove(settings.createdKey); } if (note.modified != null) { - data.props[settings.modifiedKey] = toIso8601WithTimezone(note.modified); + data.props[settings.modifiedKey] = toIso8601WithTimezone(note.modified!); } else { data.props.remove(settings.modifiedKey); } - if (note.title != null) { + if (note.title.isNotEmpty) { var title = emojiParser.unemojify(note.title.trim()); if (settings.titleSettings == SettingsTitle.InH1) { if (title.isNotEmpty) { diff --git a/lib/core/notes_folder_fs.dart b/lib/core/notes_folder_fs.dart index 2b12d784..4a164b9a 100644 --- a/lib/core/notes_folder_fs.dart +++ b/lib/core/notes_folder_fs.dart @@ -1,10 +1,7 @@ -// @dart=2.9 - import 'dart:collection'; import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:path/path.dart'; import 'package:synchronized/synchronized.dart'; @@ -24,7 +21,7 @@ class IgnoredFile { String filePath; IgnoreReason reason; - IgnoredFile({@required this.filePath, @required this.reason}); + IgnoredFile({required this.filePath, required this.reason}); String get fileName { return p.basename(filePath); @@ -32,7 +29,7 @@ class IgnoredFile { } class NotesFolderFS with NotesFolderNotifier implements NotesFolder { - final NotesFolderFS _parent; + final NotesFolderFS? _parent; String _folderPath; var _lock = Lock(); @@ -54,9 +51,9 @@ class NotesFolderFS with NotesFolderNotifier implements NotesFolder { } @override - NotesFolder get parent => _parent; + NotesFolder? get parent => _parent; - NotesFolderFS get parentFS => _parent; + NotesFolderFS? get parentFS => _parent; void _entityChanged() { notifyListeners(); @@ -190,7 +187,7 @@ class NotesFolderFS with NotesFolderNotifier implements NotesFolder { futures.add(f); } - return Future.wait(futures); + await Future.wait(futures); } Future load() async { @@ -418,7 +415,7 @@ class NotesFolderFS with NotesFolderNotifier implements NotesFolder { if (parent == null) { return ""; } - return p.join(parent.pathSpec(), name); + return p.join(parent!.pathSpec(), name); } @override @@ -448,7 +445,7 @@ class NotesFolderFS with NotesFolderNotifier implements NotesFolder { return this; } - NotesFolderFS getFolderWithSpec(String spec) { + NotesFolderFS? getFolderWithSpec(String spec) { if (pathSpec() == spec) { return this; } @@ -465,12 +462,12 @@ class NotesFolderFS with NotesFolderNotifier implements NotesFolder { NotesFolderFS get rootFolder { var folder = this; while (folder.parent != null) { - folder = folder.parent; + folder = folder.parent as NotesFolderFS; } return folder; } - Note getNoteWithSpec(String spec) { + Note? getNoteWithSpec(String spec) { var parts = spec.split(p.separator); var folder = this; while (parts.length != 1) { diff --git a/lib/core/sorting_mode.dart b/lib/core/sorting_mode.dart index 72e3487b..73b564e5 100644 --- a/lib/core/sorting_mode.dart +++ b/lib/core/sorting_mode.dart @@ -1,5 +1,3 @@ -// @dart=2.9 - import 'package:easy_localization/easy_localization.dart'; import 'package:gitjournal/core/note.dart'; @@ -160,7 +158,7 @@ int _sortCreatedDesc(Note a, Note b) { if (bDt == null && aDt == null) { return a.fileName.compareTo(b.fileName); } - return bDt.compareTo(aDt); + return bDt!.compareTo(aDt!); } int _sortModifiedDesc(Note a, Note b) { @@ -175,12 +173,12 @@ int _sortModifiedDesc(Note a, Note b) { if (bDt == null && aDt == null) { return a.fileName.compareTo(b.fileName); } - return bDt.compareTo(aDt); + return bDt!.compareTo(aDt!); } int _sortTitleAsc(Note a, Note b) { - var aTitleExists = a.title != null && a.title.isNotEmpty; - var bTitleExists = b.title != null && b.title.isNotEmpty; + var aTitleExists = a.title.isNotEmpty; + var bTitleExists = b.title.isNotEmpty; if (!aTitleExists && bTitleExists) { return 1; diff --git a/lib/error_reporting.dart b/lib/error_reporting.dart index bb6d3871..1ecb151d 100644 --- a/lib/error_reporting.dart +++ b/lib/error_reporting.dart @@ -1,5 +1,3 @@ -// @dart=2.9 - import 'dart:async'; import 'dart:io'; @@ -30,8 +28,8 @@ Future initSentry() async { Future get _environmentEvent async { final packageInfo = await PackageInfo.fromPlatform(); final deviceInfoPlugin = DeviceInfoPlugin(); - SentryOperatingSystem os; - SentryDevice device; + SentryOperatingSystem? os; + SentryDevice? device; if (Platform.isAndroid) { final androidInfo = await deviceInfoPlugin.androidInfo; os = SentryOperatingSystem( @@ -77,19 +75,19 @@ void flutterOnErrorHandler(FlutterErrorDetails details) { if (reportCrashes == true) { // vHanda: This doesn't always call our zone error handler, why? // Zone.current.handleUncaughtError(details.exception, details.stack); - reportError(details.exception, details.stack); + reportError(details.exception, details.stack ?? StackTrace.current); } else { FlutterError.dumpErrorToConsole(details); } } bool get reportCrashes => _reportCrashes ??= _initReportCrashes(); -bool _reportCrashes; +bool? _reportCrashes; bool _initReportCrashes() { return !JournalApp.isInDebugMode && AppSettings.instance.collectCrashReports; } -Future reportError(Object error, StackTrace stackTrace) async { +Future reportError(dynamic error, StackTrace stackTrace) async { Log.e("Uncaught Exception", ex: error, stacktrace: stackTrace); if (reportCrashes) { @@ -124,8 +122,8 @@ Future logExceptionWarning(Object e, StackTrace stackTrace) async { List breadcrumbs = []; void captureErrorBreadcrumb({ - @required String name, - Map parameters, + required String name, + required Map parameters, }) { var b = Breadcrumb( message: name, @@ -136,19 +134,20 @@ void captureErrorBreadcrumb({ } Future captureSentryException( - Object exception, + dynamic exception, StackTrace stackTrace, { SentryLevel level = SentryLevel.error, }) async { try { await initSentry(); final event = (await _environmentEvent).copyWith( - exception: exception, + throwable: exception, breadcrumbs: breadcrumbs, level: level, ); - return Sentry.captureEvent(event, stackTrace: Trace.from(stackTrace).terse); + await Sentry.captureEvent(event, stackTrace: Trace.from(stackTrace).terse); + return; } catch (e) { print("Failed to report with Sentry: $e"); } diff --git a/lib/folder_views/common.dart b/lib/folder_views/common.dart index 81bd51a6..46112ee8 100644 --- a/lib/folder_views/common.dart +++ b/lib/folder_views/common.dart @@ -16,14 +16,10 @@ import 'package:gitjournal/screens/note_editor.dart'; import 'package:gitjournal/settings.dart'; import 'package:gitjournal/utils.dart'; import 'package:gitjournal/utils/logger.dart'; +import 'common_types.dart'; import 'standard_view.dart'; -enum FolderViewType { - Standard, - Journal, - Card, - Grid, -} +export 'common_types.dart'; Widget buildFolderView({ @required FolderViewType viewType, diff --git a/lib/settings.dart b/lib/settings.dart index 46675acb..efda65e6 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -29,7 +29,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:uuid/uuid.dart'; import 'package:gitjournal/core/sorting_mode.dart'; -import 'package:gitjournal/folder_views/common.dart'; +import 'package:gitjournal/folder_views/common_types.dart'; import 'package:gitjournal/screens/note_editor.dart'; const DEFAULT_ID = "0";