diff --git a/lib/core/note.dart b/lib/core/note.dart index a1166c47..f4eac6c4 100644 --- a/lib/core/note.dart +++ b/lib/core/note.dart @@ -437,6 +437,11 @@ class Link { other is Note && runtimeType == other.runtimeType && filePath == other.filePath; + + @override + String toString() { + return 'Link{term: $term, filePath: $filePath}'; + } } class LinkExtractor implements md.NodeVisitor { diff --git a/lib/core/notes_folder_fs.dart b/lib/core/notes_folder_fs.dart index 63f86fd3..f4f1a791 100644 --- a/lib/core/notes_folder_fs.dart +++ b/lib/core/notes_folder_fs.dart @@ -458,8 +458,33 @@ class NotesFolderFS with NotesFolderNotifier implements NotesFolder { Set getNoteTagsRecursively() { return _fetchTags(this, {}); } + + Future> matchNotes(NoteMatcherAsync pred) async { + var matchedNotes = []; + await _matchNotes(matchedNotes, pred); + return matchedNotes; + } + + Future> _matchNotes( + List matchedNotes, + NoteMatcherAsync pred, + ) async { + for (var note in _notes) { + var matches = await pred(note); + if (matches) { + matchedNotes.add(note); + } + } + + for (var folder in _folders) { + await folder._matchNotes(matchedNotes, pred); + } + return matchedNotes; + } } +typedef NoteMatcherAsync = Future Function(Note n); + Set _fetchTags(NotesFolder folder, Set tags) { for (var note in folder.notes) { tags.addAll(note.tags); diff --git a/lib/widgets/note_viewer.dart b/lib/widgets/note_viewer.dart index 46d2e0bf..e87c2860 100644 --- a/lib/widgets/note_viewer.dart +++ b/lib/widgets/note_viewer.dart @@ -47,6 +47,7 @@ class NoteViewer extends StatelessWidget { ), ); + final rootFolder = Provider.of(context); var view = EditorScrollView( child: Column( children: [ @@ -58,7 +59,6 @@ class NoteViewer extends StatelessWidget { styleSheet: markdownStyleSheet, onTapLink: (String link) { if (link.startsWith('./')) { - final rootFolder = Provider.of(context); var spec = link.substring(2); var note = rootFolder.getNoteWithSpec(spec); if (note != null) { @@ -75,6 +75,7 @@ class NoteViewer extends StatelessWidget { url, note.parent.folderPath + p.separator, null, null), ), ), + NoteBacklinkRenderer(note: note, rootFolder: rootFolder), // _buildFooter(context), ], crossAxisAlignment: CrossAxisAlignment.start, @@ -181,3 +182,86 @@ Widget _handleDataSchemeUri(Uri uri, final double width, final double height) { } return const SizedBox(); } + +class NoteBacklinkRenderer extends StatefulWidget { + final Note note; + final NotesFolderFS rootFolder; + + NoteBacklinkRenderer({ + @required this.note, + @required this.rootFolder, + }); + + @override + _NoteBacklinkRendererState createState() => _NoteBacklinkRendererState(); +} + +class _NoteBacklinkRendererState extends State { + List linkedNotes = []; + + @override + void initState() { + super.initState(); + + _initStateAsync(); + } + + Future _initStateAsync() async { + var predicate = (Note n) async { + var links = await n.fetchLinks(); + var matchedLink = links.firstWhere( + (l) => l.filePath == widget.note.filePath, + orElse: () => null, + ); + return matchedLink != null; + }; + + var l = await widget.rootFolder.matchNotes(predicate); + if (!mounted) return; + setState(() { + linkedNotes = l; + print("linkedNote $linkedNotes"); + }); + } + + @override + Widget build(BuildContext context) { + if (linkedNotes.isEmpty) { + return Container(); + } + + var textTheme = Theme.of(context).textTheme; + var c = Column( + children: [ + Text('BackLinks', style: textTheme.headline5), + const SizedBox(height: 8.0), + for (var n in linkedNotes) _buildNoteLink(n), + ], + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + ); + + return Padding( + padding: const EdgeInsets.fromLTRB(0.0, 16.0, 0.0, 16.0), + child: c, + ); + } + + Widget _buildNoteLink(Note note) { + var textTheme = Theme.of(context).textTheme; + var title = note.title; + if (title.isEmpty) { + title = note.fileName; + } + + return Padding( + padding: const EdgeInsets.fromLTRB(0.0, 8.0, 0, 8.0), + child: GestureDetector( + onTap: () { + openNoteEditor(context, note); + }, + child: Text('- $title', style: textTheme.bodyText1), + ), + ); + } +}