Add Tags sidebar

Fixes #114

This was there because I couldn't figure out how to configure Billing in
iOS, now that it is done, I can have the pro mode in iOS as well.
This commit is contained in:
Vishesh Handa
2020-05-14 16:04:33 +02:00
parent 7ef9cfe60f
commit 5c04bf204e
8 changed files with 126 additions and 29 deletions

View File

@ -54,6 +54,8 @@ screens:
delete: Delete Folder delete: Delete Folder
decoration: Folder Name decoration: Folder Name
empty: Please enter a name empty: Please enter a name
tags:
title: Tags
widgets: widgets:
rename: rename:
yes: Rename yes: Rename

View File

@ -6,6 +6,7 @@ import 'package:firebase_analytics/observer.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gitjournal/analytics.dart'; import 'package:gitjournal/analytics.dart';
import 'package:gitjournal/screens/folder_listing.dart'; import 'package:gitjournal/screens/folder_listing.dart';
import 'package:gitjournal/screens/tag_listing.dart';
import 'package:gitjournal/screens/note_editor.dart'; import 'package:gitjournal/screens/note_editor.dart';
import 'package:gitjournal/screens/purchase_screen.dart'; import 'package:gitjournal/screens/purchase_screen.dart';
import 'package:gitjournal/screens/purchase_thankyou_screen.dart'; import 'package:gitjournal/screens/purchase_thankyou_screen.dart';
@ -293,7 +294,7 @@ class _JournalAppState extends State<JournalApp> {
//debugShowMaterialGrid: true, //debugShowMaterialGrid: true,
onGenerateRoute: (settings) { onGenerateRoute: (settings) {
var route = settings.name; var route = settings.name;
if (route == '/folders') { if (route == '/folders' || route == '/tags') {
return PageRouteBuilder( return PageRouteBuilder(
settings: settings, settings: settings,
pageBuilder: (_, __, ___) => _screenForRoute(route, stateContainer), pageBuilder: (_, __, ___) => _screenForRoute(route, stateContainer),
@ -320,6 +321,8 @@ class _JournalAppState extends State<JournalApp> {
return HomeScreen(); return HomeScreen();
case '/folders': case '/folders':
return FolderListingScreen(); return FolderListingScreen();
case '/tags':
return TagListingScreen();
case '/settings': case '/settings':
return SettingsScreen(); return SettingsScreen();
case '/setupRemoteGit': case '/setupRemoteGit':

View File

@ -2,13 +2,17 @@ import 'package:gitjournal/core/note.dart';
import 'package:gitjournal/core/notes_folder.dart'; import 'package:gitjournal/core/notes_folder.dart';
import 'package:gitjournal/core/notes_folder_notifier.dart'; import 'package:gitjournal/core/notes_folder_notifier.dart';
typedef NotesFilter = bool Function(Note note);
class FlattenedNotesFolder with NotesFolderNotifier implements NotesFolder { class FlattenedNotesFolder with NotesFolderNotifier implements NotesFolder {
final NotesFolder _parentFolder; final NotesFolder _parentFolder;
final NotesFilter filter;
final String title;
var _notes = <Note>[]; var _notes = <Note>[];
var _folders = <NotesFolder>[]; var _folders = <NotesFolder>[];
FlattenedNotesFolder(this._parentFolder) { FlattenedNotesFolder(this._parentFolder, {this.filter, this.title}) {
_addFolder(_parentFolder); _addFolder(_parentFolder);
} }
@ -57,11 +61,17 @@ class FlattenedNotesFolder with NotesFolderNotifier implements NotesFolder {
} }
void _noteAdded(int _, Note note) { void _noteAdded(int _, Note note) {
if (filter != null && !filter(note)) {
return;
}
_notes.add(note); _notes.add(note);
notifyNoteAdded(-1, note); notifyNoteAdded(-1, note);
} }
void _noteRemoved(int _, Note note) { void _noteRemoved(int _, Note note) {
if (filter != null && !filter(note)) {
return;
}
var i = _notes.indexWhere((n) => n.filePath == note.filePath); var i = _notes.indexWhere((n) => n.filePath == note.filePath);
assert(i != -1); assert(i != -1);
@ -70,7 +80,22 @@ class FlattenedNotesFolder with NotesFolderNotifier implements NotesFolder {
} }
void _noteModified(int i, Note note) { void _noteModified(int i, Note note) {
notifyNoteModified(-1, note); if (filter == null) {
notifyNoteModified(-1, note);
return;
}
if (_notes.contains(note)) {
if (filter(note)) {
notifyNoteModified(-1, note);
} else {
_noteRemoved(-1, note);
}
} else {
if (filter(note)) {
_noteAdded(-1, note);
}
}
} }
@override @override
@ -97,7 +122,7 @@ class FlattenedNotesFolder with NotesFolderNotifier implements NotesFolder {
} }
@override @override
String get name => "All Notes"; String get name => title ?? "All Notes";
@override @override
NotesFolderConfig get config { NotesFolderConfig get config {

View File

@ -1,4 +1,3 @@
class Features { class Features {
static bool perFolderConfig = false; static bool perFolderConfig = false;
static bool purchaseProModeAvailable = true;
} }

View File

@ -5,7 +5,6 @@ import 'package:collection/collection.dart';
import 'package:gitjournal/core/note.dart'; import 'package:gitjournal/core/note.dart';
import 'package:gitjournal/core/md_yaml_doc.dart'; import 'package:gitjournal/core/md_yaml_doc.dart';
import 'package:gitjournal/core/notes_folder.dart';
import 'package:gitjournal/core/notes_folder_fs.dart'; import 'package:gitjournal/core/notes_folder_fs.dart';
import 'package:gitjournal/editors/journal_editor.dart'; import 'package:gitjournal/editors/journal_editor.dart';
import 'package:gitjournal/editors/markdown_editor.dart'; import 'package:gitjournal/editors/markdown_editor.dart';

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gitjournal/features.dart';
import 'package:gitjournal/screens/settings_screen.dart'; import 'package:gitjournal/screens/settings_screen.dart';
import 'package:gitjournal/settings.dart'; import 'package:gitjournal/settings.dart';
import 'package:gitjournal/screens/settings_widgets.dart'; import 'package:gitjournal/screens/settings_widgets.dart';
@ -48,25 +47,24 @@ class SettingsEditorsScreenState extends State<SettingsEditorsScreen> {
setState(() {}); setState(() {});
}, },
), ),
if (Features.purchaseProModeAvailable) SettingsHeader("Journal Editor"), SettingsHeader("Journal Editor"),
if (Features.purchaseProModeAvailable) ProSettingOverlay(
ProSettingOverlay( child: ListTile(
child: ListTile( title: const Text("Default Folder"),
title: const Text("Default Folder"), subtitle: Text(defaultNewFolder),
subtitle: Text(defaultNewFolder), onTap: () async {
onTap: () async { var destFolder = await showDialog<NotesFolderFS>(
var destFolder = await showDialog<NotesFolderFS>( context: context,
context: context, builder: (context) => FolderSelectionDialog(),
builder: (context) => FolderSelectionDialog(), );
);
Settings.instance.journalEditordefaultNewNoteFolderSpec = Settings.instance.journalEditordefaultNewNoteFolderSpec =
destFolder != null ? destFolder.pathSpec() : ""; destFolder != null ? destFolder.pathSpec() : "";
Settings.instance.save(); Settings.instance.save();
setState(() {}); setState(() {});
}, },
),
), ),
),
]); ]);
return Scaffold( return Scaffold(

View File

@ -0,0 +1,59 @@
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:gitjournal/core/flattened_notes_folder.dart';
import 'package:gitjournal/core/note.dart';
import 'package:gitjournal/core/notes_folder_fs.dart';
import 'package:gitjournal/screens/folder_view.dart';
import 'package:gitjournal/widgets/app_bar_menu_button.dart';
import 'package:gitjournal/widgets/app_drawer.dart';
import 'package:provider/provider.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class TagListingScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
var rootFolder = Provider.of<NotesFolderFS>(context);
var allTags = rootFolder.getNoteTagsRecursively();
var allTagsSorted = SplayTreeSet<String>.from(allTags);
var listView = ListView(
children: <Widget>[
for (var tag in allTagsSorted) _buildTagTile(context, tag),
],
);
return Scaffold(
appBar: AppBar(
title: Text(tr('screens.tags.title')),
leading: GJAppBarMenuButton(),
),
body: Scrollbar(child: listView),
drawer: AppDrawer(),
);
}
Widget _buildTagTile(BuildContext context, String tag) {
var theme = Theme.of(context);
var titleColor = theme.textTheme.headline1.color;
return ListTile(
leading: FaIcon(FontAwesomeIcons.tag, color: titleColor),
title: Text(tag),
onTap: () {
var route = MaterialPageRoute(builder: (context) {
var rootFolder = Provider.of<NotesFolderFS>(context);
var folder = FlattenedNotesFolder(
rootFolder,
filter: (Note n) => n.tags.contains(tag),
title: tag,
);
return FolderView(notesFolder: folder);
});
Navigator.of(context).push(route);
},
);
}
}

View File

@ -2,7 +2,7 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_email_sender/flutter_email_sender.dart'; import 'package:flutter_email_sender/flutter_email_sender.dart';
import 'package:gitjournal/features.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:gitjournal/settings.dart'; import 'package:gitjournal/settings.dart';
import 'package:gitjournal/utils/logger.dart'; import 'package:gitjournal/utils/logger.dart';
import 'package:launch_review/launch_review.dart'; import 'package:launch_review/launch_review.dart';
@ -65,7 +65,7 @@ class AppDrawer extends StatelessWidget {
), ),
), ),
if (setupGitButton != null) ...[setupGitButton, divider], if (setupGitButton != null) ...[setupGitButton, divider],
if (Features.purchaseProModeAvailable && !Settings.instance.proMode) if (!Settings.instance.proMode)
_buildDrawerTile( _buildDrawerTile(
context, context,
icon: Icons.power, icon: Icons.power,
@ -79,8 +79,7 @@ class AppDrawer extends StatelessWidget {
); );
}, },
), ),
if (Features.purchaseProModeAvailable && !Settings.instance.proMode) if (!Settings.instance.proMode) divider,
divider,
_buildDrawerTile( _buildDrawerTile(
context, context,
icon: Icons.note, icon: Icons.note,
@ -95,6 +94,14 @@ class AppDrawer extends StatelessWidget {
onTap: () => _navTopLevel(context, '/folders'), onTap: () => _navTopLevel(context, '/folders'),
selected: currentRoute == "/folders", selected: currentRoute == "/folders",
), ),
_buildDrawerTile(
context,
icon: FontAwesomeIcons.tag,
isFontAwesome: true,
title: "Tags",
onTap: () => _navTopLevel(context, '/tags'),
selected: currentRoute == "/tags",
),
divider, divider,
_buildDrawerTile( _buildDrawerTile(
context, context,
@ -205,6 +212,7 @@ class AppDrawer extends StatelessWidget {
@required IconData icon, @required IconData icon,
@required String title, @required String title,
@required Function onTap, @required Function onTap,
bool isFontAwesome = false,
bool selected = false, bool selected = false,
}) { }) {
var theme = Theme.of(context); var theme = Theme.of(context);
@ -213,8 +221,12 @@ class AppDrawer extends StatelessWidget {
color: selected ? theme.accentColor : listTileTheme.textColor, color: selected ? theme.accentColor : listTileTheme.textColor,
); );
var iconW = !isFontAwesome
? Icon(icon, color: textStyle.color)
: FaIcon(icon, color: textStyle.color);
var tile = ListTile( var tile = ListTile(
leading: Icon(icon, color: textStyle.color), leading: iconW,
title: Text(title, style: textStyle), title: Text(title, style: textStyle),
onTap: onTap, onTap: onTap,
selected: selected, selected: selected,