stats: tap on donut legend to filter by mime type
This commit is contained in:
parent
789f195306
commit
c2022a6ee6
2 changed files with 56 additions and 39 deletions
|
@ -1,6 +1,5 @@
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/model/image_entry.dart';
|
import 'package:aves/model/image_entry.dart';
|
||||||
import 'package:aves/model/mime_types.dart';
|
|
||||||
import 'package:aves/widgets/common/icons.dart';
|
import 'package:aves/widgets/common/icons.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
@ -34,14 +33,15 @@ class MimeFilter extends CollectionFilter {
|
||||||
_label ??= lowMime.split('/')[0].toUpperCase();
|
_label ??= lowMime.split('/')[0].toUpperCase();
|
||||||
} else {
|
} else {
|
||||||
_filter = (entry) => entry.mimeType == lowMime;
|
_filter = (entry) => entry.mimeType == lowMime;
|
||||||
if (lowMime == MimeTypes.svg) {
|
_label = displayType(lowMime);
|
||||||
_label = 'SVG';
|
|
||||||
}
|
|
||||||
_label ??= lowMime.split('/')[1].toUpperCase();
|
|
||||||
}
|
}
|
||||||
_icon ??= AIcons.vector;
|
_icon ??= AIcons.vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String displayType(String mime) {
|
||||||
|
return mime.toUpperCase().replaceFirst(RegExp('.*/(X-)?'), '').replaceFirst('+XML', '').replaceFirst('VND.', '');
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool filter(ImageEntry entry) => _filter(entry);
|
bool filter(ImageEntry entry) => _filter(entry);
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,13 @@ import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/model/filters/location.dart';
|
import 'package:aves/model/filters/location.dart';
|
||||||
|
import 'package:aves/model/filters/mime.dart';
|
||||||
import 'package:aves/model/filters/tag.dart';
|
import 'package:aves/model/filters/tag.dart';
|
||||||
import 'package:aves/model/image_entry.dart';
|
import 'package:aves/model/image_entry.dart';
|
||||||
import 'package:aves/model/source/collection_lens.dart';
|
import 'package:aves/model/source/collection_lens.dart';
|
||||||
import 'package:aves/utils/color_utils.dart';
|
import 'package:aves/utils/color_utils.dart';
|
||||||
import 'package:aves/utils/constants.dart';
|
import 'package:aves/utils/constants.dart';
|
||||||
|
import 'package:aves/widgets/album/collection_page.dart';
|
||||||
import 'package:aves/widgets/album/empty.dart';
|
import 'package:aves/widgets/album/empty.dart';
|
||||||
import 'package:aves/widgets/common/data_providers/media_query_data_provider.dart';
|
import 'package:aves/widgets/common/data_providers/media_query_data_provider.dart';
|
||||||
import 'package:aves/widgets/common/icons.dart';
|
import 'package:aves/widgets/common/icons.dart';
|
||||||
|
@ -112,30 +114,25 @@ class StatsPage extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _cleanMime(String mime) {
|
|
||||||
mime = mime.toUpperCase().replaceFirst(RegExp('.*/(X-)?'), '').replaceFirst('+XML', '').replaceFirst('VND.', '');
|
|
||||||
return mime;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildMimeDonut(BuildContext context, String Function(num) label, Map<String, num> byMimeTypes) {
|
Widget _buildMimeDonut(BuildContext context, String Function(num) label, Map<String, num> byMimeTypes) {
|
||||||
if (byMimeTypes.isEmpty) return SizedBox.shrink();
|
if (byMimeTypes.isEmpty) return SizedBox.shrink();
|
||||||
|
|
||||||
final sum = byMimeTypes.values.fold<int>(0, (prev, v) => prev + v);
|
final sum = byMimeTypes.values.fold<int>(0, (prev, v) => prev + v);
|
||||||
|
|
||||||
final seriesData = byMimeTypes.entries.map((kv) => StringNumDatum(_cleanMime(kv.key), kv.value)).toList();
|
final seriesData = byMimeTypes.entries.map((kv) => EntryByMimeDatum(mimeType: kv.key, entryCount: kv.value)).toList();
|
||||||
seriesData.sort((kv1, kv2) {
|
seriesData.sort((d1, d2) {
|
||||||
final c = kv2.value.compareTo(kv1.value);
|
final c = d2.entryCount.compareTo(d1.entryCount);
|
||||||
return c != 0 ? c : compareAsciiUpperCase(kv1.key, kv2.key);
|
return c != 0 ? c : compareAsciiUpperCase(d1.displayText, d2.displayText);
|
||||||
});
|
});
|
||||||
|
|
||||||
final series = [
|
final series = [
|
||||||
charts.Series<StringNumDatum, String>(
|
charts.Series<EntryByMimeDatum, String>(
|
||||||
id: 'mime',
|
id: 'mime',
|
||||||
colorFn: (d, i) => charts.ColorUtil.fromDartColor(stringToColor(d.key)),
|
colorFn: (d, i) => charts.ColorUtil.fromDartColor(d.color),
|
||||||
domainFn: (d, i) => d.key,
|
domainFn: (d, i) => d.displayText,
|
||||||
measureFn: (d, i) => d.value,
|
measureFn: (d, i) => d.entryCount,
|
||||||
data: seriesData,
|
data: seriesData,
|
||||||
labelAccessorFn: (d, _) => '${d.key}: ${d.value}',
|
labelAccessorFn: (d, _) => '${d.displayText}: ${d.entryCount}',
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -171,23 +168,26 @@ class StatsPage extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: seriesData
|
children: seriesData
|
||||||
.map((kv) => Text.rich(
|
.map((d) => GestureDetector(
|
||||||
|
onTap: () => _goToCollection(context, MimeFilter(d.mimeType)),
|
||||||
|
child: Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
alignment: PlaceholderAlignment.middle,
|
alignment: PlaceholderAlignment.middle,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsetsDirectional.only(end: 8),
|
padding: EdgeInsetsDirectional.only(end: 8),
|
||||||
child: Icon(AIcons.disc, color: stringToColor(kv.key)),
|
child: Icon(AIcons.disc, color: d.color),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextSpan(text: '${kv.key} '),
|
TextSpan(text: '${d.displayText} '),
|
||||||
TextSpan(text: '${kv.value}', style: TextStyle(color: Colors.white70)),
|
TextSpan(text: '${d.entryCount}', style: TextStyle(color: Colors.white70)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
),
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
|
@ -230,16 +230,33 @@ class StatsPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _goToCollection(BuildContext context, CollectionFilter filter) {
|
||||||
|
if (collection == null) return;
|
||||||
|
Navigator.pushAndRemoveUntil(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => CollectionPage(collection.derive(filter)),
|
||||||
|
),
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StringNumDatum {
|
class EntryByMimeDatum {
|
||||||
final String key;
|
final String mimeType;
|
||||||
final num value;
|
final String displayText;
|
||||||
|
final int entryCount;
|
||||||
|
|
||||||
const StringNumDatum(this.key, this.value);
|
EntryByMimeDatum({
|
||||||
|
@required this.mimeType,
|
||||||
|
@required this.entryCount,
|
||||||
|
}) : displayText = MimeFilter.displayType(mimeType);
|
||||||
|
|
||||||
|
Color get color => stringToColor(displayText);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return '[$runtimeType#$hashCode: key=$key, value=$value]';
|
return '[$runtimeType#$hashCode: mimeType=$mimeType, displayText=$displayText, entryCount=$entryCount]';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue