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