search: added recent filters

This commit is contained in:
Thibault Deckers 2020-11-01 10:22:16 +09:00
parent 2dcb2b6c7b
commit 206e30de30
13 changed files with 87 additions and 7 deletions

View file

@ -87,6 +87,6 @@ class AlbumFilter extends CollectionFilter {
@override
String toString() {
return 'AlbumFilter{album=$album}';
return '$runtimeType#${shortHash(this)}{album=$album}';
}
}

View file

@ -1,6 +1,7 @@
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/common/icons.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class LocationFilter extends CollectionFilter {
@ -45,7 +46,7 @@ class LocationFilter extends CollectionFilter {
// as of Flutter v1.22.0-12.1.pre emoji shadows are rendered as colorful duplicates,
// not filled with the shadow color as expected, so we remove them
if (flag != null) return Text(flag, style: TextStyle(fontSize: size, shadows: []));
return Icon(AIcons.location, size: size);
return Icon(_location.isEmpty ? AIcons.locationOff : AIcons.location, size: size);
}
@override
@ -62,7 +63,7 @@ class LocationFilter extends CollectionFilter {
@override
String toString() {
return 'LocationFilter{level=$level, location=$_location}';
return '$runtimeType#${shortHash(this)}{level=$level, location=$_location}';
}
// U+0041 Latin Capital letter A

View file

@ -1,6 +1,7 @@
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/common/icons.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class MimeFilter extends CollectionFilter {
@ -81,4 +82,9 @@ class MimeFilter extends CollectionFilter {
@override
int get hashCode => hashValues(type, mime);
@override
String toString() {
return '$runtimeType#${shortHash(this)}{mime=$mime}';
}
}

View file

@ -69,4 +69,9 @@ class QueryFilter extends CollectionFilter {
@override
int get hashCode => hashValues(type, query);
@override
String toString() {
return '$runtimeType#${shortHash(this)}{query=$query}';
}
}

View file

@ -1,6 +1,7 @@
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/common/icons.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class TagFilter extends CollectionFilter {
@ -32,7 +33,7 @@ class TagFilter extends CollectionFilter {
String get label => tag.isEmpty ? emptyLabel : tag;
@override
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true, bool embossed = false}) => showGenericIcon ? Icon(AIcons.tag, size: size) : null;
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true, bool embossed = false}) => showGenericIcon ? Icon(tag.isEmpty ? AIcons.tagOff : AIcons.tag, size: size) : null;
@override
String get typeKey => type;
@ -48,6 +49,6 @@ class TagFilter extends CollectionFilter {
@override
String toString() {
return 'TagFilter{tag=$tag}';
return '$runtimeType#${shortHash(this)}{tag=$tag}';
}
}

View file

@ -55,6 +55,10 @@ class Settings extends ChangeNotifier {
// rendering
static const svgBackgroundKey = 'svg_background';
// search
static const saveSearchHistoryKey = 'save_search_history';
static const searchHistoryKey = 'search_history';
Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
}
@ -179,6 +183,16 @@ class Settings extends ChangeNotifier {
set svgBackground(int newValue) => setAndNotify(svgBackgroundKey, newValue);
// search
bool get saveSearchHistory => getBoolOrDefault(saveSearchHistoryKey, true);
set saveSearchHistory(bool newValue) => setAndNotify(saveSearchHistoryKey, newValue);
List<CollectionFilter> get searchHistory => (_prefs.getStringList(searchHistoryKey) ?? []).map(CollectionFilter.fromJson).toList();
set searchHistory(List<CollectionFilter> newValue) => setAndNotify(searchHistoryKey, newValue.map((filter) => filter.toJson()).toList());
// utils
// `RoutePredicate`

View file

@ -160,6 +160,12 @@ class Constants {
licenseUrl: 'https://github.com/MikeMitterer/dart-latlong/blob/master/LICENSE',
sourceUrl: 'https://github.com/MikeMitterer/dart-latlong',
),
Dependency(
name: 'Material Design Icons Flutter',
license: 'MIT',
licenseUrl: 'https://github.com/ziofat/material_design_icons_flutter/blob/master/LICENSE',
sourceUrl: 'https://github.com/ziofat/material_design_icons_flutter',
),
Dependency(
name: 'Overlay Support',
license: 'Apache 2.0',

View file

@ -316,6 +316,7 @@ class AppDebugPageState extends State<AppDebugPage> {
'collectionTileExtent': '${settings.collectionTileExtent}',
'infoMapZoom': '${settings.infoMapZoom}',
'pinnedFilters': '${settings.pinnedFilters}',
'searchHistory': '${settings.searchHistory}',
}),
],
);

