Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 5586 - OxF filter for term searches #5637

Merged
merged 1 commit into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion packages/smooth_app/lib/data_models/product_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class ProductList {
this.pageNumber = 0,
this.language,
this.country,
this.productType,
});

ProductList.keywordSearch(
Expand All @@ -55,13 +56,15 @@ class ProductList {
required int pageNumber,
required OpenFoodFactsLanguage language,
required OpenFoodFactsCountry? country,
required ProductType productType,
}) : this._(
listType: ProductListType.HTTP_SEARCH_KEYWORDS,
parameters: keywords,
pageSize: pageSize,
pageNumber: pageNumber,
language: language,
country: country,
productType: productType,
);

ProductList.categorySearch(
Expand All @@ -70,78 +73,90 @@ class ProductList {
required int pageNumber,
required OpenFoodFactsLanguage language,
required OpenFoodFactsCountry? country,
required ProductType productType,
}) : this._(
listType: ProductListType.HTTP_SEARCH_CATEGORY,
parameters: category,
pageSize: pageSize,
pageNumber: pageNumber,
language: language,
country: country,
productType: productType,
);

ProductList.contributor(
final String userId, {
required int pageSize,
required int pageNumber,
required OpenFoodFactsLanguage language,
required ProductType productType,
}) : this._(
listType: ProductListType.HTTP_USER_CONTRIBUTOR,
parameters: userId,
pageSize: pageSize,
pageNumber: pageNumber,
language: language,
productType: productType,
);

ProductList.informer(
final String userId, {
required int pageSize,
required int pageNumber,
required OpenFoodFactsLanguage language,
required ProductType productType,
}) : this._(
listType: ProductListType.HTTP_USER_INFORMER,
parameters: userId,
pageSize: pageSize,
pageNumber: pageNumber,
language: language,
productType: productType,
);

ProductList.photographer(
final String userId, {
required int pageSize,
required int pageNumber,
required OpenFoodFactsLanguage language,
required ProductType productType,
}) : this._(
listType: ProductListType.HTTP_USER_PHOTOGRAPHER,
parameters: userId,
pageSize: pageSize,
pageNumber: pageNumber,
language: language,
productType: productType,
);

ProductList.toBeCompleted(
final String userId, {
required int pageSize,
required int pageNumber,
required OpenFoodFactsLanguage language,
required ProductType productType,
}) : this._(
listType: ProductListType.HTTP_USER_TO_BE_COMPLETED,
parameters: userId,
pageSize: pageSize,
pageNumber: pageNumber,
language: language,
productType: productType,
);

ProductList.allToBeCompleted({
required int pageSize,
required int pageNumber,
required OpenFoodFactsLanguage language,
required OpenFoodFactsCountry? country,
required ProductType productType,
}) : this._(
listType: ProductListType.HTTP_ALL_TO_BE_COMPLETED,
pageSize: pageSize,
pageNumber: pageNumber,
language: language,
country: country,
productType: productType,
);

