[mob][photos] Add 'isApplied' state to filters, change UI for applied filters

This commit is contained in:
ashilkn
2024-10-22 17:17:00 +05:30
parent 2d6bd7f5a5
commit 1e868ac624
6 changed files with 192 additions and 41 deletions

View File

@@ -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 ?? {};

View File

@@ -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);
}

View File

@@ -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,
),
);
},

View File

@@ -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(),
],
),
);
}

View File

@@ -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),
),
),
);
}
}

View File

@@ -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),
),
),