Browse Source

Merge pull request #1044 from nextcloud/feat/named_records

Feat/named records
pull/1048/head
Nikolas Rimikis 1 year ago committed by GitHub
parent
commit
b4cb8c239f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      packages/neon/neon/lib/src/blocs/apps.dart
  2. 10
      packages/neon/neon/lib/src/models/app_implementation.dart
  3. 4
      packages/neon/neon/lib/src/sort_box/sort_box_builder.dart
  4. 8
      packages/neon/neon_dashboard/lib/src/app.dart
  5. 7
      packages/neon/neon_files/lib/neon_files.dart
  6. 4
      packages/neon/neon_files/lib/sort/files.dart
  7. 2
      packages/neon/neon_files/lib/widgets/browser_view.dart
  8. 2
      packages/neon/neon_news/lib/neon_news.dart
  9. 4
      packages/neon/neon_news/lib/sort/articles.dart
  10. 4
      packages/neon/neon_news/lib/sort/feeds.dart
  11. 10
      packages/neon/neon_news/lib/sort/folders.dart
  12. 4
      packages/neon/neon_news/lib/widgets/folders_view.dart
  13. 6
      packages/neon/neon_notes/lib/neon_notes.dart
  14. 2
      packages/neon/neon_notes/lib/sort/categories.dart
  15. 4
      packages/neon/neon_notes/lib/sort/notes.dart
  16. 2
      packages/neon/neon_notes/lib/widgets/notes_view.dart
  17. 8
      packages/neon/neon_notifications/lib/neon_notifications.dart
  18. 1
      packages/nextcloud/lib/nextcloud.dart
  19. 2
      packages/nextcloud/lib/src/helpers/common.dart
  20. 10
      packages/nextcloud/lib/src/helpers/core.dart
  21. 7
      packages/nextcloud/lib/src/helpers/news.dart
  22. 10
      packages/nextcloud/lib/src/helpers/notes.dart
  23. 4
      packages/nextcloud/test/core_test.dart
  24. 4
      packages/nextcloud/test/news_test.dart
  25. 4
      packages/nextcloud/test/notes_test.dart
  26. 20
      packages/sort_box/lib/sort_box.dart
  27. 20
      packages/sort_box/test/sort_box_test.dart

17
packages/neon/neon/lib/src/blocs/apps.dart

