Skip to content

Commit

Permalink
Merge pull request #1417 from nextcloud/refactor/sort_box
Browse files Browse the repository at this point in the history
refactor: cleanup sort_box public api
  • Loading branch information
Leptopoda authored Jan 2, 2024
2 parents f4e5d97 + 2b67dd8 commit 6fc51bb
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 119 deletions.
4 changes: 2 additions & 2 deletions packages/neon/neon_files/lib/src/sort/files.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import 'package:neon_framework/sort_box.dart';
import 'package:nextcloud/webdav.dart';

final filesSortBox = SortBox<FilesSortProperty, WebDavFile>(
{
properties: {
FilesSortProperty.name: (file) => file.name.toLowerCase(),
FilesSortProperty.modifiedDate: (file) => file.lastModified?.millisecondsSinceEpoch ?? 0,
FilesSortProperty.size: (file) => file.size ?? 0,
FilesSortProperty.isFolder: (file) => file.isDirectory ? 0 : 1,
},
{
boxes: const {
FilesSortProperty.modifiedDate: {
(property: FilesSortProperty.name, order: SortBoxOrder.ascending),
},
Expand Down
4 changes: 2 additions & 2 deletions packages/neon/neon_news/lib/src/sort/articles.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import 'package:neon_news/src/options.dart';
import 'package:nextcloud/news.dart' as news;

final articlesSortBox = SortBox<ArticlesSortProperty, news.Article>(
{
properties: {
ArticlesSortProperty.publishDate: (article) => article.pubDate,
ArticlesSortProperty.alphabetical: (article) => article.title.toLowerCase(),
ArticlesSortProperty.byFeed: (article) => article.feedId,
},
{
boxes: const {
ArticlesSortProperty.alphabetical: {
(property: ArticlesSortProperty.publishDate, order: SortBoxOrder.descending),
},
Expand Down
4 changes: 2 additions & 2 deletions packages/neon/neon_news/lib/src/sort/feeds.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import 'package:neon_news/src/options.dart';
import 'package:nextcloud/news.dart' as news;

final feedsSortBox = SortBox<FeedsSortProperty, news.Feed>(
{
properties: {
FeedsSortProperty.alphabetical: (feed) => feed.title.toLowerCase(),
FeedsSortProperty.unreadCount: (feed) => feed.unreadCount ?? 0,
},
{
boxes: const {
FeedsSortProperty.alphabetical: {
(property: FeedsSortProperty.unreadCount, order: SortBoxOrder.descending),
},
Expand Down
4 changes: 2 additions & 2 deletions packages/neon/neon_news/lib/src/sort/folders.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import 'package:neon_news/src/options.dart';
import 'package:nextcloud/news.dart' as news;

final foldersSortBox = SortBox<FoldersSortProperty, FolderFeedsWrapper>(
{
properties: {
FoldersSortProperty.alphabetical: (folderFeedsWrapper) => folderFeedsWrapper.folder.name.toLowerCase(),
FoldersSortProperty.unreadCount: (folderFeedsWrapper) => folderFeedsWrapper.unreadCount,
},
{
boxes: const {
FoldersSortProperty.alphabetical: {
(property: FoldersSortProperty.unreadCount, order: SortBoxOrder.descending),
},
Expand Down
4 changes: 2 additions & 2 deletions packages/neon/neon_notes/lib/src/sort/categories.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import 'package:neon_framework/sort_box.dart';
import 'package:neon_notes/src/options.dart';

final categoriesSortBox = SortBox<CategoriesSortProperty, NoteCategory>(
{
properties: {
CategoriesSortProperty.alphabetical: (category) => category.name.toLowerCase(),
CategoriesSortProperty.notesCount: (category) => category.count,
},
{
boxes: const {
CategoriesSortProperty.notesCount: {
(property: CategoriesSortProperty.alphabetical, order: SortBoxOrder.ascending),
},
Expand Down
4 changes: 2 additions & 2 deletions packages/neon/neon_notes/lib/src/sort/notes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import 'package:neon_notes/src/options.dart';
import 'package:nextcloud/notes.dart' as notes;

final notesSortBox = SortBox<NotesSortProperty, notes.Note>(
{
properties: {
NotesSortProperty.alphabetical: (note) => note.title.toLowerCase(),
NotesSortProperty.lastModified: (note) => note.modified,
NotesSortProperty.favorite: (note) => note.favorite ? 0 : 1,
},
{
boxes: const {
NotesSortProperty.alphabetical: {
(property: NotesSortProperty.lastModified, order: SortBoxOrder.descending),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ class SortBoxBuilder<T extends Enum, R> extends StatelessWidget {
valueListenable: sortBoxOrder,
builder: (context, order, _) {
final box = (property: property, order: order);
sortBox.sort(input, box, presort);

return builder(context, sortBox.sort(input, box, presort));
return builder(context, input);
},
),
);
Expand Down
86 changes: 1 addition & 85 deletions packages/sort_box/lib/sort_box.dart
Original file line number Diff line number Diff line change
@@ -1,85 +1 @@
/// Signature of a function returning a [Comparable].
typedef ComparableGetter<T> = Comparable<Object> Function(T);

/// The box to sort by.
///
/// A box contains a property and a corresponding order.
typedef Box<T> = ({T property, SortBoxOrder order});

/// Sorting Box to sort [List]s on multiple properties.
class SortBox<T extends Enum, R> {
/// Constructs a new SortBox.
///
/// A *Box* is a record of a property and how to order it.
SortBox(
this._properties,
this._boxes,
);

/// A mapping of all values [T] to their [ComparableGetter].
final Map<T, ComparableGetter<R>> _properties;

/// A mapping of values [T] to their *Boxes*.
///
/// The Boxes are applied if two elements are considered equal regarding their property [T].
final Map<T, Set<Box<T>>> _boxes;

/// Sorts the [input] list according to their [box].
///
/// A box contains the property and [SortBoxOrder] how the list should be sorted.
/// In case the property of two elements is considered equal all following boxes specified at `_boxes[property]` are applied.
/// If specified [presort] will be applied before [box] and [_boxes].
///
/// This function sorts the input in place and a reference to it mutating the provided list.
List<R> sort(
List<R> input,
Box<T> box, [
Set<Box<T>>? presort,
]) {
if (input.length <= 1) {
return input;
}

final boxes = {
...?presort,
box,
...?_boxes[box.property],
};

final sorted = input..sort((item1, item2) => _compare(item1, item2, boxes.iterator..moveNext()));

return sorted;
}

int _compare(
R item1,
R item2,
Iterator<Box<T>> iterator,
) {
final box = iterator.current;
final comparableGetter = _properties[box.property]!;

final comparable1 = comparableGetter(item1);
final comparable2 = comparableGetter(item2);

final order = switch (box.order) {
SortBoxOrder.ascending => comparable1.compareTo(comparable2),
SortBoxOrder.descending => comparable2.compareTo(comparable1),
};

if (order == 0 && iterator.moveNext()) {
return _compare(item1, item2, iterator);
}

return order;
}
}

/// Sorting order used by [SortBox].
enum SortBoxOrder {
/// Ascending sorting order.
ascending,

/// Descending sorting order.
descending,
}
export 'src/sort_box.dart';
87 changes: 87 additions & 0 deletions packages/sort_box/lib/src/sort_box.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import 'package:meta/meta.dart';

/// Signature of a function returning a [Comparable].
typedef ComparableGetter<T> = Comparable<Object> Function(T);

/// The box to sort by.
///
/// A box contains a property and a corresponding order.
typedef Box<T> = ({T property, SortBoxOrder order});

/// Sorting Box to sort [List]s on multiple properties.
@immutable
class SortBox<T extends Enum, R> {
/// Constructs a new SortBox.
///
/// A *Box* is a record of a property and how to order it.
const SortBox({
required this.properties,
required this.boxes,
});

/// A mapping of all values [T] to their [ComparableGetter].
final Map<T, ComparableGetter<R>> properties;

/// A mapping of values [T] to their *Boxes*.
///
/// The Boxes are applied if two elements are considered equal regarding their property [T].
final Map<T, Set<Box<T>>> boxes;

/// Sorts the [input] list according to their [box].
///
/// A box contains the property and [SortBoxOrder] how the list should be sorted.
/// In case the property of two elements is considered equal all following boxes specified at `boxes[property]` are applied.
/// If specified [presort] will be applied before [box] and [boxes].
void sort(
List<R> input,
Box<T> box, [
Set<Box<T>>? presort,
]) {
if (input.length <= 1) {
return;
}

final boxes = {
...?presort,
box,
...?this.boxes[box.property],
};

input.sort((item1, item2) => _compare(item1, item2, boxes.iterator..moveNext()));
}

/// Iteratively compares two elements [item1] and [item2] according to the current box in [iterator].
int _compare(
R item1,
R item2,
Iterator<Box<T>> iterator,
) {
final box = iterator.current;
final comparableGetter = properties[box.property]!;

final comparable1 = comparableGetter(item1);
final comparable2 = comparableGetter(item2);

final order = switch (box.order) {
SortBoxOrder.ascending => comparable1.compareTo(comparable2),
SortBoxOrder.descending => comparable2.compareTo(comparable1),
};

// If equal try the next property in the box
if (order == 0 && iterator.moveNext()) {
return _compare(item1, item2, iterator);
}

return order;
}
}

/// Sorting order used by [SortBox].
enum SortBoxOrder {
/// Ascending sorting order.
ascending,

/// Descending sorting order.
descending,
}
3 changes: 3 additions & 0 deletions packages/sort_box/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ publish_to: none
environment:
sdk: '>=3.1.0 <4.0.0'

dependencies:
meta: ^1.0.0

dev_dependencies:
neon_lints:
git:
Expand Down
Loading

0 comments on commit 6fc51bb

Please sign in to comment.