Add inventory sorting functions
This commit is contained in:
parent
9f08e6ce62
commit
2d785d6cb4
4 changed files with 159 additions and 9 deletions
|
@ -1,24 +1,74 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:recon/apis/record_api.dart';
|
import 'package:recon/apis/record_api.dart';
|
||||||
import 'package:recon/clients/api_client.dart';
|
import 'package:recon/clients/api_client.dart';
|
||||||
import 'package:recon/models/inventory/resonite_directory.dart';
|
import 'package:recon/models/inventory/resonite_directory.dart';
|
||||||
import 'package:recon/models/records/record.dart';
|
import 'package:recon/models/records/record.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
enum SortMode {
|
||||||
|
name,
|
||||||
|
date;
|
||||||
|
|
||||||
|
int sortFunction(Record a, Record b, {bool reverse = false}) {
|
||||||
|
final func = switch (this) {
|
||||||
|
SortMode.name => (Record x, Record y) =>
|
||||||
|
x.formattedName.toString().toLowerCase().compareTo(y.formattedName.toString().toLowerCase()),
|
||||||
|
SortMode.date => (Record x, Record y) => x.creationTime.compareTo(y.creationTime),
|
||||||
|
};
|
||||||
|
if (reverse) {
|
||||||
|
return func(b, a);
|
||||||
|
}
|
||||||
|
return func(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Map<SortMode, IconData> _iconsMap = {
|
||||||
|
SortMode.name: Icons.sort_by_alpha,
|
||||||
|
SortMode.date: Icons.access_time_outlined
|
||||||
|
};
|
||||||
|
|
||||||
|
IconData get icon => _iconsMap[this] ?? Icons.question_mark;
|
||||||
|
}
|
||||||
|
|
||||||
class InventoryClient extends ChangeNotifier {
|
class InventoryClient extends ChangeNotifier {
|
||||||
final ApiClient apiClient;
|
final ApiClient apiClient;
|
||||||
|
final Map<String, Record> _selectedRecords = {};
|
||||||
|
|
||||||
Future<ResoniteDirectory>? _currentDirectory;
|
Future<ResoniteDirectory>? _currentDirectory;
|
||||||
|
SortMode _sortMode = SortMode.name;
|
||||||
Future<ResoniteDirectory>? get directoryFuture => _currentDirectory;
|
bool _sortReverse = false;
|
||||||
|
|
||||||
InventoryClient({required this.apiClient});
|
InventoryClient({required this.apiClient});
|
||||||
|
|
||||||
final Map<String, Record> _selectedRecords = {};
|
SortMode get sortMode => _sortMode;
|
||||||
|
|
||||||
|
bool get sortReverse => _sortReverse;
|
||||||
|
|
||||||
|
set sortMode(SortMode mode) {
|
||||||
|
if (_sortMode != mode) {
|
||||||
|
_sortMode = mode;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set sortReverse(bool reverse) {
|
||||||
|
if (_sortReverse != reverse) {
|
||||||
|
_sortReverse = reverse;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<Record> get selectedRecords => _selectedRecords.values.toList();
|
List<Record> get selectedRecords => _selectedRecords.values.toList();
|
||||||
|
|
||||||
|
Future<ResoniteDirectory>? get directoryFuture => _currentDirectory?.then(
|
||||||
|
(ResoniteDirectory value) {
|
||||||
|
value.children.sort(
|
||||||
|
(ResoniteDirectory a, ResoniteDirectory b) => _sortMode.sortFunction(a.record, b.record, reverse: _sortReverse),
|
||||||
|
);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
bool get isAnyRecordSelected => _selectedRecords.isNotEmpty;
|
bool get isAnyRecordSelected => _selectedRecords.isNotEmpty;
|
||||||
|
|
||||||
bool isRecordSelected(Record record) => _selectedRecords.containsKey(record.id);
|
bool isRecordSelected(Record record) => _selectedRecords.containsKey(record.id);
|
||||||
|
@ -142,7 +192,8 @@ class InventoryClient extends ChangeNotifier {
|
||||||
_currentDirectory = _getDirectory(record).then(
|
_currentDirectory = _getDirectory(record).then(
|
||||||
(records) {
|
(records) {
|
||||||
childDir.children.clear();
|
childDir.children.clear();
|
||||||
childDir.children.addAll(records.map((record) => ResoniteDirectory.fromRecord(record: record, parent: childDir)));
|
childDir.children
|
||||||
|
.addAll(records.map((record) => ResoniteDirectory.fromRecord(record: record, parent: childDir)));
|
||||||
return childDir;
|
return childDir;
|
||||||
},
|
},
|
||||||
).onError((error, stackTrace) {
|
).onError((error, stackTrace) {
|
||||||
|
|
|
@ -77,8 +77,9 @@ class _InventoryBrowserState extends State<InventoryBrowser> with AutomaticKeepA
|
||||||
}
|
}
|
||||||
final directory = snapshot.data;
|
final directory = snapshot.data;
|
||||||
final records = directory?.records ?? [];
|
final records = directory?.records ?? [];
|
||||||
|
records.sort(
|
||||||
records.sort((a, b) => a.name.compareTo(b.name));
|
(Record a, Record b) => iClient.sortMode.sortFunction(a, b, reverse: iClient.sortReverse),
|
||||||
|
);
|
||||||
final paths = records
|
final paths = records
|
||||||
.where((element) =>
|
.where((element) =>
|
||||||
element.recordType == RecordType.link || element.recordType == RecordType.directory)
|
element.recordType == RecordType.link || element.recordType == RecordType.directory)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:ui';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:recon/auxiliary.dart';
|
import 'package:recon/auxiliary.dart';
|
||||||
|
@ -50,7 +51,7 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<InventoryClient>(
|
return Consumer<InventoryClient>(
|
||||||
builder: (BuildContext context, InventoryClient iClient, Widget? child) {
|
builder: (BuildContext context, InventoryClient iClient, Widget? _) {
|
||||||
return AnimatedSwitcher(
|
return AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 350),
|
duration: const Duration(milliseconds: 350),
|
||||||
transitionBuilder: (child, animation) => FadeTransition(
|
transitionBuilder: (child, animation) => FadeTransition(
|
||||||
|
@ -61,6 +62,103 @@ class _InventoryBrowserAppBarState extends State<InventoryBrowserAppBar> {
|
||||||
? AppBar(
|
? AppBar(
|
||||||
key: const ValueKey("default-appbar"),
|
key: const ValueKey("default-appbar"),
|
||||||
title: const Text("Inventory"),
|
title: const Text("Inventory"),
|
||||||
|
actions: [
|
||||||
|
PopupMenuButton(
|
||||||
|
icon: const Icon(Icons.swap_vert),
|
||||||
|
onSelected: (value) {
|
||||||
|
iClient.sortReverse = value;
|
||||||
|
},
|
||||||
|
itemBuilder: (context) {
|
||||||
|
return [
|
||||||
|
PopupMenuItem(
|
||||||
|
value: false,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_upward,
|
||||||
|
color: iClient.sortReverse == false
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Ascending",
|
||||||
|
style: TextStyle(
|
||||||
|
color: iClient.sortReverse == false
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: true,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.arrow_downward,
|
||||||
|
color: iClient.sortReverse == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.onSurface),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Descending",
|
||||||
|
style: TextStyle(
|
||||||
|
color: iClient.sortReverse == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
];
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
|
child: PopupMenuButton(
|
||||||
|
icon: const Icon(Icons.sort),
|
||||||
|
onSelected: (value) {
|
||||||
|
iClient.sortMode = value;
|
||||||
|
},
|
||||||
|
itemBuilder: (context) {
|
||||||
|
return SortMode.values
|
||||||
|
.map(
|
||||||
|
(e) => PopupMenuItem(
|
||||||
|
value: e,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
e.icon,
|
||||||
|
color: iClient.sortMode == e
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
toBeginningOfSentenceCase(e.name) ?? e.name,
|
||||||
|
style: TextStyle(
|
||||||
|
color: iClient.sortMode == e
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
: AppBar(
|
: AppBar(
|
||||||
key: const ValueKey("selection-appbar"),
|
key: const ValueKey("selection-appbar"),
|
||||||
|
|
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.9.3-beta+1
|
version: 0.10.0-beta+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.1'
|
sdk: '>=3.0.1'
|
||||||
|
|
Loading…
Reference in a new issue