@ -123,9 +123,9 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
final notSupported = <String, String?>{}; final notSupported = <String, String?>{};
try { try {
final (coreSupported, coreMinimumVersion) = _account.client.core.isSupported(capabilities.requireData); final coreCheck = _account.client.core.isSupported(capabilities.requireData);
if (!coreSupported) { if (!coreCheck.isSupported) {
notSupported['core'] = coreMinimumVersion.toString(); notSupported['core'] = coreCheck.minimumVersion.toString();
} }
} catch (e, s) { } catch (e, s) {
debugPrint(e.toString()); debugPrint(e.toString());
@ -134,9 +134,14 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
for (final app in apps.requireData) { for (final app in apps.requireData) {
try { try {
final (supported, minimumVersion) = await app.isSupported(_account, capabilities.requireData); final check = await app.isSupported(_account, capabilities.requireData);
if (!(supported ?? true)) {
notSupported[app.id] = minimumVersion; if (check == null) {
continue;
}
if (!check.isSupported) {
notSupported[app.id] = check.minimumVersion;
} }
} catch (e, s) { } catch (e, s) {
debugPrint(e.toString()); debugPrint(e.toString());

10
packages/neon/neon/lib/src/models/app_implementation.dart

@ -15,6 +15,7 @@ import 'package:neon/src/settings/models/storage.dart';
import 'package:neon/src/utils/provider.dart'; import 'package:neon/src/utils/provider.dart';
import 'package:neon/src/widgets/drawer_destination.dart'; import 'package:neon/src/widgets/drawer_destination.dart';
import 'package:nextcloud/core.dart' as core; import 'package:nextcloud/core.dart' as core;
import 'package:nextcloud/nextcloud.dart' show VersionSupported;
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:vector_graphics/vector_graphics.dart'; import 'package:vector_graphics/vector_graphics.dart';
@ -36,15 +37,14 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
/// Checks if the app is supported on the server of the [account]. /// Checks if the app is supported on the server of the [account].
/// ///
/// A `supported` value of `null` means that it can not be known if the app is supported. /// A value of `null` means that it can not be known if the app is supported.
/// This is the case for apps that depend on the server version like files and we assume that the app is supported. /// This is the case for apps that depend on the server version like files and we assume that the app is supported.
/// The server support is handled differently. /// The server support is handled differently.
/// FutureOr<VersionSupported<String>?> isSupported(
/// The first value of the record is the supported status and the second value is the supported minimum version.
FutureOr<(bool? supported, String? minimumVersion)> isSupported(
final Account account, final Account account,
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities, final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
); ) =>
null;
final blocsCache = AccountCache<T>(); final blocsCache = AccountCache<T>();

4
packages/neon/neon/lib/src/sort_box/sort_box_builder.dart

@ -38,7 +38,7 @@ class SortBoxBuilder<T extends Enum, R> extends StatelessWidget {
final SortBoxWidgetBuilder<R> builder; final SortBoxWidgetBuilder<R> builder;
/// Pre sorts input. /// Pre sorts input.
final Set<(T property, SortBoxOrder order)>? presort; final Set<Box<T>>? presort;
@override @override
Widget build(final BuildContext context) { Widget build(final BuildContext context) {
@ -52,7 +52,7 @@ class SortBoxBuilder<T extends Enum, R> extends StatelessWidget {
builder: (final context, final property, final _) => ValueListenableBuilder<SortBoxOrder>( builder: (final context, final property, final _) => ValueListenableBuilder<SortBoxOrder>(
valueListenable: sortBoxOrder, valueListenable: sortBoxOrder,
builder: (final context, final order, final _) { builder: (final context, final order, final _) {
final box = (property, order); final box = (property: property, order: order);
return builder(context, sortBox.sort(input, box, presort)); return builder(context, sortBox.sort(input, box, presort));
}, },

8
packages/neon/neon_dashboard/lib/src/app.dart

@ -6,7 +6,6 @@ import 'package:neon_dashboard/src/blocs/dashboard.dart';
import 'package:neon_dashboard/src/options.dart'; import 'package:neon_dashboard/src/options.dart';
import 'package:neon_dashboard/src/pages/main.dart'; import 'package:neon_dashboard/src/pages/main.dart';
import 'package:neon_dashboard/src/routes.dart'; import 'package:neon_dashboard/src/routes.dart';
import 'package:nextcloud/core.dart' as core;
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
/// Implementation of the server `dashboard` app. /// Implementation of the server `dashboard` app.
@ -34,11 +33,4 @@ class DashboardApp extends AppImplementation<DashboardBloc, DashboardAppSpecific
@override @override
final RouteBase route = $dashboardAppRoute; final RouteBase route = $dashboardAppRoute;
@override
(bool?, String?) isSupported(
final Account account,
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) =>
const (null, null);
} }

7
packages/neon/neon_files/lib/neon_files.dart

@ -72,11 +72,4 @@ class FilesApp extends AppImplementation<FilesBloc, FilesAppSpecificOptions> {
@override @override
final RouteBase route = $filesAppRoute; final RouteBase route = $filesAppRoute;
@override
(bool? supported, String? minimumVersion) isSupported(
final Account account,
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) =>
const (null, null);
} }

4
packages/neon/neon_files/lib/sort/files.dart

@ -9,10 +9,10 @@ final filesSortBox = SortBox<FilesSortProperty, WebDavFile>(
}, },
{ {
FilesSortProperty.modifiedDate: { FilesSortProperty.modifiedDate: {
(FilesSortProperty.name, SortBoxOrder.ascending), (property: FilesSortProperty.name, order: SortBoxOrder.ascending),
}, },
FilesSortProperty.size: { FilesSortProperty.size: {
(FilesSortProperty.name, SortBoxOrder.ascending), (property: FilesSortProperty.name, order: SortBoxOrder.ascending),
}, },
}, },
); );

2
packages/neon/neon_files/lib/widgets/browser_view.dart

@ -80,7 +80,7 @@ class _FilesBrowserViewState extends State<FilesBrowserView> {
sortProperty: widget.bloc.options.filesSortPropertyOption, sortProperty: widget.bloc.options.filesSortPropertyOption,
sortBoxOrder: widget.bloc.options.filesSortBoxOrderOption, sortBoxOrder: widget.bloc.options.filesSortBoxOrderOption,
presort: const { presort: const {
(FilesSortProperty.isFolder, SortBoxOrder.ascending), (property: FilesSortProperty.isFolder, order: SortBoxOrder.ascending),
}, },
input: files, input: files,
builder: (final context, final sorted) { builder: (final context, final sorted) {

2
packages/neon/neon_news/lib/neon_news.dart

@ -85,7 +85,7 @@ class NewsApp extends AppImplementation<NewsBloc, NewsAppSpecificOptions> {
BehaviorSubject<int> getUnreadCounter(final NewsBloc bloc) => bloc.unreadCounter; BehaviorSubject<int> getUnreadCounter(final NewsBloc bloc) => bloc.unreadCounter;
@override @override
Future<(bool? supported, String? minimumVersion)> isSupported( Future<VersionSupported<String>> isSupported(
final Account account, final Account account,
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities, final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) => ) =>

4
packages/neon/neon_news/lib/sort/articles.dart

@ -8,10 +8,10 @@ final articlesSortBox = SortBox<ArticlesSortProperty, news.Article>(
}, },
{ {
ArticlesSortProperty.alphabetical: { ArticlesSortProperty.alphabetical: {
(ArticlesSortProperty.publishDate, SortBoxOrder.descending), (property: ArticlesSortProperty.publishDate, order: SortBoxOrder.descending),
}, },
ArticlesSortProperty.byFeed: { ArticlesSortProperty.byFeed: {
(ArticlesSortProperty.alphabetical, SortBoxOrder.ascending), (property: ArticlesSortProperty.alphabetical, order: SortBoxOrder.ascending),
}, },
}, },
); );

4
packages/neon/neon_news/lib/sort/feeds.dart

@ -7,10 +7,10 @@ final feedsSortBox = SortBox<FeedsSortProperty, news.Feed>(
}, },
{ {
FeedsSortProperty.alphabetical: { FeedsSortProperty.alphabetical: {
(FeedsSortProperty.unreadCount, SortBoxOrder.descending), (property: FeedsSortProperty.unreadCount, order: SortBoxOrder.descending),
}, },
FeedsSortProperty.unreadCount: { FeedsSortProperty.unreadCount: {
(FeedsSortProperty.alphabetical, SortBoxOrder.ascending), (property: FeedsSortProperty.alphabetical, order: SortBoxOrder.ascending),
}, },
}, },
); );

10
packages/neon/neon_news/lib/sort/folders.dart

@ -2,17 +2,17 @@ part of '../neon_news.dart';
final foldersSortBox = SortBox<FoldersSortProperty, FolderFeedsWrapper>( final foldersSortBox = SortBox<FoldersSortProperty, FolderFeedsWrapper>(
{ {
FoldersSortProperty.alphabetical: (final folderFeedsWrapper) => folderFeedsWrapper.$1.name.toLowerCase(), FoldersSortProperty.alphabetical: (final folderFeedsWrapper) => folderFeedsWrapper.folder.name.toLowerCase(),
FoldersSortProperty.unreadCount: (final folderFeedsWrapper) => folderFeedsWrapper.$3, FoldersSortProperty.unreadCount: (final folderFeedsWrapper) => folderFeedsWrapper.unreadCount,
}, },
{ {
FoldersSortProperty.alphabetical: { FoldersSortProperty.alphabetical: {
(FoldersSortProperty.unreadCount, SortBoxOrder.descending), (property: FoldersSortProperty.unreadCount, order: SortBoxOrder.descending),
}, },
FoldersSortProperty.unreadCount: { FoldersSortProperty.unreadCount: {
(FoldersSortProperty.alphabetical, SortBoxOrder.ascending), (property: FoldersSortProperty.alphabetical, order: SortBoxOrder.ascending),
}, },
}, },
); );
typedef FolderFeedsWrapper = (news.Folder folder, int feedCount, int unreadCount); typedef FolderFeedsWrapper = ({news.Folder folder, int feedCount, int unreadCount});

4
packages/neon/neon_news/lib/widgets/folders_view.dart

@ -23,7 +23,7 @@ class NewsFoldersView extends StatelessWidget {
final feedCount = feedsInFolder.length; final feedCount = feedsInFolder.length;
final unreadCount = feedsInFolder.fold(0, (final a, final b) => a + b.unreadCount!); final unreadCount = feedsInFolder.fold(0, (final a, final b) => a + b.unreadCount!);
return (folder, feedCount, unreadCount); return (folder: folder, feedCount: feedCount, unreadCount: unreadCount);
}).toList() }).toList()
: null, : null,
builder: (final context, final sorted) => NeonListView( builder: (final context, final sorted) => NeonListView(
@ -45,7 +45,7 @@ class NewsFoldersView extends StatelessWidget {
final BuildContext context, final BuildContext context,
final FolderFeedsWrapper folderFeedsWrapper, final FolderFeedsWrapper folderFeedsWrapper,
) { ) {
final (folder, feedCount, unreadCount) = folderFeedsWrapper; final (folder: folder, feedCount: feedCount, unreadCount: unreadCount) = folderFeedsWrapper;
return ListTile( return ListTile(
title: Text( title: Text(
folder.name, folder.name,

6
packages/neon/neon_notes/lib/neon_notes.dart

@ -71,11 +71,11 @@ class NotesApp extends AppImplementation<NotesBloc, NotesAppSpecificOptions> {
final RouteBase route = $notesAppRoute; final RouteBase route = $notesAppRoute;
@override @override
(bool? supported, String? minimumVersion) isSupported( VersionSupported<String> isSupported(
final Account account, final Account account,
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities, final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) { ) {
final (supported, minimumVersion) = account.client.notes.isSupported(capabilities); final result = account.client.notes.isSupported(capabilities);
return (supported, minimumVersion.toString()); return (isSupported: result.isSupported, minimumVersion: result.minimumVersion.toString());
} }
} }

2
packages/neon/neon_notes/lib/sort/categories.dart

@ -7,7 +7,7 @@ final categoriesSortBox = SortBox<CategoriesSortProperty, NoteCategory>(
}, },
{ {
CategoriesSortProperty.notesCount: { CategoriesSortProperty.notesCount: {
(CategoriesSortProperty.alphabetical, SortBoxOrder.ascending), (property: CategoriesSortProperty.alphabetical, order: SortBoxOrder.ascending),
}, },
}, },
); );

4
packages/neon/neon_notes/lib/sort/notes.dart

@ -8,10 +8,10 @@ final notesSortBox = SortBox<NotesSortProperty, notes.Note>(
}, },
{ {
NotesSortProperty.alphabetical: { NotesSortProperty.alphabetical: {
(NotesSortProperty.lastModified, SortBoxOrder.descending), (property: NotesSortProperty.lastModified, order: SortBoxOrder.descending),
}, },
NotesSortProperty.lastModified: { NotesSortProperty.lastModified: {
(NotesSortProperty.alphabetical, SortBoxOrder.ascending), (property: NotesSortProperty.alphabetical, order: SortBoxOrder.ascending),
}, },
}, },
); );

