[mob][photos] Add 'isApplied' state to filters, change UI for applied filters
This commit is contained in:
@@ -11,6 +11,7 @@ abstract class HierarchicalSearchFilter {
|
||||
//in gallery is when the filter is the initial filter (top level) of the
|
||||
//gallery.
|
||||
final Set<int> matchedUploadedIDs;
|
||||
bool isApplied = false;
|
||||
|
||||
HierarchicalSearchFilter({matchedUploadedIDs})
|
||||
: matchedUploadedIDs = matchedUploadedIDs ?? {};
|
||||
|
||||
@@ -25,16 +25,26 @@ class SearchFilterDataProvider {
|
||||
|
||||
void applyFilters(List<HierarchicalSearchFilter> filters) {
|
||||
_recommendedFiltersNotifier.removeFilters(filters);
|
||||
|
||||
late final List<HierarchicalSearchFilter> allFiltersToAdd;
|
||||
if (!isSearchingNotifier.value) {
|
||||
isSearchingNotifier.value = true;
|
||||
_appliedFiltersNotifier.addFilters([initialGalleryFilter, ...filters]);
|
||||
allFiltersToAdd = [initialGalleryFilter, ...filters];
|
||||
} else {
|
||||
_appliedFiltersNotifier.addFilters(filters);
|
||||
allFiltersToAdd = filters;
|
||||
}
|
||||
|
||||
for (HierarchicalSearchFilter filter in allFiltersToAdd) {
|
||||
filter.isApplied = true;
|
||||
}
|
||||
_appliedFiltersNotifier.addFilters(allFiltersToAdd);
|
||||
}
|
||||
|
||||
void removeAppliedFilters(List<HierarchicalSearchFilter> filters) {
|
||||
_appliedFiltersNotifier.removeFilters(filters);
|
||||
for (HierarchicalSearchFilter filter in filters) {
|
||||
filter.isApplied = false;
|
||||
}
|
||||
_safelyAddToRecommended(filters);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,10 +66,14 @@ class _AppliedFiltersState extends State<AppliedFilters> {
|
||||
)
|
||||
: GenericFilterChip(
|
||||
label: filter.name(),
|
||||
onTap: () {
|
||||
apply: () {
|
||||
_searchFilterDataProvider.applyFilters([filter]);
|
||||
},
|
||||
remove: () {
|
||||
_searchFilterDataProvider.removeAppliedFilters([filter]);
|
||||
},
|
||||
leadingIcon: filter.icon(),
|
||||
isApplied: filter.isApplied,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,58 +1,114 @@
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter/widgets.dart";
|
||||
import "package:photos/core/constants.dart";
|
||||
import "package:photos/models/file/file.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/viewer/search/result/person_face_widget.dart";
|
||||
|
||||
class GenericFilterChip extends StatelessWidget {
|
||||
class GenericFilterChip extends StatefulWidget {
|
||||
final String label;
|
||||
final IconData? leadingIcon;
|
||||
final VoidCallback onTap;
|
||||
final VoidCallback apply;
|
||||
final VoidCallback remove;
|
||||
final bool isApplied;
|
||||
|
||||
const GenericFilterChip({
|
||||
required this.label,
|
||||
required this.onTap,
|
||||
required this.apply,
|
||||
required this.remove,
|
||||
required this.isApplied,
|
||||
this.leadingIcon,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<GenericFilterChip> createState() => _GenericFilterChipState();
|
||||
}
|
||||
|
||||
class _GenericFilterChipState extends State<GenericFilterChip> {
|
||||
late bool _isApplied;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_isApplied = widget.isApplied;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap.call,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: getEnteColorScheme(context).fillFaint,
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.circular(kFilterChipHeight / 2)),
|
||||
border: Border.all(
|
||||
color: getEnteColorScheme(context).strokeFaint,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
leadingIcon != null
|
||||
? Icon(
|
||||
leadingIcon,
|
||||
size: 16,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: Text(
|
||||
label,
|
||||
style: getEnteTextTheme(context).miniBold,
|
||||
),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if (_isApplied) {
|
||||
widget.remove();
|
||||
} else {
|
||||
widget.apply();
|
||||
}
|
||||
_isApplied = !_isApplied;
|
||||
});
|
||||
},
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: getEnteColorScheme(context).fillFaint,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(kFilterChipHeight / 2),
|
||||
),
|
||||
],
|
||||
border: Border.all(
|
||||
color: getEnteColorScheme(context).strokeFaint,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
widget.leadingIcon != null
|
||||
? Icon(
|
||||
widget.leadingIcon,
|
||||
size: 16,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: Text(
|
||||
widget.label,
|
||||
style: getEnteTextTheme(context).miniBold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
_isApplied
|
||||
? Positioned(
|
||||
top: -4,
|
||||
right: -4,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(1),
|
||||
decoration: BoxDecoration(
|
||||
color: getEnteColorScheme(context).backgroundElevated2,
|
||||
border: Border.all(
|
||||
color: getEnteColorScheme(context).strokeMuted,
|
||||
width: 0.5,
|
||||
),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.close_rounded,
|
||||
size: 14,
|
||||
color: getEnteColorScheme(context).textBase,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import "package:flutter/material.dart";
|
||||
import "package:photos/core/constants.dart";
|
||||
import "package:photos/models/search/hierarchical/face_filter.dart";
|
||||
import "package:photos/ui/viewer/gallery/state/search_filter_data_provider.dart";
|
||||
import "package:photos/ui/viewer/hierarchicial_search/filter_chip.dart";
|
||||
|
||||
class FilterOptionsBottomSheet extends StatelessWidget {
|
||||
final SearchFilterDataProvider searchFilterDataProvider;
|
||||
const FilterOptionsBottomSheet(
|
||||
this.searchFilterDataProvider, {
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final recommendations = searchFilterDataProvider.recommendations;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 32),
|
||||
child: SizedBox(
|
||||
height: kFilterChipHeight,
|
||||
child: ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final filter = recommendations[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: filter is FaceFilter
|
||||
? FaceFilterChip(
|
||||
personId: filter.personId,
|
||||
clusterId: filter.clusterId,
|
||||
faceThumbnailFile: filter.faceFile,
|
||||
name: filter.name(),
|
||||
onTap: () {
|
||||
searchFilterDataProvider.applyFilters([filter]);
|
||||
},
|
||||
)
|
||||
: GenericFilterChip(
|
||||
label: filter.name(),
|
||||
apply: () {
|
||||
searchFilterDataProvider.applyFilters([filter]);
|
||||
},
|
||||
remove: () {
|
||||
searchFilterDataProvider.removeAppliedFilters([filter]);
|
||||
},
|
||||
leadingIcon: filter.icon(),
|
||||
isApplied: filter.isApplied,
|
||||
),
|
||||
);
|
||||
},
|
||||
clipBehavior: Clip.none,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: recommendations.length,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,11 @@ import "package:flutter/material.dart";
|
||||
import "package:photos/core/constants.dart";
|
||||
import "package:photos/models/search/hierarchical/face_filter.dart";
|
||||
import "package:photos/models/search/hierarchical/hierarchical_search_filter.dart";
|
||||
import "package:photos/ui/components/buttons/icon_button_widget.dart";
|
||||
import "package:photos/ui/viewer/gallery/state/inherited_search_filter_data.dart";
|
||||
import "package:photos/ui/viewer/gallery/state/search_filter_data_provider.dart";
|
||||
import "package:photos/ui/viewer/hierarchicial_search/filter_chip.dart";
|
||||
import "package:photos/ui/viewer/hierarchicial_search/filter_options_bottom_sheet.dart";
|
||||
|
||||
class RecommendedFilters extends StatefulWidget {
|
||||
const RecommendedFilters({super.key});
|
||||
@@ -67,7 +69,23 @@ class _RecommendedFiltersState extends State<RecommendedFilters> {
|
||||
child: ListView.builder(
|
||||
key: ValueKey(_filtersUpdateCount),
|
||||
itemBuilder: (context, index) {
|
||||
final filter = _recommendations[index];
|
||||
if (index == 0) {
|
||||
return IconButtonWidget(
|
||||
icon: Icons.sort,
|
||||
iconButtonType: IconButtonType.rounded,
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return FilterOptionsBottomSheet(
|
||||
_searchFilterDataProvider,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
final filter = _recommendations[index - 1];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: filter is FaceFilter
|
||||
@@ -82,16 +100,21 @@ class _RecommendedFiltersState extends State<RecommendedFilters> {
|
||||
)
|
||||
: GenericFilterChip(
|
||||
label: filter.name(),
|
||||
onTap: () {
|
||||
apply: () {
|
||||
_searchFilterDataProvider.applyFilters([filter]);
|
||||
},
|
||||
remove: () {
|
||||
_searchFilterDataProvider
|
||||
.removeAppliedFilters([filter]);
|
||||
},
|
||||
leadingIcon: filter.icon(),
|
||||
isApplied: filter.isApplied,
|
||||
),
|
||||
);
|
||||
},
|
||||
clipBehavior: Clip.none,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: _recommendations.length,
|
||||
itemCount: _recommendations.length + 1,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user