Compare commits

...

5 Commits

Author SHA1 Message Date
58578c734e 🍱 Update app icon 2024-08-15 23:00:25 +08:00
76f2c0ad58 🐛 Fixes no api key hint 2024-08-15 22:53:49 +08:00
cfa61e1a8a Support named alert config 2024-08-15 22:50:00 +08:00
22b863f2bf 🌐 Complete english translations 2024-08-15 21:24:41 +08:00
4569c33430 Search hint 2024-08-15 21:20:07 +08:00
48 changed files with 97 additions and 18 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 900 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 B

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 B

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -1,15 +1,20 @@
import 'package:dietary_guard/models/food_data.dart';
class AlertConfiguration {
String name;
int nutrientId;
double maxValue;
double minValue;
AlertConfiguration({
required this.name,
required this.nutrientId,
required this.maxValue,
required this.minValue,
});
Map<String, dynamic> toJson() => {
'name': name,
'nutrient_id': nutrientId,
'min_value': minValue,
'max_value': maxValue,
@@ -17,6 +22,7 @@ class AlertConfiguration {
factory AlertConfiguration.fromJson(Map<String, dynamic> json) =>
AlertConfiguration(
name: json['name'],
nutrientId: json['nutrient_id'],
minValue: json['min_value'],
maxValue: json['max_value'],
@@ -25,18 +31,18 @@ class AlertConfiguration {
class AlertDetectResult {
AlertConfiguration config;
FoodNutrient? nutrient;
String name;
String? unitName;
double? current;
double? difference;
bool isOutOfRange;
bool isUndetected;
AlertDetectResult({
required this.config,
required this.nutrient,
required this.name,
required this.unitName,
required this.current,
required this.difference,
required this.isOutOfRange,
required this.isUndetected,

View File

@@ -33,14 +33,14 @@ class _FoodDetailsScreenState extends State<FoodDetailsScreen> {
double? difference;
String name = 'undetected'.tr;
String? unitName;
double? current;
FoodNutrient? current;
for (final nutrient in widget.item.foodNutrients) {
if (item.nutrientId != nutrient.nutrientId) continue;
name = nutrient.nutrientName;
unitName = unitNameValues.reverse[nutrient.unitName];
if (nutrient.value != null) {
current = nutrient;
final value = nutrient.value!;
current = value;
if (value > item.maxValue) {
difference = value - item.maxValue;
isOutOfRange = true;
@@ -54,9 +54,9 @@ class _FoodDetailsScreenState extends State<FoodDetailsScreen> {
_alertDetectResult.add(AlertDetectResult(
config: item,
nutrient: current,
name: name,
unitName: unitName,
current: current,
difference: difference,
isOutOfRange: isOutOfRange,
isUndetected: isUndetected,

View File

@@ -15,7 +15,9 @@ class QueryScreen extends StatefulWidget {
class _QueryScreenState extends State<QueryScreen> {
bool _isLoading = false;
bool _hasApiKey = true;
int? _totalCount;
List<FoodData> _foodData = List.empty();
Future<void> _searchFood(String probe) async {
@@ -24,11 +26,15 @@ class _QueryScreenState extends State<QueryScreen> {
setState(() => _isLoading = true);
final FoodDataController data = Get.find();
if (data.getApiKey() == null) return;
if (data.getApiKey() == null || data.getApiKey()!.isEmpty) {
setState(() => _hasApiKey = false);
return;
}
final result = await data.searchFood(probe);
setState(() {
_totalCount = result.totalHits;
_foodData = result.foods;
_isLoading = false;
});
@@ -38,7 +44,11 @@ class _QueryScreenState extends State<QueryScreen> {
void initState() {
super.initState();
final FoodDataController data = Get.find();
data.initialize(context);
data.initialize(context).then((_) {
setState(() {
_hasApiKey = data.getApiKey() != null && data.getApiKey()!.isNotEmpty;
});
});
}
@override
@@ -48,6 +58,14 @@ class _QueryScreenState extends State<QueryScreen> {
child: SafeArea(
child: Column(
children: [
if (!_hasApiKey)
Text('searchNoKeyHint'.tr).paddingSymmetric(vertical: 8)
else if (_totalCount != null)
Text('searchResultHint'
.trParams({'count': _totalCount.toString()}))
.paddingSymmetric(vertical: 8)
else
Text('searchHint'.tr).paddingSymmetric(vertical: 8),
SearchBar(
padding: const WidgetStatePropertyAll<EdgeInsets>(
EdgeInsets.symmetric(horizontal: 16.0),

View File

@@ -16,6 +16,7 @@ class _AlertSettingsScreenState extends State<AlertSettingsScreen> {
void _addAlert() {
setState(() {
_currentAlerts.add(AlertConfiguration(
name: 'Alert #${_currentAlerts.length}',
nutrientId: 0,
maxValue: 0,
minValue: 0,
@@ -64,16 +65,16 @@ class _AlertSettingsScreenState extends State<AlertSettingsScreen> {
children: [
Expanded(
child: TextFormField(
initialValue: x.nutrientId.toString(),
initialValue: x.name,
decoration: InputDecoration(
border: const OutlineInputBorder(),
label: Text("alertNutrientId".tr),
label: Text("alertName".tr),
isDense: true,
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
onChanged: (value) {
x.nutrientId = int.tryParse(value) ?? 0;
x.name = value;
},
),
),
@@ -87,6 +88,20 @@ class _AlertSettingsScreenState extends State<AlertSettingsScreen> {
],
),
const SizedBox(height: 12),
TextFormField(
initialValue: x.nutrientId.toString(),
decoration: InputDecoration(
border: const OutlineInputBorder(),
label: Text("alertNutrientId".tr),
isDense: true,
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
onChanged: (value) {
x.nutrientId = int.tryParse(value) ?? 0;
},
),
const SizedBox(height: 12),
Row(
children: [
Expanded(

View File

@@ -1,17 +1,53 @@
const i18nEnglish = {
'appName': 'DietaryGuard',
'appDescription': 'Your Healthy Dietary Guard',
'appCopyright': 'Copyright © 2024 Solsynth LLC\nOriginal Author @littlesheep',
'query': 'Query',
'settings': 'Settings',
'preparingData': 'Preparing data...',
'settingsApplied': 'Settings Applied',
'preparingData': 'Preparing data',
'settingsApplied': 'Settings applied',
'settingsDataSection': 'Data Source',
'settingsAlertSection': 'Alert',
'dataSourceSettings': 'Data Source Settings',
'alertSettings': 'Alert Configuration',
'newAlert': 'New Alert',
'apply': 'Apply',
'searchHistoryNotIncluded': 'Search History not Included, yet',
'searchHistoryNotIncluded': 'Search history not implemented yet',
'nutrients': 'Nutrients',
'alertNutrientId': 'Nutrient ID',
'alertMaxValue': 'Max',
'alertMinValue': 'Min',
'alertMaxValue': 'Alert Max Value',
'alertMinValue': 'Alert Min Value',
'alertName': 'Alert Name',
'alerts': 'Alerts',
'alertOutOfRange':
'@count alert rules triggered, click the button on the right for details',
'alertUnclear':
'@count alert rules lack supporting data, click the button on the right for details',
'alertEmpty': 'No alert rules configured, go to settings to add rules',
'alertSafe': 'No alert rules triggered, safe to consume',
'alertDetectResult': 'Alert Match Details',
'undetected': 'Not Detected',
'dataCollectionSelection': 'Search Data Collection Range',
'dataCollectionFoundation': 'Foundation Data Collection',
'dataCollectionFoundationDescription':
'Includes food raw materials, with a smaller range but comprehensive data accuracy',
'dataCollectionBranded': 'Branded Data Collection',
'dataCollectionBrandedDescription':
'Includes various branded processed foods',
'dataCollectionSurvey': 'Survey Data Collection',
'dataCollectionSurveyDescription':
'Uses data from the "What We Eat in America" journal',
'dataCollectionLegacy': 'Legacy Data Collection',
'dataCollectionLegacyDescription':
'Historical data from analysis, calculations, and public literature',
'fdcApiKey': 'USDA Food Data Central API Key',
'fdcApiKeyHint':
'DietaryGuards data comes from the USDA public API, so you need to configure an API key. But dont worry, its completely free! Check our wiki to learn how to get an API key.',
'fdcApiCredit':
'DietaryGuards food data comes from the U.S. Department of Agriculture, Agricultural Research Service, Beltsville Human Nutrition Research Center. FoodData Central. We appreciate their generous contribution of food data released into the public domain.',
'loading': 'Loading',
'searchHint': 'Type keywords below to search',
'searchNoKeyHint':
'No API key, please first set up the API key in "Settings" > "Data Source"',
'searchResultHint': '@count records matched (showing 100)',
};

View File

@@ -17,6 +17,7 @@ const i18nSimplifiedChinese = {
'alertNutrientId': '营养物质编号',
'alertMaxValue': '告警上限',
'alertMinValue': '告警下限',
'alertName': '告警规则名',
'alerts': '告警',
'alertOutOfRange': '有 @count 项告警规则触发,点击右侧按钮了解详情',
'alertUnclear': '有 @count 项告警规则并无数据支持,点击右侧按钮了解详情',
@@ -39,4 +40,7 @@ const i18nSimplifiedChinese = {
'fdcApiCredit':
'DietaryGuard 的食品数据来源于 U.S. Department of Agriculture, Agricultural Research Service, Beltsville Human Nutrition Research Center. FoodData Central. 在此感谢他们慷慨贡献的食品数据并发布在公有领域。',
'loading': '加载中',
'searchHint': '在下方键入关键词来搜索',
'searchNoKeyHint': '无 API 令牌,请先在「设置」>「数据源」设置 API 密钥',
'searchResultHint': '共命中 @count 条记录(显示前 100 条)',
};

View File

@@ -60,13 +60,13 @@ class AlertDetectResultDialog extends StatelessWidget {
title: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(item.name),
Text(item.config.name),
const SizedBox(width: 6),
Badge(label: Text('#${item.config.nutrientId}'))
],
),
subtitle: Text(
'${item.current ?? 'undetected'.tr} ${(item.difference ?? 0) > 0 ? '' : ''}${item.difference?.abs().toPrecision(2) ?? '-'} ${item.unitName ?? ''}',
'${item.nutrient?.value ?? 'undetected'.tr} ${(item.difference ?? 0) > 0 ? '' : ''}${item.difference?.abs().toPrecision(2) ?? '-'} ${item.unitName ?? ''}',
),
);
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 B

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 B

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
version: 1.0.0+2
environment:
sdk: ^3.5.0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 B

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 10 KiB