Skip to content

Commit

Permalink
Sort notes and folders
Browse files Browse the repository at this point in the history
  • Loading branch information
Edgiest05 committed Nov 30, 2024
1 parent 10d1906 commit f21e159
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 17 deletions.
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ android {
applicationId "com.adilhanney.saber"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 23
minSdkVersion 24
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
Expand Down
183 changes: 183 additions & 0 deletions lib/components/home/sort_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import 'dart:io';
import 'dart:math';

Check warning on line 2 in lib/components/home/sort_button.dart

View workflow job for this annotation

GitHub Actions / Run Flutter tests

Unused import: 'dart:math'.

Try removing the import directive. See https://dart.dev/diagnostics/unused_import to learn more about this problem.

import 'package:flutter/material.dart';
import 'package:saber/data/file_manager/file_manager.dart';
import 'package:saber/data/prefs.dart';
import 'package:saber/i18n/strings.g.dart';
import 'package:saber/pages/editor/editor.dart';

class SortNotes {
SortNotes._();

static final Map<String, void Function(List<String>, bool)> _sortFunctions = {
t.home.sortNames.alphabetical: _sortNotesAlpha,
t.home.sortNames.lastModified: _sortNotesLastModified,
t.home.sortNames.sizeOnDisk: _sortNotesSize,
};
static final PlainPref<int> _sortFunctionIdx = Prefs.sortFunctionIdx;
static final PlainPref<bool> _isIncreasingOrder = Prefs.isSortIncreasing;

static bool _isNeeded = true;
static bool get isNeeded => _isNeeded;

static int get sortFunctionIdx => _sortFunctionIdx.value;
static set sortFunctionIdx(int value) {
_sortFunctionIdx.value = value;
_isNeeded = true;
}

static bool get isIncreasingOrder => _isIncreasingOrder.value;
static set isIncreasingOrder(bool value) {
_isIncreasingOrder.value = value;
_isNeeded = true;
}

static void _reverse(List<String> list) {
final n = list.length;
for (int i = 0; i < n / 2; i++) {
final tmp = list[i];
list[i] = list[n - i - 1];
list[n - i - 1] = tmp;
}
}

static void sortNotes(List<String> filePaths, {bool forced = false}) {
if (_isNeeded || forced) {
_sortFunctions[_sortFunctions.keys.elementAt(sortFunctionIdx)]!
.call(filePaths, isIncreasingOrder);
_isNeeded = false;
}
}

static void _sortNotesAlpha(List<String> filePaths, bool isIncreasing) {
filePaths.sort((a, b) => a.split('/').last.compareTo(b.split('/').last));
if (!isIncreasing) _reverse(filePaths);
}

static DateTime _getDirLastModified(Directory dir) {
assert(dir.existsSync());
DateTime out = dir.statSync().modified;
for (FileSystemEntity entity in dir.listSync(recursive: true, followLinks: false)) {
if (entity is File && entity.absolute.path.endsWith(Editor.extension)) {
final DateTime curFileModified = entity.lastModifiedSync();
if (curFileModified.isAfter(out)) out = curFileModified;
}
}
return out;
}

static void _sortNotesLastModified(
List<String> filePaths, bool isIncreasing) {
filePaths.sort((a, b) {
final Directory firstDir = Directory(FileManager.documentsDirectory + a);
final Directory secondDir = Directory(FileManager.documentsDirectory + b);
final DateTime firstTime = firstDir.existsSync()
? _getDirLastModified(firstDir)
: FileManager.lastModified(a + Editor.extension);
final DateTime secondTime = secondDir.existsSync()
? _getDirLastModified(secondDir)
: FileManager.lastModified(b + Editor.extension);
return firstTime.compareTo(secondTime);
});
if (!isIncreasing) _reverse(filePaths);
}

static int _getDirSize(Directory dir) {
assert(dir.existsSync());
int out = 0;
for (FileSystemEntity entity in dir.listSync(recursive: true, followLinks: false)) {
if (entity is File && entity.absolute.path.endsWith(Editor.extension)) {
final int curFileSize = entity.lengthSync();
out += curFileSize;
}
}
return out;
}

static void _sortNotesSize(List<String> filePaths, bool isIncreasing) {
filePaths.sort((a, b) {
final Directory firstDir = Directory(FileManager.documentsDirectory + a);
final Directory secondDir = Directory(FileManager.documentsDirectory + b);
final int firstSize = firstDir.existsSync()
? _getDirSize(firstDir)
: FileManager.getFile('$a${Editor.extension}').statSync().size;
final int secondSize = secondDir.existsSync()
? _getDirSize(secondDir)
: FileManager.getFile('$b${Editor.extension}').statSync().size;
return firstSize.compareTo(secondSize);
});
if (!isIncreasing) _reverse(filePaths);
}
}

class SortButton extends StatelessWidget {
const SortButton({
super.key,
required this.callback,
});

final void Function() callback;

@override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(Icons.sort),
onPressed: () async {
showDialog(
context: context,
builder: (BuildContext context) {
return _SortButtonDialog();
},
).then((_) => callback());
},
);
}
}

