diff --git a/lib/core/notes_folder.dart b/lib/core/notes_folder.dart index a24d0585..b213cbc1 100644 --- a/lib/core/notes_folder.dart +++ b/lib/core/notes_folder.dart @@ -144,4 +144,12 @@ class NotesFolder with ChangeNotifier { notifyListeners(); } + + void rename(String newName) { + var dir = Directory(folderPath); + var parentDirName = dirname(folderPath); + var newFullPath = p.join(parentDirName, newName); + + dir.renameSync(newFullPath); + } } diff --git a/lib/screens/folder_listing.dart b/lib/screens/folder_listing.dart index 4e791048..7330ca34 100644 --- a/lib/screens/folder_listing.dart +++ b/lib/screens/folder_listing.dart @@ -8,7 +8,14 @@ import 'package:gitjournal/core/notes_folder.dart'; import 'journal_listing.dart'; -class FolderListingScreen extends StatelessWidget { +class FolderListingScreen extends StatefulWidget { + @override + _FolderListingScreenState createState() => _FolderListingScreenState(); +} + +class _FolderListingScreenState extends State { + NotesFolder selectedFolder; + @override Widget build(BuildContext context) { final container = StateContainer.of(context); @@ -16,7 +23,7 @@ class FolderListingScreen extends StatelessWidget { var treeView = FolderTreeView( rootFolder: appState.notesFolder, - onFolderSelected: (NotesFolder folder) { + onFolderEntered: (NotesFolder folder) { var route = MaterialPageRoute( builder: (context) => JournalListingScreen( notesFolder: folder, @@ -24,12 +31,49 @@ class FolderListingScreen extends StatelessWidget { ); Navigator.of(context).push(route); }, + onFolderSelected: (folder) { + setState(() { + selectedFolder = folder; + }); + }, + onFolderUnselected: () { + setState(() { + selectedFolder = null; + }); + }, ); + Widget action; + if (selectedFolder != null) { + action = PopupMenuButton( + itemBuilder: (context) { + return [ + PopupMenuItem( + child: const Text("Rename Folder"), + value: "Rename Folder", + ) + ]; + }, + onSelected: (String _) async { + var folderName = await showDialog( + context: context, + builder: (_) => RenameFolderDialog(selectedFolder), + ); + if (folderName is String) { + final container = StateContainer.of(context); + container.renameFolder(selectedFolder, folderName); + } + }, + ); + } + return Scaffold( appBar: AppBar( title: const Text('Folders'), leading: GJAppBarMenuButton(), + actions: [ + if (selectedFolder != null) action, + ], ), body: treeView, drawer: AppDrawer(), @@ -109,3 +153,67 @@ class _CreateFolderButtonState extends State { ); } } + +class RenameFolderDialog extends StatefulWidget { + final NotesFolder folder; + + RenameFolderDialog(this.folder); + + @override + _RenameFolderDialogState createState() => _RenameFolderDialogState(); +} + +class _RenameFolderDialogState extends State { + TextEditingController _textController; + + @override + void initState() { + super.initState(); + _textController = TextEditingController(text: widget.folder.name); + } + + @override + Widget build(BuildContext context) { + var form = Form( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + decoration: const InputDecoration(labelText: 'Folder Name'), + validator: (value) { + if (value.isEmpty) return 'Please enter a name'; + return ""; + }, + autofocus: true, + keyboardType: TextInputType.text, + controller: _textController, + ), + ], + ), + ); + + return AlertDialog( + title: const Text("Rename Folder"), + actions: [ + FlatButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text("Cancel"), + ), + FlatButton( + onPressed: () { + var newFolderName = _textController.text; + return Navigator.of(context).pop(newFolderName); + }, + child: const Text("Rename"), + ), + ], + content: form, + ); + } + + @override + void dispose() { + _textController.dispose(); + super.dispose(); + } +} diff --git a/lib/state_container.dart b/lib/state_container.dart index 50430c06..da567081 100644 --- a/lib/state_container.dart +++ b/lib/state_container.dart @@ -160,6 +160,21 @@ class StateContainerState extends State { }); } + void renameFolder(NotesFolder folder, String newFolderName) async { + var oldFolderPath = folder.folderPath; + + setState(() { + folder.rename(newFolderName); + + // Update the git repo + noteRepo + .renameFolder(oldFolderPath, folder.folderPath) + .then((NoteRepoResult _) { + _syncNotes(); + }); + }); + } + void addNote(Note note) { insertNote(0, note); } diff --git a/lib/storage/git_storage.dart b/lib/storage/git_storage.dart index 1f2bd0fc..c7a2b941 100644 --- a/lib/storage/git_storage.dart +++ b/lib/storage/git_storage.dart @@ -60,6 +60,19 @@ class GitNoteRepository { return NoteRepoResult(noteFilePath: folder.folderPath, error: false); } + Future renameFolder( + String oldFullPath, + String newFullPath, + ) async { + // FIXME: This is a hacky way of adding the changes, ideally we should be calling rm + add or something + await _gitRepo.add("."); + await _gitRepo.commit( + message: "Renamed folder", + ); + + return NoteRepoResult(noteFilePath: newFullPath, error: false); + } + Future removeNote(String noteFilePath) async { var gitDir = p.join(baseDirectory, dirName); var pathSpec = noteFilePath.replaceFirst(gitDir, "").substring(1); diff --git a/lib/widgets/folder_tree_view.dart b/lib/widgets/folder_tree_view.dart index fe00bd26..a35b9b87 100644 --- a/lib/widgets/folder_tree_view.dart +++ b/lib/widgets/folder_tree_view.dart @@ -4,30 +4,71 @@ import 'package:gitjournal/core/notes_folder.dart'; typedef void FolderSelectedCallback(NotesFolder folder); -class FolderTreeView extends StatelessWidget { +class FolderTreeView extends StatefulWidget { final NotesFolder rootFolder; + final FolderSelectedCallback onFolderSelected; + final Function onFolderUnselected; + final FolderSelectedCallback onFolderEntered; FolderTreeView({ @required this.rootFolder, @required this.onFolderSelected, + @required this.onFolderUnselected, + @required this.onFolderEntered, }); + @override + _FolderTreeViewState createState() => _FolderTreeViewState(); +} + +class _FolderTreeViewState extends State { + bool inSelectionMode = false; + NotesFolder selectedFolder; + @override Widget build(BuildContext context) { + var tile = FolderTile( + folder: widget.rootFolder, + onTap: (NotesFolder folder) { + if (!inSelectionMode) { + widget.onFolderEntered(folder); + } else { + setState(() { + inSelectionMode = false; + selectedFolder = null; + }); + widget.onFolderUnselected(); + } + }, + onLongPress: (folder) { + setState(() { + inSelectionMode = true; + selectedFolder = folder; + }); + widget.onFolderSelected(folder); + }, + selectedFolder: selectedFolder, + ); + return ListView( - children: [ - FolderTile(rootFolder, onFolderSelected), - ], + children: [tile], ); } } class FolderTile extends StatefulWidget { final NotesFolder folder; - final FolderSelectedCallback onFolderSelected; + final FolderSelectedCallback onTap; + final FolderSelectedCallback onLongPress; + final NotesFolder selectedFolder; - FolderTile(this.folder, this.onFolderSelected); + FolderTile({ + @required this.folder, + @required this.onTap, + @required this.onLongPress, + @required this.selectedFolder, + }); @override FolderTileState createState() => FolderTileState(); @@ -49,7 +90,8 @@ class FolderTileState extends State { children: [ GestureDetector( child: _buildFolderTile(), - onTap: () => widget.onFolderSelected(widget.folder), + onTap: () => widget.onTap(widget.folder), + onLongPress: () => widget.onLongPress(widget.folder), ), _getChild(), ], @@ -72,6 +114,9 @@ class FolderTileState extends State { } var subtitle = folder.numberOfNotes.toString() + " Notes"; + final theme = Theme.of(context); + + var selected = widget.selectedFolder == widget.folder; return Card( child: ListTile( leading: Container( @@ -87,7 +132,9 @@ class FolderTileState extends State { title: Text(folderName), subtitle: Text(subtitle), trailing: trailling, + selected: selected, ), + color: selected ? theme.selectedRowColor : theme.cardColor, ); } @@ -102,7 +149,12 @@ class FolderTileState extends State { var children = []; widget.folder.getFolders().forEach((folder) { - children.add(FolderTile(folder, widget.onFolderSelected)); + children.add(FolderTile( + folder: folder, + onTap: widget.onTap, + onLongPress: widget.onLongPress, + selectedFolder: widget.selectedFolder, + )); }); return Container(