Core: Null Safety++

This is a rather huge change. I hope I haven't broken anything.
This commit is contained in:
Vishesh Handa
2021-05-17 18:15:47 +02:00
parent 5e5de203da
commit 73f9db5fab
8 changed files with 100 additions and 114 deletions

View File

@ -1,5 +1,3 @@
// @dart=2.9
/*
Copyright 2020-2021 Vishesh Handa <me@vhanda.in>
@ -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<String> _tags = {};
Map<String, dynamic> _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<Link> _links;
Set<String> _inlineTags;
Set<NoteImage> _images;
String? _summary;
List<Link>? _links;
Set<String>? _inlineTags;
Set<NoteImage>? _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<String> 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<NoteImage> get images {
if (_loadState != NoteLoadState.Loaded) return {};
var p = ImageExtractor();
_images ??= p.extract(body);
_images = p.extract(body);
return _images;
return _images!;
}
Map<String, dynamic> 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<NoteLoadState> 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<void> 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<void> 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<List<Link>> fetchLinks() async {
Future<List<Link>?> 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<Link> links() {
List<Link>? links() {
return _links;
}
NoteFileFormat get fileFormat {
NoteFileFormat? get fileFormat {
return _fileFormat;
}
}

View File

@ -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<NoteModificationCallback>();
var _renameListeners = ObserverList<NoteRenameCallback>();
ObserverList<NoteModificationCallback>? _modListeners =
ObserverList<NoteModificationCallback>();
ObserverList<NoteRenameCallback>? _renameListeners =
ObserverList<NoteRenameCallback>();
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<VoidCallback> _listeners = ObserverList<VoidCallback>();
ObserverList<VoidCallback>? _listeners = ObserverList<VoidCallback>();
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<VoidCallback> localListeners =
List<VoidCallback>.from(_listeners);
List<VoidCallback>.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<NoteModificationCallback>.from(_modListeners);
final localListeners =
List<NoteModificationCallback>.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<NoteRenameCallback>.from(_renameListeners);
final localListeners = List<NoteRenameCallback>.from(_renameListeners!);
for (var listener in localListeners) {
try {
if (_renameListeners.contains(listener)) {
if (_renameListeners!.contains(listener)) {
listener(note, oldPath);
}
} catch (exception, stack) {

View File

@ -1,5 +1,3 @@
// @dart=2.9
/*
Copyright 2020-2021 Vishesh Handa <me@vhanda.in>
@ -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) {

View File

@ -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<void> 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) {

View File

@ -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;

View File

@ -1,5 +1,3 @@
// @dart=2.9
import 'dart:async';
import 'dart:io';
@ -30,8 +28,8 @@ Future<void> initSentry() async {
Future<SentryEvent> 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<void> reportError(Object error, StackTrace stackTrace) async {
Future<void> reportError(dynamic error, StackTrace stackTrace) async {
Log.e("Uncaught Exception", ex: error, stacktrace: stackTrace);
if (reportCrashes) {
@ -124,8 +122,8 @@ Future<void> logExceptionWarning(Object e, StackTrace stackTrace) async {
List<Breadcrumb> breadcrumbs = [];
void captureErrorBreadcrumb({
@required String name,
Map<String, String> parameters,
required String name,
required Map<String, String> parameters,
}) {
var b = Breadcrumb(
message: name,
@ -136,19 +134,20 @@ void captureErrorBreadcrumb({
}
Future<void> 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");
}

View File

@ -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,

View File

@ -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";