ProductList.history() : this._(listType: ProductListType.HISTORY);
Expand Down Expand Up @@ -171,6 +186,9 @@ class ProductList {
/// Country at query time.
final OpenFoodFactsCountry? country;

/// ProductType at query time.
final ProductType? productType;

/// "Total size" returned by the query.
int totalSize = 0;

Expand Down Expand Up @@ -251,7 +269,8 @@ class ProductList {
',$pageSize'
',$pageNumber'
',${language?.code ?? ''}'
',${country?.offTag ?? ''}';
',${country?.offTag ?? ''}'
'${productType == null || productType == ProductType.food ? '' : ',${productType!.offTag}'}';
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ class UserPreferencesAccount extends AbstractUserPreferences {
productQuery: PagedUserProductQuery(
userId: userId,
type: UserSearchType.CONTRIBUTOR,
// TODO(monsieurtanuki): only food?
productType: ProductType.food,
),
title: appLocalizations.user_search_contributor_title,
iconData: Icons.add_circle_outline,
Expand All @@ -182,6 +184,7 @@ class UserPreferencesAccount extends AbstractUserPreferences {
productQuery: PagedUserProductQuery(
userId: userId,
type: UserSearchType.INFORMER,
productType: ProductType.food,
),
title: appLocalizations.user_search_informer_title,
iconData: Icons.edit,
Expand All @@ -193,6 +196,7 @@ class UserPreferencesAccount extends AbstractUserPreferences {
productQuery: PagedUserProductQuery(
userId: userId,
type: UserSearchType.PHOTOGRAPHER,
productType: ProductType.food,
),
title: appLocalizations.user_search_photographer_title,
iconData: Icons.add_a_photo,
Expand All @@ -204,6 +208,7 @@ class UserPreferencesAccount extends AbstractUserPreferences {
productQuery: PagedUserProductQuery(
userId: userId,
type: UserSearchType.TO_BE_COMPLETED,
productType: ProductType.food,
),
title: appLocalizations.user_search_to_be_completed_title,
iconData: Icons.more_horiz,
Expand Down Expand Up @@ -296,7 +301,9 @@ class UserPreferencesAccount extends AbstractUserPreferences {
'app/products',
),
_buildProductQueryTile(
productQuery: PagedToBeCompletedProductQuery(),
productQuery: PagedToBeCompletedProductQuery(
productType: ProductType.food,
),
title: appLocalizations.all_search_to_be_completed_title,
iconData: Icons.more_outlined,
context: context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:http/http.dart' as http;
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:share_plus/share_plus.dart';
import 'package:smooth_app/data_models/github_contributors_model.dart';
Expand Down Expand Up @@ -170,7 +171,10 @@ class UserPreferencesContribute extends AbstractUserPreferences {
ProductQueryPageHelper.openBestChoice(
name: appLocalizations.all_search_to_be_completed_title,
localDatabase: localDatabase,
productQuery: PagedToBeCompletedProductQuery(),
productQuery: PagedToBeCompletedProductQuery(
// TODO(monsieurtanuki): only food?
productType: ProductType.food,
),
// the other "context"s being popped
context: this.context,
editableAppBarTitle: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ abstract class SearchHelper extends ValueNotifier<SearchQuery?> {
/// Hint text for the search field.
String getHintText(final AppLocalizations appLocalizations);

Widget? getAdditionalFilter() => null;

/// Returns all the previous queries, in reverse order.
List<String> getAllQueries(final LocalDatabase localDatabase) =>
DaoStringList(localDatabase).getAll(historyKey).reversed.toList();
Expand Down
5 changes: 4 additions & 1 deletion packages/smooth_app/lib/pages/product/summary_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,10 @@ class _SummaryCardState extends State<SummaryCard> with UpToDateMixin {
onPressed: () async => ProductQueryPageHelper.openBestChoice(
name: categoryLabel!,
localDatabase: context.read<LocalDatabase>(),
productQuery: CategoryProductQuery(categoryTag!),
productQuery: CategoryProductQuery(
categoryTag!,
productType: upToDateProduct.productType ?? ProductType.food,
),
context: context,
searchResult: false,
),
Expand Down
32 changes: 19 additions & 13 deletions packages/smooth_app/lib/pages/search/search_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class _SearchFieldState extends State<SearchField> {

final TextStyle textStyle = SearchFieldUIHelper.textStyle(context);

final Widget? additionalFilter = widget.searchHelper.getAdditionalFilter();
return ChangeNotifierProvider<TextEditingController>.value(
value: _controller!,
child: SmoothHero(
Expand All @@ -90,19 +91,24 @@ class _SearchFieldState extends State<SearchField> {
: null,
child: Material(
// ↑ Needed by the Hero Widget
child: TextField(
controller: _controller,
focusNode: _focusNode,
onSubmitted: (String query) => _performSearch(context, query),
textInputAction: TextInputAction.search,
enableSuggestions: widget.enableSuggestions,
autocorrect: widget.autocorrect,
style: textStyle,
decoration: _getInputDecoration(
context,
localizations,
),
cursorColor: textStyle.color,
child: Column(
children: <Widget>[
TextField(
controller: _controller,
focusNode: _focusNode,
onSubmitted: (String query) => _performSearch(context, query),
textInputAction: TextInputAction.search,
enableSuggestions: widget.enableSuggestions,
autocorrect: widget.autocorrect,
style: textStyle,
decoration: _getInputDecoration(
context,
localizations,
),
cursorColor: textStyle.color,
),
if (additionalFilter != null) additionalFilter,
],
),
),
),
Expand Down
48 changes: 46 additions & 2 deletions packages/smooth_app/lib/pages/search/search_product_helper.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/data_models/fetched_product.dart';
import 'package:smooth_app/database/dao_string_list.dart';
Expand All @@ -12,18 +13,25 @@ import 'package:smooth_app/pages/product/common/product_dialog_helper.dart';
import 'package:smooth_app/pages/product/common/product_query_page_helper.dart';
import 'package:smooth_app/pages/product/common/search_helper.dart';
import 'package:smooth_app/query/keywords_product_query.dart';
import 'package:smooth_app/query/product_query.dart';

/// Search helper dedicated to product search.
class SearchProductHelper extends SearchHelper {
SearchProductHelper();

// TODO(monsieurtanuki): maybe reinit it with latest value
ProductType _productType = ProductType.food;

@override
String get historyKey => DaoStringList.keySearchProductHistory;

@override
String getHintText(final AppLocalizations appLocalizations) =>
appLocalizations.search;

@override
Widget getAdditionalFilter() => _ProductTypeFilter(this);

@override
void search(
BuildContext context,
Expand Down Expand Up @@ -71,6 +79,7 @@ class SearchProductHelper extends SearchHelper {
final FetchedProduct fetchedProduct =
await productDialogHelper.openBestChoice();
if (fetchedProduct.status == FetchedProductStatus.ok) {
// TODO(monsieurtanuki): add OxF to Matomo data?
AnalyticsHelper.trackSearch(
search: value,
searchCategory: 'barcode',
Expand All @@ -95,7 +104,6 @@ class SearchProductHelper extends SearchHelper {
}
}

// used to be in now defunct `ChoosePage`
Future<void> _onSubmittedText(
final String value,
final BuildContext context,
Expand All @@ -107,11 +115,47 @@ class SearchProductHelper extends SearchHelper {
widget: await ProductQueryPageHelper.getBestChoiceWidget(
name: value,
localDatabase: localDatabase,
productQuery: KeywordsProductQuery(value),
productQuery: KeywordsProductQuery(
value,
productType: _productType,
),
context: context,
editableAppBarTitle: false,
),
),
);
}
}

class _ProductTypeFilter extends StatefulWidget {
const _ProductTypeFilter(this.searchProductHelper);

final SearchProductHelper searchProductHelper;

@override
State<_ProductTypeFilter> createState() => _ProductTypeFilterState();
}

class _ProductTypeFilterState extends State<_ProductTypeFilter> {
@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
final List<ButtonSegment<ProductType>> segments =
<ButtonSegment<ProductType>>[];
for (final ProductType productType in ProductType.values) {
segments.add(
ButtonSegment<ProductType>(
value: productType,
label: Text(productType.getLabel(appLocalizations)),
),
);
}
return SegmentedButton<ProductType>(
segments: segments,
selected: <ProductType>{widget.searchProductHelper._productType},
onSelectionChanged: (Set<ProductType> newSelection) => setState(
() => widget.searchProductHelper._productType = newSelection.first,
),
);
}
}
Loading