mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-08-06 15:21:21 +08:00
@ -370,7 +370,12 @@ widgets:
|
||||
actions:
|
||||
moveToFolder: Move To Folder
|
||||
NoteDeleteDialog:
|
||||
title: Do you want to delete this note?
|
||||
title:
|
||||
one: Do you want to delete this note?
|
||||
two: Do you want to delete these {} notes?
|
||||
few: Do you want to delete these {} notes?
|
||||
many: Do you want to delete these {} notes?
|
||||
other: Do you want to delete these {} notes?
|
||||
yes: Yes
|
||||
no: No
|
||||
NoteEditor:
|
||||
|
@ -18,8 +18,28 @@ class CommitMessageBuilder {
|
||||
String moveNote(String oldSpec, String newSpec) =>
|
||||
"Moved Note $oldSpec -> $newSpec";
|
||||
|
||||
String moveNotes(List<String> oldSpecs, List<String> newSpecs) {
|
||||
var msg = "Moved ${oldSpecs.length} Notes\n\n";
|
||||
|
||||
for (var i = 0; i < oldSpecs.length; i++) {
|
||||
var oldSpec = oldSpecs[i];
|
||||
var newSpec = newSpecs[i];
|
||||
|
||||
msg += '* $oldSpec -> $newSpec';
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
String removeNote(String spec) => "Removed Note $spec";
|
||||
String removeFolder(String spec) => "Removed Folder $spec";
|
||||
String removeNotes(Iterable<String> specs) {
|
||||
var list = specs.toList();
|
||||
var msg = "Removed ${list.length} Notes\n\n";
|
||||
for (var spec in list) {
|
||||
msg += '- $spec';
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
String updateNote(String spec) => "Updated Note $spec";
|
||||
}
|
||||
|
@ -163,25 +163,32 @@ class GitNoteRepository {
|
||||
return _addAllAndCommit(msg);
|
||||
}
|
||||
|
||||
Future<Result<void>> moveNote(
|
||||
String oldFullPath,
|
||||
String newFullPath,
|
||||
Future<Result<void>> moveNotes(
|
||||
List<String> oldPaths,
|
||||
List<String> newPaths,
|
||||
String newFolderPath,
|
||||
) async {
|
||||
var repoPath = gitRepoPath.endsWith('/') ? gitRepoPath : '$gitRepoPath/';
|
||||
var oldSpec = oldFullPath.substring(repoPath.length);
|
||||
var newSpec = newFullPath.substring(repoPath.length);
|
||||
var msg = messageBuilder.moveNote(oldSpec, newSpec);
|
||||
var oldSpecs = oldPaths.map((p) => p.substring(repoPath.length)).toList();
|
||||
var newSpecs = newPaths.map((p) => p.substring(repoPath.length)).toList();
|
||||
|
||||
var msg = oldPaths.length == 1
|
||||
? messageBuilder.moveNote(oldSpecs.first, newSpecs.first)
|
||||
: messageBuilder.moveNotes(oldSpecs, newSpecs);
|
||||
return _addAllAndCommit(msg);
|
||||
}
|
||||
|
||||
Future<Result<void>> removeNote(Note note) async {
|
||||
Future<Result<void>> removeNotes(List<Note> notes) async {
|
||||
return catchAll(() async {
|
||||
// We are not calling note.remove() as gitRm will also remove the file
|
||||
var spec = note.pathSpec();
|
||||
await _rm(spec).throwOnError();
|
||||
for (var note in notes) {
|
||||
var spec = note.pathSpec();
|
||||
await _rm(spec).throwOnError();
|
||||
}
|
||||
await _commit(
|
||||
message: messageBuilder.removeNote(spec),
|
||||
message: notes.length == 1
|
||||
? messageBuilder.removeNote(notes.first.pathSpec())
|
||||
: messageBuilder.removeNotes(notes.map((n) => n.pathSpec())),
|
||||
authorEmail: config.gitAuthorEmail,
|
||||
authorName: config.gitAuthor,
|
||||
).throwOnError();
|
||||
|
@ -34,7 +34,7 @@ Widget buildFolderView({
|
||||
required NoteSelectedFunction noteTapped,
|
||||
required NoteSelectedFunction noteLongPressed,
|
||||
required NoteBoolPropertyFunction isNoteSelected,
|
||||
required String searchTerm,
|
||||
String searchTerm = "",
|
||||
}) {
|
||||
switch (viewType) {
|
||||
case FolderViewType.Standard:
|
||||
|
@ -68,8 +68,8 @@ class _FolderViewState extends State<FolderView> {
|
||||
var _headerType = StandardViewHeader.TitleGenerated;
|
||||
bool _showSummary = true;
|
||||
|
||||
Note? selectedNote;
|
||||
bool get inSelectionMode => selectedNote != null;
|
||||
var selectedNotes = <Note>[];
|
||||
bool get inSelectionMode => selectedNotes.isNotEmpty;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -119,7 +119,7 @@ class _FolderViewState extends State<FolderView> {
|
||||
Widget _buildBody(BuildContext context) {
|
||||
var title = widget.notesFolder.publicName;
|
||||
if (inSelectionMode) {
|
||||
title = NumberFormat.compact().format(1);
|
||||
title = NumberFormat.compact().format(selectedNotes.length);
|
||||
}
|
||||
|
||||
var folderView = buildFolderView(
|
||||
@ -128,20 +128,9 @@ class _FolderViewState extends State<FolderView> {
|
||||
emptyText: tr(LocaleKeys.screens_folder_view_empty),
|
||||
header: _headerType,
|
||||
showSummary: _showSummary,
|
||||
noteTapped: (Note note) {
|
||||
if (!inSelectionMode) {
|
||||
openNoteEditor(context, note, widget.notesFolder);
|
||||
} else {
|
||||
_resetSelection();
|
||||
}
|
||||
},
|
||||
noteLongPressed: (Note note) {
|
||||
setState(() {
|
||||
selectedNote = note;
|
||||
});
|
||||
},
|
||||
isNoteSelected: (n) => n == selectedNote,
|
||||
searchTerm: "",
|
||||
noteTapped: _noteTapped,
|
||||
noteLongPressed: _noteLongPress,
|
||||
isNoteSelected: (n) => selectedNotes.contains(n),
|
||||
);
|
||||
|
||||
Widget pinnedFolderView = const SizedBox();
|
||||
@ -152,23 +141,11 @@ class _FolderViewState extends State<FolderView> {
|
||||
emptyText: null,
|
||||
header: _headerType,
|
||||
showSummary: _showSummary,
|
||||
noteTapped: (Note note) {
|
||||
if (!inSelectionMode) {
|
||||
openNoteEditor(context, note, widget.notesFolder);
|
||||
} else {
|
||||
_resetSelection();
|
||||
}
|
||||
},
|
||||
noteLongPressed: (Note note) {
|
||||
setState(() {
|
||||
selectedNote = note;
|
||||
});
|
||||
},
|
||||
isNoteSelected: (n) => n == selectedNote,
|
||||
searchTerm: "",
|
||||
noteTapped: _noteTapped,
|
||||
noteLongPressed: _noteLongPress,
|
||||
isNoteSelected: (n) => selectedNotes.contains(n),
|
||||
);
|
||||
}
|
||||
// assert(folderView is SliverWithKeepAliveWidget);
|
||||
|
||||
var settings = Provider.of<Settings>(context);
|
||||
final showButtomMenuBar = settings.bottomMenuBar;
|
||||
@ -231,6 +208,37 @@ class _FolderViewState extends State<FolderView> {
|
||||
);
|
||||
}
|
||||
|
||||
void _noteLongPress(Note note) {
|
||||
var i = selectedNotes.indexOf(note);
|
||||
if (i != -1) {
|
||||
setState(() {
|
||||
selectedNotes.removeAt(i);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
selectedNotes.add(note);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _noteTapped(Note note) {
|
||||
if (!inSelectionMode) {
|
||||
openNoteEditor(context, note, widget.notesFolder);
|
||||
return;
|
||||
}
|
||||
|
||||
var i = selectedNotes.indexOf(note);
|
||||
if (i != -1) {
|
||||
setState(() {
|
||||
selectedNotes.removeAt(i);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
selectedNotes.add(note);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var createButton = FloatingActionButton(
|
||||
@ -558,7 +566,7 @@ class _FolderViewState extends State<FolderView> {
|
||||
onSelected: (NoteSelectedExtraActions choice) {
|
||||
switch (choice) {
|
||||
case NoteSelectedExtraActions.MoveToFolder:
|
||||
_moveNoteToFolder();
|
||||
_moveSelectedNotesToFolder();
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -572,50 +580,48 @@ class _FolderViewState extends State<FolderView> {
|
||||
);
|
||||
|
||||
return <Widget>[
|
||||
IconButton(
|
||||
icon: const Icon(Icons.share),
|
||||
onPressed: () async {
|
||||
await shareNote(selectedNote!);
|
||||
_resetSelection();
|
||||
},
|
||||
),
|
||||
if (selectedNotes.length == 1)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.share),
|
||||
onPressed: () async {
|
||||
await shareNote(selectedNotes.first);
|
||||
_resetSelection();
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: _deleteNote,
|
||||
onPressed: _deleteSelectedNotes,
|
||||
),
|
||||
extraActions,
|
||||
];
|
||||
}
|
||||
|
||||
void _deleteNote() async {
|
||||
var note = selectedNote;
|
||||
|
||||
void _deleteSelectedNotes() async {
|
||||
var settings = Provider.of<Settings>(context, listen: false);
|
||||
var shouldDelete = true;
|
||||
if (settings.confirmDelete) {
|
||||
shouldDelete = await showDialog(
|
||||
context: context,
|
||||
builder: (context) => NoteDeleteDialog(),
|
||||
);
|
||||
shouldDelete = (await showDialog(
|
||||
context: context,
|
||||
builder: (context) => NoteDeleteDialog(num: selectedNotes.length),
|
||||
)) ==
|
||||
true;
|
||||
}
|
||||
if (shouldDelete == true) {
|
||||
var stateContainer = context.read<GitJournalRepo>();
|
||||
stateContainer.removeNote(note!);
|
||||
var repo = context.read<GitJournalRepo>();
|
||||
repo.removeNotes(selectedNotes);
|
||||
}
|
||||
|
||||
_resetSelection();
|
||||
}
|
||||
|
||||
void _moveNoteToFolder() async {
|
||||
var note = selectedNote!;
|
||||
|
||||
void _moveSelectedNotesToFolder() async {
|
||||
var destFolder = await showDialog<NotesFolderFS>(
|
||||
context: context,
|
||||
builder: (context) => FolderSelectionDialog(),
|
||||
);
|
||||
if (destFolder != null) {
|
||||
var repo = context.read<GitJournalRepo>();
|
||||
repo.moveNote(note, destFolder);
|
||||
repo.moveNotes(selectedNotes, destFolder);
|
||||
}
|
||||
|
||||
_resetSelection();
|
||||
@ -623,7 +629,7 @@ class _FolderViewState extends State<FolderView> {
|
||||
|
||||
void _resetSelection() {
|
||||
setState(() {
|
||||
selectedNote = null;
|
||||
selectedNotes = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -366,8 +366,15 @@ class GitJournalRepo with ChangeNotifier {
|
||||
});
|
||||
}
|
||||
|
||||
void moveNote(Note note, NotesFolderFS destFolder) async {
|
||||
if (destFolder.folderPath == note.parent.folderPath) {
|
||||
Future<void> moveNote(Note note, NotesFolderFS destFolder) =>
|
||||
moveNotes([note], destFolder);
|
||||
|
||||
Future<void> moveNotes(List<Note> notes, NotesFolderFS destFolder) async {
|
||||
notes = notes
|
||||
.where((n) => n.parent.folderPath != destFolder.folderPath)
|
||||
.toList();
|
||||
|
||||
if (notes.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -375,10 +382,17 @@ class GitJournalRepo with ChangeNotifier {
|
||||
return _opLock.synchronized(() async {
|
||||
Log.d("Got moveNote lock");
|
||||
|
||||
var oldNotePath = note.filePath;
|
||||
NotesFolderFS.moveNote(note, destFolder);
|
||||
var oldPaths = <String>[];
|
||||
var newPaths = <String>[];
|
||||
for (var note in notes) {
|
||||
oldPaths.add(note.filePath);
|
||||
NotesFolderFS.moveNote(note, destFolder);
|
||||
newPaths.add(note.filePath);
|
||||
}
|
||||
|
||||
_gitRepo.moveNote(oldNotePath, note.filePath).then((Result<void> _) {
|
||||
_gitRepo
|
||||
.moveNotes(oldPaths, newPaths, destFolder.folderPath)
|
||||
.then((Result<void> _) {
|
||||
_syncNotes();
|
||||
numChanges += 1;
|
||||
notifyListeners();
|
||||
@ -421,15 +435,19 @@ class GitJournalRepo with ChangeNotifier {
|
||||
});
|
||||
}
|
||||
|
||||
void removeNote(Note note) async {
|
||||
void removeNote(Note note) => removeNotes([note]);
|
||||
|
||||
void removeNotes(List<Note> notes) async {
|
||||
logEvent(Event.NoteDeleted);
|
||||
|
||||
return _opLock.synchronized(() async {
|
||||
Log.d("Got removeNote lock");
|
||||
|
||||
// FIXME: What if the Note hasn't yet been saved?
|
||||
note.parent.remove(note);
|
||||
_gitRepo.removeNote(note).then((Result<void> _) async {
|
||||
for (var note in notes) {
|
||||
note.parent.remove(note);
|
||||
}
|
||||
_gitRepo.removeNotes(notes).then((Result<void> _) async {
|
||||
numChanges += 1;
|
||||
notifyListeners();
|
||||
// FIXME: Is there a way of figuring this amount dynamically?
|
||||
|
@ -335,7 +335,7 @@ class NoteEditorState extends State<NoteEditor>
|
||||
if (settings.confirmDelete) {
|
||||
shouldDelete = await showDialog(
|
||||
context: context,
|
||||
builder: (context) => NoteDeleteDialog(),
|
||||
builder: (context) => const NoteDeleteDialog(num: 1),
|
||||
);
|
||||
}
|
||||
if (shouldDelete == true) {
|
||||
|
@ -11,18 +11,22 @@ import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:gitjournal/generated/locale_keys.g.dart';
|
||||
|
||||
class NoteDeleteDialog extends StatelessWidget {
|
||||
final int num;
|
||||
|
||||
const NoteDeleteDialog({Key? key, required this.num}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(tr(LocaleKeys.widgets_NoteDeleteDialog_title)),
|
||||
title: Text(LocaleKeys.widgets_NoteDeleteDialog_title.plural(num)),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: Text(tr(LocaleKeys.widgets_NoteDeleteDialog_no)),
|
||||
child: Text(LocaleKeys.widgets_NoteDeleteDialog_no.tr()),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: Text(tr(LocaleKeys.widgets_NoteDeleteDialog_yes)),
|
||||
child: Text(LocaleKeys.widgets_NoteDeleteDialog_yes.tr()),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
Reference in New Issue
Block a user