2
packages/neon/neon_notes/lib/widgets/notes_view.dart

@ -16,7 +16,7 @@ class NotesView extends StatelessWidget {
builder: (final context, final notesList) => SortBoxBuilder<NotesSortProperty, notes.Note>( builder: (final context, final notesList) => SortBoxBuilder<NotesSortProperty, notes.Note>(
sortBox: notesSortBox, sortBox: notesSortBox,
presort: const { presort: const {
(NotesSortProperty.favorite, SortBoxOrder.ascending), (property: NotesSortProperty.favorite, order: SortBoxOrder.ascending),
}, },
sortProperty: bloc.options.notesSortPropertyOption, sortProperty: bloc.options.notesSortPropertyOption,
sortBoxOrder: bloc.options.notesSortBoxOrderOption, sortBoxOrder: bloc.options.notesSortBoxOrderOption,

8
packages/neon/neon_notifications/lib/neon_notifications.dart

@ -13,7 +13,6 @@ import 'package:neon/utils.dart';
import 'package:neon/widgets.dart'; import 'package:neon/widgets.dart';
import 'package:neon_notifications/l10n/localizations.dart'; import 'package:neon_notifications/l10n/localizations.dart';
import 'package:neon_notifications/routes.dart'; import 'package:neon_notifications/routes.dart';
import 'package:nextcloud/core.dart' as core;
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud/notifications.dart' as notifications; import 'package:nextcloud/notifications.dart' as notifications;
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
@ -54,11 +53,4 @@ class NotificationsApp extends AppImplementation<NotificationsBloc, Notification
@override @override
BehaviorSubject<int> getUnreadCounter(final NotificationsBloc bloc) => bloc.unreadCounter; BehaviorSubject<int> getUnreadCounter(final NotificationsBloc bloc) => bloc.unreadCounter;
@override
(bool? supported, String? minimumVersion) isSupported(
final Account account,
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) =>
const (null, null);
} }

1
packages/nextcloud/lib/nextcloud.dart

@ -4,4 +4,5 @@ export 'package:dynamite_runtime/models.dart';
export 'ids.dart'; export 'ids.dart';
export 'src/client.dart'; export 'src/client.dart';
export 'src/helpers/common.dart';
export 'webdav.dart'; export 'webdav.dart';

2
packages/nextcloud/lib/src/helpers/common.dart

@ -0,0 +1,2 @@
/// The result of a version check.
typedef VersionSupported<T> = ({bool isSupported, T minimumVersion});

10
packages/nextcloud/lib/src/helpers/core.dart

@ -1,6 +1,7 @@
// ignore_for_file: public_member_api_docs // ignore_for_file: public_member_api_docs
import 'package:nextcloud/src/api/core.openapi.dart' as core; import 'package:nextcloud/src/api/core.openapi.dart' as core;
import 'package:nextcloud/src/helpers/common.dart';
/// Version of core/Server supported /// Version of core/Server supported
const supportedVersion = 27; const supportedVersion = 27;
@ -9,9 +10,12 @@ extension CoreVersionSupported on core.Client {
/// Check if the core/Server version is supported by this client /// Check if the core/Server version is supported by this client
/// ///
/// Also returns the supported version number /// Also returns the supported version number
(bool, int) isSupported(final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities) => ( VersionSupported<int> isSupported(
capabilities.version.major == supportedVersion, final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
supportedVersion, ) =>
(
isSupported: capabilities.version.major == supportedVersion,
minimumVersion: supportedVersion,
); );
} }