View file

@ -6,6 +6,7 @@ import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/common/image_providers/app_icon_image_provider.dart';
import 'package:decorated_icon/decorated_icon.dart';
import 'package:flutter/material.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
class AIcons {
static const IconData allCollection = Icons.collections_outlined;
@ -20,15 +21,17 @@ class AIcons {
static const IconData disc = Icons.fiber_manual_record;
static const IconData error = Icons.error_outline;
static const IconData location = Icons.place_outlined;
static const IconData locationOff = Icons.location_off_outlined;
static const IconData raw = Icons.camera_outlined;
static const IconData shooting = Icons.camera_outlined;
static const IconData removableStorage = Icons.sd_storage_outlined;
static const IconData settings = Icons.settings_outlined;
static const IconData text = Icons.format_quote_outlined;
static const IconData tag = Icons.local_offer_outlined;
static const IconData tagOff = MdiIcons.tagOffOutline;
// actions
static const IconData addShortcut = Icons.bookmark_border;
static const IconData addShortcut = Icons.add_to_home_screen_outlined;
static const IconData clear = Icons.clear_outlined;
static const IconData collapse = Icons.expand_less_outlined;
static const IconData createAlbum = Icons.add_circle_outline;

View file

@ -25,6 +25,8 @@ class ImageSearchDelegate {
final ValueNotifier<String> expandedSectionNotifier = ValueNotifier(null);
final CollectionLens parentCollection;
static const searchHistoryCount = 10;
ImageSearchDelegate({@required this.source, this.parentCollection});
ThemeData appBarTheme(BuildContext context) {
@ -67,7 +69,8 @@ class ImageSearchDelegate {
child: ValueListenableBuilder<String>(
valueListenable: expandedSectionNotifier,
builder: (context, expandedSection, child) {
var queryFilter = _buildQueryFilter(false);
final queryFilter = _buildQueryFilter(false);
final history = settings.searchHistory;
return ListView(
padding: EdgeInsets.only(top: 8),
children: [
@ -85,6 +88,12 @@ class ImageSearchDelegate {
// but we also need to animate the query chip when it is selected by submitting the search query
heroTypeBuilder: (filter) => filter == queryFilter ? HeroType.always : HeroType.onTap,
),
if (upQuery.isEmpty && history.isNotEmpty)
_buildFilterRow(
context: context,
title: 'Recent',
filters: history,
),
StreamBuilder(
stream: source.eventBus.on<AlbumsChangedEvent>(),
builder: (context, snapshot) {
@ -170,6 +179,12 @@ class ImageSearchDelegate {
}
void _select(BuildContext context, CollectionFilter filter) {
if (settings.saveSearchHistory) {
final history = settings.searchHistory
..remove(filter)
..insert(0, filter);
settings.searchHistory = history.take(searchHistoryCount).toList();
}
if (parentCollection != null) {
_applyToParentCollectionPage(context, filter);
} else {

View file

@ -40,6 +40,7 @@ class _SettingsPageState extends State<SettingsPage> {
_buildDisplaySection(context),
_buildThumbnailsSection(context),
_buildViewerSection(context),
_buildSearchSection(context),
_buildPrivacySection(context),
],
),
@ -171,6 +172,25 @@ class _SettingsPageState extends State<SettingsPage> {
);
}
Widget _buildSearchSection(BuildContext context) {
return AvesExpansionTile(
title: 'Search',
expandedNotifier: _expandedNotifier,
children: [
SwitchListTile(
value: settings.saveSearchHistory,
onChanged: (v) {
settings.saveSearchHistory = v;
if (!v) {
settings.searchHistory = [];
}
},
title: Text('Save search history'),
),
],
);
}
Widget _buildPrivacySection(BuildContext context) {
return AvesExpansionTile(
title: 'Privacy',

View file

@ -480,6 +480,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10-nullsafety.1"
material_design_icons_flutter:
dependency: "direct main"
description:
name: material_design_icons_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.5755"
meta:
dependency: transitive
description:

View file

@ -65,6 +65,7 @@ dependencies:
google_maps_flutter:
intl:
latlong: # for flutter_map
material_design_icons_flutter:
overlay_support:
package_info:
palette_generator: