Implementing controlling of the sorting order

The dialog looks quite ugly right now, but at least everything works. I
can work on making it prettier after this.
This commit is contained in:
Vishesh Handa
2020-08-14 18:20:11 +02:00
parent 6280ccbdc3
commit 46269dd94a
13 changed files with 307 additions and 148 deletions

View File

@ -1,4 +1,6 @@
settings:
ok: Ok
cancel: Cancel
title: Settings
author:
label: Full Name
@ -71,11 +73,17 @@ settings:
markdownEditor: Markdown Editor
journalEditor: Journal Editor
defaultFolder: Default Folder
sortingMode:
sortingField:
modified: Last Modified
created: Created
filename: File Name
title: Title
sortingOrder:
asc: Ascending
desc: Descending
sortingMode:
field: Field
order: Order
remoteSync:
auto: Automatic
manual: Manual

View File

@ -70,7 +70,7 @@ class NotesFolderConfig extends Equatable {
return NotesFolderConfig(
defaultEditor: settings.defaultEditor.toEditorType(),
defaultView: settings.defaultView.toFolderViewType(),
sortingMode: settings.sortingMode,
sortingMode: SortingMode(settings.sortingField, settings.sortingOrder),
showNoteSummary: settings.showNoteSummary,
viewHeader: viewHeader,
fileNameFormat: settings.noteFileNameFormat,
@ -82,7 +82,8 @@ class NotesFolderConfig extends Equatable {
Future<void> saveToSettings() async {
var settings = Settings.instance;
settings.sortingMode = sortingMode;
settings.sortingField = sortingMode.field;
settings.sortingOrder = sortingMode.order;
settings.showNoteSummary = showNoteSummary;
settings.defaultEditor = SettingsEditorType.fromEditorType(defaultEditor);
settings.defaultView =
@ -145,8 +146,12 @@ class NotesFolderConfig extends Equatable {
Log.d('NotesFolderConfig::decode("$contents") -> ${err.toString()}');
}
var sortingMode =
SortingMode.fromInternalString(map["sortingMode"]?.toString());
var sortingField =
SortingField.fromInternalString(map["sortingField"]?.toString());
var sortingOrder =
SortingOrder.fromInternalString(map["sortingOrder"]?.toString());
var sortingMode = SortingMode(sortingField, sortingOrder);
var defaultEditor =
SettingsEditorType.fromInternalString(map["defaultEditor"]?.toString());
var defaultView = SettingsFolderViewType.fromInternalString(
@ -198,7 +203,8 @@ class NotesFolderConfig extends Equatable {
}
var map = <String, dynamic>{
"sortingMode": sortingMode.toInternalString(),
"sortingField": sortingMode.field.toInternalString(),
"sortingOrder": sortingMode.order.toInternalString(),
"defaultEditor":
SettingsEditorType.fromEditorType(defaultEditor).toInternalString(),
"defaultView": SettingsFolderViewType.fromFolderViewType(defaultView)

View File

@ -141,7 +141,8 @@ class SortedNotesFolder with NotesFolderNotifier implements NotesFolder {
bool get isEmpty => folder.isEmpty;
void changeSortingMode(SortingMode sm) {
Log.d("Setting sorting to me ${sm.toInternalString()}");
Log.d(
"Setting sorting to me ${sm.field.toInternalString()} ${sm.order.toInternalString()}");
_sortingMode = sm;
_sortFunc = _sortingMode.sortingFunction();

View File

@ -4,28 +4,14 @@ import 'package:gitjournal/core/note.dart';
typedef NoteSortingFunction = int Function(Note a, Note b);
class SortingMode {
static const Modified = SortingMode(
"settings.sortingMode.modified",
"Modified",
);
static const Created = SortingMode(
"settings.sortingMode.created",
"Created",
);
static const FileName = SortingMode(
"settings.sortingMode.filename",
"FileName",
);
static const Title = SortingMode(
"settings.sortingMode.title",
"Title",
);
static const Default = Modified;
class SortingOrder {
static const Ascending = SortingOrder("settings.sortingOrder.asc", "asc");
static const Descending = SortingOrder("settings.sortingOrder.desc", "desc");
static const Default = Descending;
final String _str;
final String _publicString;
const SortingMode(this._publicString, this._str);
const SortingOrder(this._publicString, this._str);
String toInternalString() {
return _str;
@ -35,14 +21,12 @@ class SortingMode {
return tr(_publicString);
}
static const options = <SortingMode>[
Modified,
Created,
FileName,
Title,
static const options = <SortingOrder>[
Ascending,
Descending,
];
static SortingMode fromInternalString(String str) {
static SortingOrder fromInternalString(String str) {
for (var opt in options) {
if (opt.toInternalString() == str) {
return opt;
@ -51,7 +35,7 @@ class SortingMode {
return Default;
}
static SortingMode fromPublicString(String str) {
static SortingOrder fromPublicString(String str) {
for (var opt in options) {
if (opt.toPublicString() == str) {
return opt;
@ -62,67 +46,166 @@ class SortingMode {
@override
String toString() {
assert(false, "SortingMode toString should never be called");
assert(false, "SortingOrder toString should never be called");
return "";
}
}
// vHanda FIXME: The modified and created should come from Git if not present in the document
NoteSortingFunction sortingFunction() {
switch (_str) {
case "Created":
return (Note a, Note b) {
var aDt = a.created;
var bDt = b.created;
if (aDt == null && bDt != null) {
return 1;
}
if (aDt != null && bDt == null) {
return -1;
}
if (bDt == null && aDt == null) {
return a.fileName.compareTo(b.fileName);
}
return bDt.compareTo(aDt);
};
class SortingField {
static const Modified = SortingField(
"settings.sortingField.modified",
"Modified",
);
static const Created = SortingField(
"settings.sortingField.created",
"Created",
);
static const FileName = SortingField(
"settings.sortingField.filename",
"FileName",
);
static const Title = SortingField(
"settings.sortingField.title",
"Title",
);
case "Title":
return (Note a, Note b) {
var aTitleExists = a.title != null && a.title.isNotEmpty;
var bTitleExists = b.title != null && b.title.isNotEmpty;
static const Default = Modified;
if (!aTitleExists && bTitleExists) {
return 1;
}
if (aTitleExists && !bTitleExists) {
return -1;
}
if (!aTitleExists && !bTitleExists) {
return a.fileName.compareTo(b.fileName);
}
return a.title.compareTo(b.title);
};
final String _str;
final String _publicString;
const SortingField(this._publicString, this._str);
case "FileName":
return (Note a, Note b) {
return a.fileName.compareTo(b.fileName);
};
String toInternalString() {
return _str;
}
case "Modified":
default:
return (Note a, Note b) {
var aDt = a.modified;
var bDt = b.modified;
if (aDt == null && bDt != null) {
return 1;
}
if (aDt != null && bDt == null) {
return -1;
}
if (bDt == null && aDt == null) {
return a.fileName.compareTo(b.fileName);
}
return bDt.compareTo(aDt);
};
String toPublicString() {
return tr(_publicString);
}
static const options = <SortingField>[
Modified,
Created,
FileName,
Title,
];
static SortingField fromInternalString(String str) {
for (var opt in options) {
if (opt.toInternalString() == str) {
return opt;
}
}
return Default;
}
@override
String toString() {
assert(false, "SortingField toString should never be called");
return "";
}
}
class SortingMode {
final SortingField field;
final SortingOrder order;
SortingMode(this.field, this.order);
NoteSortingFunction sortingFunction() {
switch (field) {
case SortingField.Created:
return order == SortingOrder.Descending
? _sortCreatedDesc
: _reverse(_sortCreatedDesc);
case SortingField.Title:
return order == SortingOrder.Descending
? _reverse(_sortTitleAsc)
: _sortTitleAsc;
case SortingField.FileName:
return order == SortingOrder.Descending
? _reverse(_sortFileNameAsc)
: _sortFileNameAsc;
case SortingField.Modified:
default:
return order == SortingOrder.Descending
? _sortModifiedDesc
: _reverse(_sortModifiedDesc);
}
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SortingMode && other.field == field && other.order == order;
@override
int get hashCode => order.hashCode ^ field.hashCode;
}
int _sortCreatedDesc(Note a, Note b) {
var aDt = a.created;
var bDt = b.created;
if (aDt == null && bDt != null) {
return 1;
}
if (aDt != null && bDt == null) {
return -1;
}
if (bDt == null && aDt == null) {
return a.fileName.compareTo(b.fileName);
}
return bDt.compareTo(aDt);
}
int _sortModifiedDesc(Note a, Note b) {
var aDt = a.modified;
var bDt = b.modified;
if (aDt == null && bDt != null) {
return 1;
}
if (aDt != null && bDt == null) {
return -1;
}
if (bDt == null && aDt == null) {
return a.fileName.compareTo(b.fileName);
}
return bDt.compareTo(aDt);
}
int _sortTitleAsc(Note a, Note b) {
var aTitleExists = a.title != null && a.title.isNotEmpty;
var bTitleExists = b.title != null && b.title.isNotEmpty;
if (!aTitleExists && bTitleExists) {
return 1;
}
if (aTitleExists && !bTitleExists) {
return -1;
}
if (!aTitleExists && !bTitleExists) {
return a.fileName.compareTo(b.fileName);
}
return a.title.compareTo(b.title);
}
int _sortFileNameAsc(Note a, Note b) {
return a.fileName.compareTo(b.fileName);
}
NoteSortingFunction _reverse(NoteSortingFunction func) {
return (Note a, Note b) {
int r = func(a, b);
if (r == 0) {
return r;
}
if (r < 0) {
return 1;
} else {
return -1;
}
};
}

View File

@ -39,8 +39,8 @@ class JournalView extends StatelessWidget {
var textTheme = Theme.of(context).textTheme;
DateTime date;
var sortingMode = folder.config.sortingMode;
if (sortingMode == SortingMode.Created) {
var sortingField = folder.config.sortingMode.field;
if (sortingField == SortingField.Created) {
date = note.created;
} else {
date = note.modified;

View File

@ -79,10 +79,10 @@ class StandardView extends StatelessWidget {
Widget trailing = Container();
DateTime date;
var sortingMode = folder.config.sortingMode;
if (sortingMode == SortingMode.Modified) {
var sortingField = folder.config.sortingMode.field;
if (sortingField == SortingField.Modified) {
date = note.modified;
} else if (sortingMode == SortingMode.Created) {
} else if (sortingField == SortingField.Created) {
date = note.created;
}

View File

@ -19,7 +19,7 @@ import 'package:gitjournal/widgets/app_bar_menu_button.dart';
import 'package:gitjournal/widgets/app_drawer.dart';
import 'package:gitjournal/widgets/new_note_nav_bar.dart';
import 'package:gitjournal/widgets/note_search_delegate.dart';
import 'package:gitjournal/widgets/sorting_order_selector.dart';
import 'package:gitjournal/widgets/sorting_mode_selector.dart';
import 'package:gitjournal/widgets/sync_button.dart';
enum DropDownChoices {
@ -190,7 +190,7 @@ class _FolderViewState extends State<FolderView> {
var newSortingMode = await showDialog<SortingMode>(
context: context,
builder: (BuildContext context) =>
SortingOrderSelector(sortedNotesFolder.sortingMode),
SortingModeSelector(sortedNotesFolder.sortingMode),
);
if (newSortingMode != null) {

View File

@ -32,7 +32,8 @@ class Settings extends ChangeNotifier {
String journalEditordefaultNewNoteFolderSpec = "";
RemoteSyncFrequency remoteSyncFrequency = RemoteSyncFrequency.Default;
SortingMode sortingMode = SortingMode.Default;
SortingField sortingField = SortingField.Default;
SortingOrder sortingOrder = SortingOrder.Default;
SettingsEditorType defaultEditor = SettingsEditorType.Default;
SettingsFolderViewType defaultView = SettingsFolderViewType.Default;
bool showNoteSummary = true;
@ -88,7 +89,10 @@ class Settings extends ChangeNotifier {
remoteSyncFrequency = RemoteSyncFrequency.fromInternalString(
pref.getString("remoteSyncFrequency"));
sortingMode = SortingMode.fromInternalString(pref.getString("sortingMode"));
sortingField =
SortingField.fromInternalString(pref.getString("sortingField"));
sortingOrder =
SortingOrder.fromInternalString(pref.getString("sortingOrder"));
defaultEditor =
SettingsEditorType.fromInternalString(pref.getString("defaultEditor"));
defaultView = SettingsFolderViewType.fromInternalString(
@ -169,8 +173,10 @@ class Settings extends ChangeNotifier {
"remoteSyncFrequency",
remoteSyncFrequency.toInternalString(),
defaultSet.remoteSyncFrequency.toInternalString());
_setString(pref, "sortingMode", sortingMode.toInternalString(),
defaultSet.sortingMode.toInternalString());
_setString(pref, "sortingField", sortingField.toInternalString(),
defaultSet.sortingField.toInternalString());
_setString(pref, "sortingOrder", sortingOrder.toInternalString(),
defaultSet.sortingOrder.toInternalString());
_setString(pref, "defaultEditor", defaultEditor.toInternalString(),
defaultSet.defaultEditor.toInternalString());
_setString(pref, "defaultView", defaultView.toInternalString(),
@ -252,7 +258,8 @@ class Settings extends ChangeNotifier {
journalEditordefaultNewNoteFolderSpec,
"defaultEditor": defaultEditor.toInternalString(),
"defaultView": defaultView.toInternalString(),
"sortingMode": sortingMode.toInternalString(),
"sortingField": sortingField.toInternalString(),
"sortingOrder": sortingOrder.toInternalString(),
"remoteSyncFrequency": remoteSyncFrequency.toInternalString(),
"showNoteSummary": showNoteSummary.toString(),
"folderViewHeaderType": folderViewHeaderType,

View File

@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:gitjournal/core/sorting_mode.dart';
import 'package:gitjournal/screens/settings_screen.dart';
class SortingModeSelector extends StatefulWidget {
final SortingMode selectedMode;
SortingModeSelector(this.selectedMode);
@override
_SortingModeSelectorState createState() => _SortingModeSelectorState();
}
class _SortingModeSelectorState extends State<SortingModeSelector> {
SortingField field;
SortingOrder order;
@override
void initState() {
super.initState();
field = widget.selectedMode.field;
order = widget.selectedMode.order;
}
@override
Widget build(BuildContext context) {
var children = <Widget>[
SettingsHeader(tr('settings.sortingMode.field')),
for (var sf in SortingField.options) _buildSortingTile(sf),
SettingsHeader(tr('settings.sortingMode.order')),
for (var so in SortingOrder.options) _buildSortingOrderTile(so),
];
return AlertDialog(
title: Text(tr("widgets.SortingOrderSelector.title")),
content: Column(
children: children,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
),
actions: [
OutlineButton(
child: Text(tr('settings.cancel')),
onPressed: () {
Navigator.of(context).pop();
},
),
OutlineButton(
child: Text(tr('settings.ok')),
onPressed: () {
Navigator.of(context).pop(SortingMode(field, order));
},
),
],
);
}
RadioListTile<SortingField> _buildSortingTile(SortingField sf) {
return RadioListTile<SortingField>(
title: Text(sf.toPublicString()),
value: sf,
groupValue: field,
onChanged: (SortingField sf) {
setState(() {
field = sf;
});
},
);
}
RadioListTile<SortingOrder> _buildSortingOrderTile(SortingOrder so) {
return RadioListTile<SortingOrder>(
title: Text(so.toPublicString()),
value: so,
groupValue: order,
onChanged: (SortingOrder so) {
setState(() {
order = so;
});
},
);
}
}

View File

@ -1,41 +0,0 @@
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:gitjournal/core/sorting_mode.dart';
class SortingOrderSelector extends StatelessWidget {
final SortingMode selectedMode;
SortingOrderSelector(this.selectedMode);
@override
Widget build(BuildContext context) {
var children = <Widget>[
_buildSortingTile(context, SortingMode.Modified),
_buildSortingTile(context, SortingMode.Created),
_buildSortingTile(context, SortingMode.Title),
_buildSortingTile(context, SortingMode.FileName),
];
return AlertDialog(
title: Text(tr("widgets.SortingOrderSelector.title")),
content: Column(
children: children,
mainAxisSize: MainAxisSize.min,
),
);
}
RadioListTile<SortingMode> _buildSortingTile(
BuildContext context,
SortingMode sm,
) {
return RadioListTile<SortingMode>(
title: Text(sm.toPublicString()),
value: sm,
groupValue: selectedMode,
onChanged: (SortingMode sm) => Navigator.of(context).pop(sm),
);
}
}

View File

@ -29,7 +29,8 @@ void main() {
defaultEditor: EditorType.Checklist,
defaultView: FolderViewType.Standard,
showNoteSummary: true,
sortingMode: SortingMode.Modified,
sortingMode:
SortingMode(SortingField.Modified, SortingOrder.Descending),
viewHeader: StandardViewHeader.TitleOrFileName,
fileNameFormat: NoteFileNameFormat.Default,
folder: folder,

View File

@ -39,7 +39,8 @@ void main() {
test('Should load the notes sorted', () async {
var sf = SortedNotesFolder(
folder: folder,
sortingMode: SortingMode.Modified,
sortingMode:
SortingMode(SortingField.Modified, SortingOrder.Descending),
);
expect(sf.hasNotes, true);
expect(sf.isEmpty, false);
@ -57,7 +58,8 @@ void main() {
test('Should on modification remains sorted', () async {
var sf = SortedNotesFolder(
folder: folder,
sortingMode: SortingMode.Modified,
sortingMode:
SortingMode(SortingField.Modified, SortingOrder.Descending),
);
var i = sf.notes.indexWhere((n) => n.body == "1");
@ -73,7 +75,8 @@ void main() {
test('Should add new note correctly', () async {
var sf = SortedNotesFolder(
folder: folder,
sortingMode: SortingMode.Modified,
sortingMode:
SortingMode(SortingField.Modified, SortingOrder.Descending),
);
var note = Note(folder, p.join(folder.folderPath, "new.md"));
@ -96,7 +99,8 @@ void main() {
test('Should add new note to end works correctly', () async {
var sf = SortedNotesFolder(
folder: folder,
sortingMode: SortingMode.Modified,
sortingMode:
SortingMode(SortingField.Modified, SortingOrder.Descending),
);
var note = Note(folder, p.join(folder.folderPath, "new.md"));
@ -120,7 +124,8 @@ void main() {
var folder = NotesFolderFS(null, tempDir.path);
var sf = SortedNotesFolder(
folder: folder,
sortingMode: SortingMode.Modified,
sortingMode:
SortingMode(SortingField.Modified, SortingOrder.Descending),
);
await folder.loadRecursively();

View File

@ -21,7 +21,8 @@ void main() {
n4.created = null;
var notes = [n1, n2, n3, n4];
var sortFn = SortingMode.Created.sortingFunction();
var sortFn = SortingMode(SortingField.Created, SortingOrder.Descending)
.sortingFunction();
notes.sort(sortFn);
expect(notes[0], n2);
@ -45,7 +46,8 @@ void main() {
n4.modified = null;
var notes = [n1, n2, n3, n4];
var sortFn = SortingMode.Modified.sortingFunction();
var sortFn = SortingMode(SortingField.Modified, SortingOrder.Descending)
.sortingFunction();
notes.sort(sortFn);
expect(notes[0], n2);