7
packages/nextcloud/lib/src/helpers/news.dart

@ -1,6 +1,7 @@
// ignore_for_file: public_member_api_docs // ignore_for_file: public_member_api_docs
import 'package:nextcloud/src/api/news.openapi.dart' as news; import 'package:nextcloud/src/api/news.openapi.dart' as news;
import 'package:nextcloud/src/helpers/common.dart';
/// API version of the news app supported /// API version of the news app supported
const supportedVersion = 'v1-3'; const supportedVersion = 'v1-3';
@ -9,11 +10,11 @@ extension NewsVersionSupported on news.Client {
/// Check if the news app version is supported by this client /// Check if the news app version is supported by this client
/// ///
/// Also returns the supported API version number /// Also returns the supported API version number
Future<(bool, String)> isSupported() async { Future<VersionSupported<String>> isSupported() async {
final response = await getSupportedApiVersions(); final response = await getSupportedApiVersions();
return ( return (
response.body.apiLevels!.contains(supportedVersion), isSupported: response.body.apiLevels!.contains(supportedVersion),
supportedVersion, minimumVersion: supportedVersion,
); );
} }
} }

10
packages/nextcloud/lib/src/helpers/notes.dart

@ -1,6 +1,7 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:nextcloud/src/api/core.openapi.dart' as core; import 'package:nextcloud/src/api/core.openapi.dart' as core;
import 'package:nextcloud/src/api/notes.openapi.dart' as notes; import 'package:nextcloud/src/api/notes.openapi.dart' as notes;
import 'package:nextcloud/src/helpers/common.dart';
import 'package:version/version.dart'; import 'package:version/version.dart';
/// API version of the notes app supported /// API version of the notes app supported
@ -11,11 +12,14 @@ extension NotesVersionSupported on notes.Client {
/// Check if the notes app version is supported by this client /// Check if the notes app version is supported by this client
/// ///
/// Also returns the supported API version number /// Also returns the supported API version number
(bool, int) isSupported(final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities) => ( VersionSupported<int> isSupported(
capabilities.capabilities.notesCapabilities?.notes.apiVersion final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) =>
(
isSupported: capabilities.capabilities.notesCapabilities?.notes.apiVersion
?.map(Version.parse) ?.map(Version.parse)
.firstWhereOrNull((final version) => version.major == supportedVersion) != .firstWhereOrNull((final version) => version.major == supportedVersion) !=
null, null,
supportedVersion, minimumVersion: supportedVersion,
); );
} }