class _SortButtonDialog extends StatefulWidget {
@override
State<_SortButtonDialog> createState() => _SortButtonDialogState();
}

class _SortButtonDialogState extends State<_SortButtonDialog> {
@override
Widget build(BuildContext context) {
final List<String> sortNames = SortNotes._sortFunctions.keys.toList();

return Align(
alignment: Alignment.topRight,
child: Container(
width: 220,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5)),
clipBehavior: Clip.antiAlias,
child: Material(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
for (int idx = 0; idx < sortNames.length; idx++)
RadioListTile<int>(
title: Text(sortNames[idx]),
onChanged: (int? newValue) => {
SortNotes.sortFunctionIdx = newValue!,
setState(() {}),
// Navigator.pop(context),
},
groupValue: SortNotes.sortFunctionIdx,
value: idx,
),
CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
title: Text(t.home.sortNames.increasing),
value: SortNotes.isIncreasingOrder,
onChanged: (bool? v) => {
SortNotes.isIncreasingOrder = v!,
setState(() {}),
}),
],
),
),
),
);
}
}
6 changes: 6 additions & 0 deletions lib/data/prefs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ abstract class Prefs {
static late final PlainPref<bool> hideHomeBackgrounds;
static late final PlainPref<bool> printPageIndicators;

static late final PlainPref<int> sortFunctionIdx;
static late final PlainPref<bool> isSortIncreasing;

static late final PlainPref<double> maxImageSize;

static late final PlainPref<bool> autoClearWhiteboardOnExit;
Expand Down Expand Up @@ -200,6 +203,9 @@ abstract class Prefs {
hideHomeBackgrounds = PlainPref('hideHomeBackgrounds', false);
printPageIndicators = PlainPref('printPageIndicators', false);

sortFunctionIdx = PlainPref('sortFunctionIdx', 0);
isSortIncreasing = PlainPref('isSortIncreasing', true);

maxImageSize = PlainPref('maxImageSize', 1000);

autoClearWhiteboardOnExit = PlainPref('autoClearWhiteboardOnExit', false);
Expand Down
5 changes: 5 additions & 0 deletions lib/i18n/strings.i18n.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ home:
multipleRenamedTo: "The following notes will be renamed:"
numberRenamedTo: $n notes will be renamed to avoid conflicts
deleteNote: Delete note
sortNames:
alphabetical: Alphabetical
lastModified: Last Modified
sizeOnDisk: Size
increasing: Increasing
renameFolder:
renameFolder: Rename folder
folderName: Folder name
Expand Down
14 changes: 14 additions & 0 deletions lib/i18n/strings_en.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class TranslationsHomeEn {
late final TranslationsHomeRenameNoteEn renameNote = TranslationsHomeRenameNoteEn.internal(_root);
late final TranslationsHomeMoveNoteEn moveNote = TranslationsHomeMoveNoteEn.internal(_root);
String get deleteNote => 'Delete note';
late final TranslationsHomeSortNamesEn sortNames = TranslationsHomeSortNamesEn.internal(_root);
late final TranslationsHomeRenameFolderEn renameFolder = TranslationsHomeRenameFolderEn.internal(_root);
late final TranslationsHomeDeleteFolderEn deleteFolder = TranslationsHomeDeleteFolderEn.internal(_root);
}
Expand Down Expand Up @@ -309,6 +310,19 @@ class TranslationsHomeMoveNoteEn {
String numberRenamedTo({required Object n}) => '${n} notes will be renamed to avoid conflicts';
}

// Path: home.sortNames
class TranslationsHomeSortNamesEn {
TranslationsHomeSortNamesEn.internal(this._root);

final Translations _root; // ignore: unused_field

// Translations
String get alphabetical => 'Alphabetical';
String get lastModified => 'Last Modified';
String get sizeOnDisk => 'Size';
String get increasing => 'Increasing';
}

// Path: home.renameFolder
class TranslationsHomeRenameFolderEn {
TranslationsHomeRenameFolderEn.internal(this._root);
Expand Down
14 changes: 14 additions & 0 deletions lib/i18n/strings_it.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class _TranslationsHomeIt extends TranslationsHomeEn {
@override late final _TranslationsHomeRenameNoteIt renameNote = _TranslationsHomeRenameNoteIt._(_root);
@override late final _TranslationsHomeMoveNoteIt moveNote = _TranslationsHomeMoveNoteIt._(_root);
@override String get deleteNote => 'Elimina nota';
@override late final _TranslationsHomeSortNamesIt sortNames = _TranslationsHomeSortNamesIt._(_root);
@override late final _TranslationsHomeRenameFolderIt renameFolder = _TranslationsHomeRenameFolderIt._(_root);
@override late final _TranslationsHomeDeleteFolderIt deleteFolder = _TranslationsHomeDeleteFolderIt._(_root);
}
Expand Down Expand Up @@ -306,6 +307,19 @@ class _TranslationsHomeMoveNoteIt extends TranslationsHomeMoveNoteEn {
@override String numberRenamedTo({required Object n}) => '${n} le note verranno rinominate per evitare conflitti';
}

// Path: home.sortNames
class _TranslationsHomeSortNamesIt extends TranslationsHomeSortNamesEn {
_TranslationsHomeSortNamesIt._(TranslationsIt root) : this._root = root, super.internal(root);

final TranslationsIt _root; // ignore: unused_field

// Translations
@override String get alphabetical => 'Alfabetico';
@override String get lastModified => 'Ultima Modifica';
@override String get sizeOnDisk => 'Dimensioni';
@override String get increasing => 'Crescente';
}

// Path: home.renameFolder
class _TranslationsHomeRenameFolderIt extends TranslationsHomeRenameFolderEn {
_TranslationsHomeRenameFolderIt._(TranslationsIt root) : this._root = root, super.internal(root);
Expand Down
5 changes: 5 additions & 0 deletions lib/i18n/strings_it.i18n.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ home:
multipleRenamedTo: "Le note seguenti verranno rinominate:"
numberRenamedTo: $n le note verranno rinominate per evitare conflitti
deleteNote: Elimina nota
sortNames:
alphabetical: Alfabetico
lastModified: Ultima Modifica
sizeOnDisk: Dimensioni
increasing: Crescente
renameFolder:
renameFolder: Rinomina cartella
folderName: Nome cartella
Expand Down
40 changes: 30 additions & 10 deletions lib/pages/home/browse.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:saber/components/home/move_note_button.dart';
import 'package:saber/components/home/new_note_button.dart';
import 'package:saber/components/home/no_files.dart';
import 'package:saber/components/home/rename_note_button.dart';
import 'package:saber/components/home/sort_button.dart';
import 'package:saber/components/home/syncing_button.dart';
import 'package:saber/data/file_manager/file_manager.dart';
import 'package:saber/data/routes.dart';
Expand All @@ -30,6 +31,8 @@ class BrowsePage extends StatefulWidget {

class _BrowsePageState extends State<BrowsePage> {
DirectoryChildren? children;
final List<String> files = [];
final List<String> folders = [];

final List<String?> pathHistory = [];
String? path;
Expand All @@ -41,6 +44,7 @@ class _BrowsePageState extends State<BrowsePage> {
path = widget.initialPath;

findChildrenOfPath();

fileWriteSubscription =
FileManager.fileWriteStream.stream.listen(fileWriteListener);
selectedFiles.addListener(_setState);
Expand Down Expand Up @@ -73,6 +77,16 @@ class _BrowsePageState extends State<BrowsePage> {
}

children = await FileManager.getChildrenOfDirectory(path ?? '/');
files.clear();
for (String filePath in children?.files ?? const []) {
files.add("${path ?? ""}/$filePath");
}
folders.clear();
for (String directoryPath in children?.directories ?? const []) {
folders.add("${path ?? ""}/$directoryPath");
}
SortNotes.sortNotes(files, forced: true);
SortNotes.sortNotes(folders, forced: true);

if (mounted) setState(() {});
}
Expand Down Expand Up @@ -135,8 +149,18 @@ class _BrowsePageState extends State<BrowsePage> {
titlePadding: EdgeInsetsDirectional.only(
start: cupertino ? 0 : 16, bottom: 16),
),
actions: const [
SyncingButton(),
actions: [
const SyncingButton(),
SortButton(
callback: () => {
if (SortNotes.isNeeded)
{
SortNotes.sortNotes(files, forced: true),
SortNotes.sortNotes(folders, forced: true),
setState(() {}),
}
},
),
],
),
),
Expand Down Expand Up @@ -164,10 +188,9 @@ class _BrowsePageState extends State<BrowsePage> {
await FileManager.deleteDirectory(folderPath);
findChildrenOfPath();
},
folders: [
for (String directoryPath in children?.directories ?? const [])
directoryPath,
],
folders: folders
.map((e) => e.split('/').last)
.toList(),
),
if (children == null) ...[
// loading
Expand All @@ -185,10 +208,7 @@ class _BrowsePageState extends State<BrowsePage> {
),
sliver: MasonryFiles(
crossAxisCount: crossAxisCount,
files: [
for (String filePath in children?.files ?? const [])
"${path ?? ""}/$filePath",
],
files: files,
selectedFiles: selectedFiles,
),
),
Expand Down
Loading

0 comments on commit f21e159

Please sign in to comment.