Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 189 additions & 0 deletions lib/components/home/sort_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import 'dart:io';

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';
import 'package:stow_plain/stow_plain.dart';

class SortNotes {
SortNotes._();

static final List<void Function(List<String>, bool)> _sortFunctions = [
_sortNotesAlpha,
_sortNotesLastModified,
_sortNotesSize,
];
static final PlainStow<int> _sortFunctionIdx = stows.sortFunctionIdx;
static final PlainStow<bool> _isIncreasingOrder = stows.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[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) {
// Needs to match the order of _sortFunctions
final List<String> sortNames = [
t.home.sortNames.alphabetical,
t.home.sortNames.lastModified,
t.home.sortNames.sizeOnDisk,
];

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(() {}),
}),
],
),
),
),
);
}
}
5 changes: 5 additions & 0 deletions lib/data/prefs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ class Stows {
final printPageIndicators =
PlainStow('printPageIndicators', false, volatile: !_isOnMainIsolate);

final sortFunctionIdx =
PlainStow('sortFunctionIdx', 0, volatile: !_isOnMainIsolate);
final isSortIncreasing =
PlainStow('isSortIncreasing', true, volatile: !_isOnMainIsolate);

final maxImageSize =
PlainStow<double>('maxImageSize', 1000, volatile: !_isOnMainIsolate);

Expand Down
5 changes: 5 additions & 0 deletions lib/i18n/en.i18n.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,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
5 changes: 5 additions & 0 deletions lib/i18n/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
14 changes: 14 additions & 0 deletions lib/i18n/strings_en.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,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 @@ -314,6 +315,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 @@ -75,6 +75,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);
@override String get noPreviewAvailable => 'Nessuna anteprima disponibile';
Expand Down Expand Up @@ -312,6 +313,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
38 changes: 28 additions & 10 deletions lib/pages/home/browse.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:saber/components/home/new_note_button.dart';
import 'package:saber/components/home/no_files.dart';
import 'package:saber/components/home/path_components.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 @@ -33,6 +34,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 @@ -44,6 +47,7 @@ class _BrowsePageState extends State<BrowsePage> {
path = widget.initialPath;

findChildrenOfPath();

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

children = widget.overrideChildren ??
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 @@ -142,8 +156,18 @@ class _BrowsePageState extends State<BrowsePage> {
titlePadding: EdgeInsetsDirectional.only(
start: cupertino ? 0 : 16, bottom: 8),
),
actions: const [
SyncingButton(),
actions: [
const SyncingButton(),
SortButton(
callback: () => {
if (SortNotes.isNeeded)
{
SortNotes.sortNotes(files, forced: true),
SortNotes.sortNotes(folders, forced: true),
setState(() {}),
}
},
),
],
),
SliverToBoxAdapter(
Expand Down Expand Up @@ -177,10 +201,7 @@ 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 @@ -200,10 +221,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
Loading