4
packages/nextcloud/test/core_test.dart

@ -25,8 +25,8 @@ void main() {
expect(response.statusCode, 200); expect(response.statusCode, 200);
expect(() => response.headers, isA<void>()); expect(() => response.headers, isA<void>());
final (supported, _) = client.core.isSupported(response.body.ocs.data); final result = client.core.isSupported(response.body.ocs.data);
expect(supported, isTrue); expect(result.isSupported, isTrue);
}); });
test('Is supported from status', () async { test('Is supported from status', () async {

4
packages/nextcloud/test/news_test.dart

@ -38,8 +38,8 @@ void main() {
); );
test('Is supported', () async { test('Is supported', () async {
final (supported, _) = await client.news.isSupported(); final result = await client.news.isSupported();
expect(supported, isTrue); expect(result.isSupported, isTrue);
}); });
test('Add feed', () async { test('Add feed', () async {

4
packages/nextcloud/test/notes_test.dart

@ -25,8 +25,8 @@ void main() {
expect(response.statusCode, 200); expect(response.statusCode, 200);
expect(() => response.headers, isA<void>()); expect(() => response.headers, isA<void>());
final (supported, _) = client.notes.isSupported(response.body.ocs.data); final result = client.notes.isSupported(response.body.ocs.data);
expect(supported, isTrue); expect(result.isSupported, isTrue);
}); });
test('Create note favorite', () async { test('Create note favorite', () async {

20
packages/sort_box/lib/sort_box.dart

@ -1,6 +1,11 @@
/// Signature of a function returning a [Comparable]. /// Signature of a function returning a [Comparable].
typedef ComparableGetter<T> = Comparable<Object> Function(T); 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. /// Sorting Box to sort [List]s on multiple properties.
class SortBox<T extends Enum, R> { class SortBox<T extends Enum, R> {
/// Constructs a new SortBox. /// Constructs a new SortBox.
@ -17,7 +22,7 @@ class SortBox<T extends Enum, R> {
/// A mapping of values [T] to their *Boxes*. /// A mapping of values [T] to their *Boxes*.
/// ///
/// The Boxes are applied if two elements are considered equal regarding their property [T]. /// The Boxes are applied if two elements are considered equal regarding their property [T].
final Map<T, Set<(T property, SortBoxOrder order)>> _boxes; final Map<T, Set<Box<T>>> _boxes;
/// Sorts the [input] list according to their [box]. /// Sorts the [input] list according to their [box].
/// ///
@ -28,8 +33,8 @@ class SortBox<T extends Enum, R> {
/// This function sorts the input in place and a reference to it mutating the provided list. /// This function sorts the input in place and a reference to it mutating the provided list.
List<R> sort( List<R> sort(
final List<R> input, final List<R> input,
final (T property, SortBoxOrder order) box, [ final Box<T> box, [
final Set<(T property, SortBoxOrder order)>? presort, final Set<Box<T>>? presort,
]) { ]) {
if (input.length <= 1) { if (input.length <= 1) {
return input; return input;
@ -38,7 +43,7 @@ class SortBox<T extends Enum, R> {
final boxes = { final boxes = {
...?presort, ...?presort,
box, box,
...?_boxes[box.$1], ...?_boxes[box.property],
}; };
final sorted = input..sort((final item1, final item2) => _compare(item1, item2, boxes.iterator..moveNext())); final sorted = input..sort((final item1, final item2) => _compare(item1, item2, boxes.iterator..moveNext()));
@ -49,16 +54,15 @@ class SortBox<T extends Enum, R> {
int _compare( int _compare(
final R item1, final R item1,
final R item2, final R item2,
final Iterator<(T property, SortBoxOrder order)> iterator, final Iterator<Box<T>> iterator,
) { ) {
final box = iterator.current; final box = iterator.current;
final (property, sortBoxOrder) = box; final comparableGetter = _properties[box.property]!;
final comparableGetter = _properties[property]!;
final comparable1 = comparableGetter(item1); final comparable1 = comparableGetter(item1);
final comparable2 = comparableGetter(item2); final comparable2 = comparableGetter(item2);
final order = switch (sortBoxOrder) { final order = switch (box.order) {
SortBoxOrder.ascending => comparable1.compareTo(comparable2), SortBoxOrder.ascending => comparable1.compareTo(comparable2),
SortBoxOrder.descending => comparable2.compareTo(comparable1), SortBoxOrder.descending => comparable2.compareTo(comparable1),
}; };

20
packages/sort_box/test/sort_box_test.dart

@ -31,14 +31,14 @@ void main() {
}, },
{ {
FruitSort.alphabetical: { FruitSort.alphabetical: {
(FruitSort.count, SortBoxOrder.ascending), (property: FruitSort.count, order: SortBoxOrder.ascending),
}, },
FruitSort.count: { FruitSort.count: {
(FruitSort.alphabetical, SortBoxOrder.ascending), (property: FruitSort.alphabetical, order: SortBoxOrder.ascending),
}, },
FruitSort.price: { FruitSort.price: {
(FruitSort.alphabetical, SortBoxOrder.descending), (property: FruitSort.alphabetical, order: SortBoxOrder.descending),
(FruitSort.count, SortBoxOrder.ascending), (property: FruitSort.count, order: SortBoxOrder.ascending),
}, },
}, },
); );
@ -52,7 +52,7 @@ void main() {
const Fruit('Banana', 4), const Fruit('Banana', 4),
const Fruit('Apple', 5), const Fruit('Apple', 5),
]; ];
final sorted = sortBox.sort(fruits, (FruitSort.alphabetical, SortBoxOrder.ascending)); final sorted = sortBox.sort(fruits, (property: FruitSort.alphabetical, order: SortBoxOrder.ascending));
for (var i = 0; i < 3; i++) { for (var i = 0; i < 3; i++) {
expect(sorted[i].name, 'Apple'); expect(sorted[i].name, 'Apple');
@ -70,7 +70,7 @@ void main() {
const Fruit('Banana', 2), const Fruit('Banana', 2),
const Fruit('Apple', 3), const Fruit('Apple', 3),
]; ];
final sorted = sortBox.sort(fruits, (FruitSort.count, SortBoxOrder.ascending)); final sorted = sortBox.sort(fruits, (property: FruitSort.count, order: SortBoxOrder.ascending));
final names = ['Apple', 'Banana', 'Apple', 'Apple', 'Banana']; final names = ['Apple', 'Banana', 'Apple', 'Apple', 'Banana'];
for (var i = 0; i < 5; i++) { for (var i = 0; i < 5; i++) {
@ -91,7 +91,7 @@ void main() {
const Fruit('Banana', 1), const Fruit('Banana', 1),
const Fruit('Apple', 2), const Fruit('Apple', 2),
]; ];
final sorted = sortBox.sort(fruits, (FruitSort.count, SortBoxOrder.ascending)); final sorted = sortBox.sort(fruits, (property: FruitSort.count, order: SortBoxOrder.ascending));
final names = ['Apple', 'Banana', 'Apple', 'Apple', 'Banana']; final names = ['Apple', 'Banana', 'Apple', 'Apple', 'Banana'];
for (var i = 0; i < 5; i++) { for (var i = 0; i < 5; i++) {
@ -112,7 +112,7 @@ void main() {
const Fruit('Banana', 2), const Fruit('Banana', 2),
const Fruit('Apple', 5), const Fruit('Apple', 5),
]; ];
final sorted = sortBox.sort(fruits, (FruitSort.alphabetical, SortBoxOrder.ascending)); final sorted = sortBox.sort(fruits, (property: FruitSort.alphabetical, order: SortBoxOrder.ascending));
for (var i = 0; i < 3; i++) { for (var i = 0; i < 3; i++) {
expect(sorted[i].name, 'Apple'); expect(sorted[i].name, 'Apple');
@ -134,7 +134,7 @@ void main() {
const Fruit('Elderberry', 1), const Fruit('Elderberry', 1),
const Fruit('Damson', 1), const Fruit('Damson', 1),
]; ];
final sorted = sortBox.sort(fruits, (FruitSort.count, SortBoxOrder.ascending)); final sorted = sortBox.sort(fruits, (property: FruitSort.count, order: SortBoxOrder.ascending));
final names = ['Apple', 'Banana', 'Coconut', 'Damson', 'Elderberry']; final names = ['Apple', 'Banana', 'Coconut', 'Damson', 'Elderberry'];
for (var i = 0; i < 5; i++) { for (var i = 0; i < 5; i++) {
@ -152,7 +152,7 @@ void main() {
const Fruit('Banana', 1, 3), const Fruit('Banana', 1, 3),
const Fruit('Apple', 2, 3), const Fruit('Apple', 2, 3),
]; ];
final sorted = sortBox.sort(fruits, (FruitSort.price, SortBoxOrder.ascending)); final sorted = sortBox.sort(fruits, (property: FruitSort.price, order: SortBoxOrder.ascending));
final price = [0, 2, 3, 3, 3]; final price = [0, 2, 3, 3, 3];
for (var i = 0; i < 5; i++) { for (var i = 0; i < 5; i++) {

Loading…
Cancel
Save