diff --git a/lib/model/filters/query.dart b/lib/model/filters/query.dart index 376e07b4f..8d73336fd 100644 --- a/lib/model/filters/query.dart +++ b/lib/model/filters/query.dart @@ -2,6 +2,7 @@ import 'package:aves/model/entry.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; +import 'package:aves/utils/file_utils.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -20,30 +21,25 @@ class QueryFilter extends CollectionFilter { List get props => [query, live]; static final _fieldPattern = RegExp(r'(.+)([=<>])(.+)'); + static final _fileSizePattern = RegExp(r'(\d+)([KMG])?'); static const keyContentId = 'ID'; + static const keyContentYear = 'YEAR'; + static const keyContentMonth = 'MONTH'; + static const keyContentDay = 'DAY'; + static const keyContentWidth = 'WIDTH'; + static const keyContentHeight = 'HEIGHT'; + static const keyContentSize = 'SIZE'; static const opEqual = '='; + static const opLower = '<'; + static const opGreater = '>'; QueryFilter(this.query, {this.colorful = true, this.live = false}) { var upQuery = query.toUpperCase(); - final match = _fieldPattern.firstMatch(upQuery); - if (match != null) { - final key = match.group(1)?.trim(); - final op = match.group(2)?.trim(); - final value = match.group(3)?.trim(); - if (key != null && op != null && value != null) { - switch (key) { - case keyContentId: - if (op == opEqual) { - final contentId = int.tryParse(value); - if (contentId != null) { - _test = (entry) => entry.contentId == contentId; - return; - } - } - break; - } - } + final test = fieldTest(upQuery); + if (test != null) { + _test = test; + return; } // allow NOT queries starting with `-` @@ -101,4 +97,114 @@ class QueryFilter extends CollectionFilter { @override String get key => '$type-$query'; + + EntryFilter? fieldTest(String upQuery) { + var match = _fieldPattern.firstMatch(upQuery); + if (match == null) return null; + + final key = match.group(1)?.trim(); + final op = match.group(2)?.trim(); + var valueString = match.group(3)?.trim(); + if (key == null || op == null || valueString == null) return null; + + final valueInt = int.tryParse(valueString); + + switch (key) { + case keyContentId: + if (valueInt == null) return null; + if (op == opEqual) { + return (entry) => entry.contentId == valueInt; + } + break; + case keyContentYear: + if (valueInt == null) return null; + switch (op) { + case opEqual: + return (entry) => (entry.bestDate?.year ?? 0) == valueInt; + case opLower: + return (entry) => (entry.bestDate?.year ?? 0) < valueInt; + case opGreater: + return (entry) => (entry.bestDate?.year ?? 0) > valueInt; + } + break; + case keyContentMonth: + if (valueInt == null) return null; + switch (op) { + case opEqual: + return (entry) => (entry.bestDate?.month ?? 0) == valueInt; + case opLower: + return (entry) => (entry.bestDate?.month ?? 0) < valueInt; + case opGreater: + return (entry) => (entry.bestDate?.month ?? 0) > valueInt; + } + break; + case keyContentDay: + if (valueInt == null) return null; + switch (op) { + case opEqual: + return (entry) => (entry.bestDate?.day ?? 0) == valueInt; + case opLower: + return (entry) => (entry.bestDate?.day ?? 0) < valueInt; + case opGreater: + return (entry) => (entry.bestDate?.day ?? 0) > valueInt; + } + break; + case keyContentWidth: + if (valueInt == null) return null; + switch (op) { + case opEqual: + return (entry) => entry.displaySize.width == valueInt; + case opLower: + return (entry) => entry.displaySize.width < valueInt; + case opGreater: + return (entry) => entry.displaySize.width > valueInt; + } + break; + case keyContentHeight: + if (valueInt == null) return null; + switch (op) { + case opEqual: + return (entry) => entry.displaySize.height == valueInt; + case opLower: + return (entry) => entry.displaySize.height < valueInt; + case opGreater: + return (entry) => entry.displaySize.height > valueInt; + } + break; + case keyContentSize: + match = _fileSizePattern.firstMatch(valueString); + if (match == null) return null; + + valueString = match.group(1)?.trim(); + if (valueString == null) return null; + final valueInt = int.tryParse(valueString); + if (valueInt == null) return null; + + var bytes = valueInt; + final multiplierString = match.group(2)?.trim(); + switch (multiplierString) { + case 'K': + bytes *= kilo; + break; + case 'M': + bytes *= mega; + break; + case 'G': + bytes *= giga; + break; + } + + switch (op) { + case opEqual: + return (entry) => (entry.sizeBytes ?? 0) == bytes; + case opLower: + return (entry) => (entry.sizeBytes ?? 0) < bytes; + case opGreater: + return (entry) => (entry.sizeBytes ?? 0) > bytes; + } + break; + } + + return null; + } } diff --git a/lib/utils/file_utils.dart b/lib/utils/file_utils.dart index ca707920f..5ed77aaf5 100644 --- a/lib/utils/file_utils.dart +++ b/lib/utils/file_utils.dart @@ -1,16 +1,16 @@ import 'package:intl/intl.dart'; -const _kiloDivider = 1024; -const _megaDivider = _kiloDivider * _kiloDivider; -const _gigaDivider = _megaDivider * _kiloDivider; -const _teraDivider = _gigaDivider * _kiloDivider; +const kilo = 1024; +const mega = kilo * kilo; +const giga = mega * kilo; +const tera = giga * kilo; String formatFileSize(String locale, int size, {int round = 2}) { - if (size < _kiloDivider) return '$size B'; + if (size < kilo) return '$size B'; final formatter = NumberFormat('0${round > 0 ? '.${'0' * round}' : ''}', locale); - if (size < _megaDivider) return '${formatter.format(size / _kiloDivider)} KB'; - if (size < _gigaDivider) return '${formatter.format(size / _megaDivider)} MB'; - if (size < _teraDivider) return '${formatter.format(size / _gigaDivider)} GB'; - return '${formatter.format(size / _teraDivider)} TB'; + if (size < mega) return '${formatter.format(size / kilo)} KB'; + if (size < giga) return '${formatter.format(size / mega)} MB'; + if (size < tera) return '${formatter.format(size / giga)} GB'; + return '${formatter.format(size / tera)} TB'; }