search: added recent filters
This commit is contained in:
parent
2dcb2b6c7b
commit
206e30de30
13 changed files with 87 additions and 7 deletions
|
@ -87,6 +87,6 @@ class AlbumFilter extends CollectionFilter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AlbumFilter{album=$album}';
|
return '$runtimeType#${shortHash(this)}{album=$album}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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/widgets/common/icons.dart';
|
import 'package:aves/widgets/common/icons.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class LocationFilter extends CollectionFilter {
|
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,
|
// 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
|
// not filled with the shadow color as expected, so we remove them
|
||||||
if (flag != null) return Text(flag, style: TextStyle(fontSize: size, shadows: []));
|
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
|
@override
|
||||||
|
@ -62,7 +63,7 @@ class LocationFilter extends CollectionFilter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'LocationFilter{level=$level, location=$_location}';
|
return '$runtimeType#${shortHash(this)}{level=$level, location=$_location}';
|
||||||
}
|
}
|
||||||
|
|
||||||
// U+0041 Latin Capital letter A
|
// U+0041 Latin Capital letter A
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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/widgets/common/icons.dart';
|
import 'package:aves/widgets/common/icons.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class MimeFilter extends CollectionFilter {
|
class MimeFilter extends CollectionFilter {
|
||||||
|
@ -81,4 +82,9 @@ class MimeFilter extends CollectionFilter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => hashValues(type, mime);
|
int get hashCode => hashValues(type, mime);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return '$runtimeType#${shortHash(this)}{mime=$mime}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,4 +69,9 @@ class QueryFilter extends CollectionFilter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => hashValues(type, query);
|
int get hashCode => hashValues(type, query);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return '$runtimeType#${shortHash(this)}{query=$query}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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/widgets/common/icons.dart';
|
import 'package:aves/widgets/common/icons.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class TagFilter extends CollectionFilter {
|
class TagFilter extends CollectionFilter {
|
||||||
|
@ -32,7 +33,7 @@ class TagFilter extends CollectionFilter {
|
||||||
String get label => tag.isEmpty ? emptyLabel : tag;
|
String get label => tag.isEmpty ? emptyLabel : tag;
|
||||||
|
|
||||||
@override
|
@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
|
@override
|
||||||
String get typeKey => type;
|
String get typeKey => type;
|
||||||
|
@ -48,6 +49,6 @@ class TagFilter extends CollectionFilter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'TagFilter{tag=$tag}';
|
return '$runtimeType#${shortHash(this)}{tag=$tag}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,10 @@ class Settings extends ChangeNotifier {
|
||||||
// rendering
|
// rendering
|
||||||
static const svgBackgroundKey = 'svg_background';
|
static const svgBackgroundKey = 'svg_background';
|
||||||
|
|
||||||
|
// search
|
||||||
|
static const saveSearchHistoryKey = 'save_search_history';
|
||||||
|
static const searchHistoryKey = 'search_history';
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
_prefs = await SharedPreferences.getInstance();
|
_prefs = await SharedPreferences.getInstance();
|
||||||
}
|
}
|
||||||
|
@ -179,6 +183,16 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
set svgBackground(int newValue) => setAndNotify(svgBackgroundKey, newValue);
|
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
|
// utils
|
||||||
|
|
||||||
// `RoutePredicate`
|
// `RoutePredicate`
|
||||||
|
|
|
@ -160,6 +160,12 @@ class Constants {
|
||||||
licenseUrl: 'https://github.com/MikeMitterer/dart-latlong/blob/master/LICENSE',
|
licenseUrl: 'https://github.com/MikeMitterer/dart-latlong/blob/master/LICENSE',
|
||||||
sourceUrl: 'https://github.com/MikeMitterer/dart-latlong',
|
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(
|
Dependency(
|
||||||
name: 'Overlay Support',
|
name: 'Overlay Support',
|
||||||
license: 'Apache 2.0',
|
license: 'Apache 2.0',
|
||||||
|
|
|
@ -316,6 +316,7 @@ class AppDebugPageState extends State<AppDebugPage> {
|
||||||
'collectionTileExtent': '${settings.collectionTileExtent}',
|
'collectionTileExtent': '${settings.collectionTileExtent}',
|
||||||
'infoMapZoom': '${settings.infoMapZoom}',
|
'infoMapZoom': '${settings.infoMapZoom}',
|
||||||
'pinnedFilters': '${settings.pinnedFilters}',
|
'pinnedFilters': '${settings.pinnedFilters}',
|
||||||
|
'searchHistory': '${settings.searchHistory}',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:aves/utils/constants.dart';
|
||||||
import 'package:aves/widgets/common/image_providers/app_icon_image_provider.dart';
|
import 'package:aves/widgets/common/image_providers/app_icon_image_provider.dart';
|
||||||
import 'package:decorated_icon/decorated_icon.dart';
|
import 'package:decorated_icon/decorated_icon.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||||
|
|
||||||
class AIcons {
|
class AIcons {
|
||||||
static const IconData allCollection = Icons.collections_outlined;
|
static const IconData allCollection = Icons.collections_outlined;
|
||||||
|
@ -20,15 +21,17 @@ class AIcons {
|
||||||
static const IconData disc = Icons.fiber_manual_record;
|
static const IconData disc = Icons.fiber_manual_record;
|
||||||
static const IconData error = Icons.error_outline;
|
static const IconData error = Icons.error_outline;
|
||||||
static const IconData location = Icons.place_outlined;
|
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 raw = Icons.camera_outlined;
|
||||||
static const IconData shooting = Icons.camera_outlined;
|
static const IconData shooting = Icons.camera_outlined;
|
||||||
static const IconData removableStorage = Icons.sd_storage_outlined;
|
static const IconData removableStorage = Icons.sd_storage_outlined;
|
||||||
static const IconData settings = Icons.settings_outlined;
|
static const IconData settings = Icons.settings_outlined;
|
||||||
static const IconData text = Icons.format_quote_outlined;
|
static const IconData text = Icons.format_quote_outlined;
|
||||||
static const IconData tag = Icons.local_offer_outlined;
|
static const IconData tag = Icons.local_offer_outlined;
|
||||||
|
static const IconData tagOff = MdiIcons.tagOffOutline;
|
||||||
|
|
||||||
// actions
|
// 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 clear = Icons.clear_outlined;
|
||||||
static const IconData collapse = Icons.expand_less_outlined;
|
static const IconData collapse = Icons.expand_less_outlined;
|
||||||
static const IconData createAlbum = Icons.add_circle_outline;
|
static const IconData createAlbum = Icons.add_circle_outline;
|
||||||
|
|
|
@ -25,6 +25,8 @@ class ImageSearchDelegate {
|
||||||
final ValueNotifier<String> expandedSectionNotifier = ValueNotifier(null);
|
final ValueNotifier<String> expandedSectionNotifier = ValueNotifier(null);
|
||||||
final CollectionLens parentCollection;
|
final CollectionLens parentCollection;
|
||||||
|
|
||||||
|
static const searchHistoryCount = 10;
|
||||||
|
|
||||||
ImageSearchDelegate({@required this.source, this.parentCollection});
|
ImageSearchDelegate({@required this.source, this.parentCollection});
|
||||||
|
|
||||||
ThemeData appBarTheme(BuildContext context) {
|
ThemeData appBarTheme(BuildContext context) {
|
||||||
|
@ -67,7 +69,8 @@ class ImageSearchDelegate {
|
||||||
child: ValueListenableBuilder<String>(
|
child: ValueListenableBuilder<String>(
|
||||||
valueListenable: expandedSectionNotifier,
|
valueListenable: expandedSectionNotifier,
|
||||||
builder: (context, expandedSection, child) {
|
builder: (context, expandedSection, child) {
|
||||||
var queryFilter = _buildQueryFilter(false);
|
final queryFilter = _buildQueryFilter(false);
|
||||||
|
final history = settings.searchHistory;
|
||||||
return ListView(
|
return ListView(
|
||||||
padding: EdgeInsets.only(top: 8),
|
padding: EdgeInsets.only(top: 8),
|
||||||
children: [
|
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
|
// 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,
|
heroTypeBuilder: (filter) => filter == queryFilter ? HeroType.always : HeroType.onTap,
|
||||||
),
|
),
|
||||||
|
if (upQuery.isEmpty && history.isNotEmpty)
|
||||||
|
_buildFilterRow(
|
||||||
|
context: context,
|
||||||
|
title: 'Recent',
|
||||||
|
filters: history,
|
||||||
|
),
|
||||||
StreamBuilder(
|
StreamBuilder(
|
||||||
stream: source.eventBus.on<AlbumsChangedEvent>(),
|
stream: source.eventBus.on<AlbumsChangedEvent>(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
@ -170,6 +179,12 @@ class ImageSearchDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _select(BuildContext context, CollectionFilter filter) {
|
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) {
|
if (parentCollection != null) {
|
||||||
_applyToParentCollectionPage(context, filter);
|
_applyToParentCollectionPage(context, filter);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -40,6 +40,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
_buildDisplaySection(context),
|
_buildDisplaySection(context),
|
||||||
_buildThumbnailsSection(context),
|
_buildThumbnailsSection(context),
|
||||||
_buildViewerSection(context),
|
_buildViewerSection(context),
|
||||||
|
_buildSearchSection(context),
|
||||||
_buildPrivacySection(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) {
|
Widget _buildPrivacySection(BuildContext context) {
|
||||||
return AvesExpansionTile(
|
return AvesExpansionTile(
|
||||||
title: 'Privacy',
|
title: 'Privacy',
|
||||||
|
|
|
@ -480,6 +480,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.10-nullsafety.1"
|
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:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -65,6 +65,7 @@ dependencies:
|
||||||
google_maps_flutter:
|
google_maps_flutter:
|
||||||
intl:
|
intl:
|
||||||
latlong: # for flutter_map
|
latlong: # for flutter_map
|
||||||
|
material_design_icons_flutter:
|
||||||
overlay_support:
|
overlay_support:
|
||||||
package_info:
|
package_info:
|
||||||
palette_generator:
|
palette_generator:
|
||||||
|
|
Loading…
Reference in a new issue