From d9e7840822ef2f2d0c8073b1d61fc00514f38e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 3 Jul 2023 10:37:41 +0200 Subject: [PATCH 001/184] class structure and strings for table creation --- .../model/database/SQLiteDatabaseAccess.dart | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index e69de29b..52ed4cca 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -0,0 +1,180 @@ +import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; +import 'package:app/view_model/repository/data_classes/meal/Additive.dart'; +import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; +import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Mealplan.dart'; +import 'package:app/view_model/repository/error_handling/Result.dart'; +import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; + + +class SQLiteDatabaseAccess implements IDatabaseAccess { + /// The string to create a table for the canteen. + final String _canteen = ''' + CREATE TABLE Canteen ( + canteenID TEXT PRIMARY KEY, + name TEXT + ) + '''; + + /// The string to create a table for a line of a canteen. + final String _line = ''' + CREATE TABLE Line( + lineID TEXT PRIMARY KEY, + canteenID TEXT, + name TEXT, + position INTEGER, + FOREIGN KEY(canteenID) REFERENCES Canteen(canteenID) + ) + '''; + + /// The string to create a table for a mealplan. + final String _mealplan = ''' + CREATE TABLE MealPlan( + mealplanID TEXT PRIMARY KEY, + lineID TEXT, + date TEXT PRIMARY KEY, + isClosed BOOLEAN, + FOREIGN KEY(lineID) REFERENCES Line(lineID) + ) + '''; + + /// The string to create a table for a meal. + final String _meal = ''' + CREATE TABLE Meal( + mealID TEXT PRIMARY KEY, + mealplanID TEXT, + name TEXT, + foodtype TEXT CHECK(foodtype IN (${FoodType.values.map((type) => "'$type'").join(', ')})), + priceStudent INTEGER, + priceEmployee INTEGER, + pricePupil INTEGER, + priceGuest INTEGER, + individualRating INTEGER, + numberOfRatings INTEGER, + averageRating DECIMAL(1,1), + lastServed TEXT, + nextServed TEXT, + relativeFrequency TEXT CHECK IN (${Frequency.values.map((frequency) => "'$frequency'").join(', ')}), + FOREIGN KEY(mealplanID) REFERENCES MealPlan(mealplanID) + ) + '''; + + /// The string to create a table for a side. + final String _side = ''' + CREATE TABLE Side( + sideID TEXT PRIMARY KEY, + mealID TEXT, + name TEXT, + foodtype TEXT CHECK(foodtype IN (${FoodType.values.map((type) => "'$type'").join(', ')})), + priceStudent INTEGER, + priceEmployee INTEGER, + pricePupil INTEGER, + priceGuest INTEGER, + FOREIGN KEY(mealID) REFERENCES Meal(mealID) + ) + '''; + + /// The string to create a table for an additive. + final String _image = ''' + CREATE TABLE Image( + imageID TEXT PRIMARY KEY, + mealID TEXT, + url TEXT, + FOREIGN KEY(mealID) REFERENCES Meal(mealID) + ) + '''; + + /// The string to create a table for an additive of a meal. + final String _mealAdditive = ''' + CREATE TABLE MealAdditive( + mealID TEXT, + additiveID TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), + FOREIGN KEY(mealID) REFERENCES Meal(mealID), + ) + '''; + + /// The string to create a table for an allergen of a meal. + final String _mealAllergen = ''' + CREATE TABLE MealAllergen( + mealID TEXT, + allergenID TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), + FOREIGN KEY(mealID) REFERENCES Meal(mealID), + ) + '''; + + /// The string to create a table for an additive of a side. + final String _sideAdditive = ''' + CREATE TABLE SideAdditive( + sideID TEXT, + additiveID TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), + FOREIGN KEY(sideID) REFERENCES Side(sideID), + ) + '''; + + /// The string to create a table for an allergen of a side. + final String _sideAllergen = ''' + CREATE TABLE SideAllergen( + sideID TEXT, + allergenID TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), + FOREIGN KEY(sideID) REFERENCES Side(sideID), + ) + '''; + + /// The string to create a table for a favorite. + final String _favorite = ''' + CREATE TABLE Favorite( + favoriteID TEXT PRIMARY KEY, + lineID TEXT, + lastDate TEXT, + foodtype TEXT CHECK(foodtype IN (${FoodType.values.map((type) => "'$type'").join(', ')})), + priceStudent INTEGER, + priceEmployee INTEGER, + pricePupil INTEGER, + priceGuest INTEGER, + FOREIGN KEY(lineID) REFERENCES Line(lineID) + ) + '''; + + SQLiteDatabaseAccess() { + // TODO: implement constructor + } + + @override + Future addFavorite(Meal meal) { + // TODO: implement addFavorite + throw UnimplementedError(); + } + + @override + Future deleteFavorite(Meal meal) { + // TODO: implement deleteFavorite + throw UnimplementedError(); + } + + @override + Future> getFavorites() { + // TODO: implement getFavorites + throw UnimplementedError(); + } + + @override + Future> getMealFavorite(String id) { + // TODO: implement getMealFavorite + throw UnimplementedError(); + } + + @override + Future>> getMealPlan(DateTime date, Canteen canteen) { + // TODO: implement getMealPlan + throw UnimplementedError(); + } + + @override + Future updateAll(List mealplans) { + // TODO: implement updateAll + throw UnimplementedError(); + } + +} \ No newline at end of file From 90d578e34a00e14bc084c1ecf696488d7f179718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 3 Jul 2023 15:51:40 +0200 Subject: [PATCH 002/184] add dependency for shared preferences --- .../Flutter/GeneratedPluginRegistrant.swift | 2 + app/pubspec.lock | 56 +++++++++++++++++++ app/pubspec.yaml | 1 + 3 files changed, 59 insertions(+) diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift index e777c67d..b8e2b22f 100644 --- a/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,7 +6,9 @@ import FlutterMacOS import Foundation import path_provider_foundation +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/app/pubspec.lock b/app/pubspec.lock index 0c4deba5..38701396 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -293,6 +293,62 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.4" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "0dc5c49ad8a05ed358b991b60c7b0ba1a14e16dae58af9b420d6b9e82dc024ab" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d + url: "https://pub.dev" + source: hosted + version: "2.3.0" sky_engine: dependency: transitive description: flutter diff --git a/app/pubspec.yaml b/app/pubspec.yaml index c8899e62..94bf80d6 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: sdk: flutter flutter_svg: ^2.0.7 + shared_preferences: ^2.2.0 dev_dependencies: flutter_test: From d122aa3edeb49315d1c27b42d406194991238489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 3 Jul 2023 17:10:46 +0200 Subject: [PATCH 003/184] implement everything but canteen --- .../local_storage/SharedPreferenceAccess.dart | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index e69de29b..dc698da5 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -0,0 +1,145 @@ + +import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; +import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; +import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/data_classes/settings/ColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; +import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; +import 'package:app/view_model/repository/interface/ILocalStorage.dart'; + +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../view_model/repository/data_classes/filter/Sorting.dart'; +import '../../view_model/repository/data_classes/meal/FoodType.dart'; + +class SharedPreferenceAccess implements ILocalStorage { + + SharedPreferenceAccess(); + + @override + Future getCanteen() async { + // TODO: implement getColorScheme + throw UnimplementedError(); + } + + @override + Future getClientIdentifier() async { + final pref = await SharedPreferences.getInstance(); + final clientIdentifier = pref.getString('clientIdentifier') ?? ""; + return Future.value(clientIdentifier); + } + + @override + Future getColorScheme() async { + final pref = await SharedPreferences.getInstance(); + final colorScheme = pref.getString('colorScheme'); + return Future.value(ColorScheme.values.firstWhere((e) => e.toString() == colorScheme)); + } + + @override + Future getFilterPreferences() async { + final pref = await SharedPreferences.getInstance(); + + // get data from shared preferences + final categories = pref.getStringList('filterCategories'); + final allergens = pref.getStringList('filterAllergens'); + final price = pref.getInt('filterPrice'); + final rating = pref.getInt('filterRating'); + final frequency = pref.getStringList('filterFrequency'); + final onlyFavorites = pref.getBool('filterOnlyFavorites'); + final sortedBy = pref.getString('filterSort'); + final ascending = pref.getBool('filterSortAscending'); + + // convert values to right format + List? foodTypeEnum; + if (categories != null) { + foodTypeEnum = List.of(categories.map((e) => FoodType.values.firstWhere((element) => element.toString() == e))); + } + + List? allergensEnum; + if (allergens != null) { + allergensEnum = List.of(allergens.map((e) => Allergen.values.firstWhere((element) => element.toString() == e))); + } + + List? frequencyEnum; + if (frequency != null) { + frequencyEnum = List.of(frequency.map((e) => Frequency.values.firstWhere((element) => element.toString() == e))); + } + + Sorting? sortedByEnum; + if (categories != null) { + sortedByEnum = Sorting.values.firstWhere((e) => e.toString() == sortedBy); + } + + // return filter preferences + return Future.value(FilterPreferences( + categories: foodTypeEnum, + allergens: allergensEnum, + price: price, + rating: rating, + frequency: frequencyEnum, + onlyFavorite: onlyFavorites, + sortedBy: sortedByEnum, + ascending: ascending + )); + } + + @override + Future getMealPlanFormat() async { + final pref = await SharedPreferences.getInstance(); + final mealPlanFormat = pref.getString('mealPlanFormat'); + return Future.value(MealPlanFormat.values.firstWhere((e) => e.toString() == mealPlanFormat)); + } + + @override + Future getPriceCategory() async { + final pref = await SharedPreferences.getInstance(); + final priceCategory = pref.getString('priceCategory'); + return Future.value(PriceCategory.values.firstWhere((e) => e.toString() == priceCategory)); + + } + + @override + Future setCanteen(Canteen canteen) { + // TODO: implement setCanteen + throw UnimplementedError(); + } + + @override + Future setClientIdentifier(String identifier) async { + final pref = await SharedPreferences.getInstance(); + await pref.setString('clientIdentifier', identifier); + } + + @override + Future setColorScheme(ColorScheme scheme) async { + final pref = await SharedPreferences.getInstance(); + await pref.setString('colorScheme', scheme.toString()); + } + + @override + Future setFilterPreferences(FilterPreferences filter) async { + final pref = await SharedPreferences.getInstance(); + await pref.setStringList('filterCategories', List.of(filter.categories.map((e) => e.toString()))); + await pref.setStringList('filterAllergens', List.of(filter.allergens.map((e) => e.toString()))); + await pref.setInt('filterPrice', filter.price); + await pref.setInt('filterRating', filter.rating); + await pref.setStringList('filterFrequency', List.of(filter.frequency.map((e) => e.toString()))); + await pref.setBool('filterFavorite', filter.onlyFavorite); + await pref.setString('filterSort', filter.sortedBy.toString()); + await pref.setBool('filterSortAscending', filter.ascending); + } + + @override + Future setMealPlanFormat(MealPlanFormat format) async { + final pref = await SharedPreferences.getInstance(); + await pref.setString('mealPlanFormat', format.toString()); + } + + @override + Future setPriceCategory(PriceCategory category) async { + final pref = await SharedPreferences.getInstance(); + await pref.setString('priceCategory', category.toString()); + } +} \ No newline at end of file From db2650d53036255a1c8b2f6eb27ff28d7e2a6e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 4 Jul 2023 17:05:08 +0200 Subject: [PATCH 004/184] NOT NULL statements and Checks where useful --- .../model/database/SQLiteDatabaseAccess.dart | 71 ++++++++++--------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index 52ed4cca..5e9357c9 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -14,7 +14,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { final String _canteen = ''' CREATE TABLE Canteen ( canteenID TEXT PRIMARY KEY, - name TEXT + name TEXT NOT NULL ) '''; @@ -22,9 +22,9 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { final String _line = ''' CREATE TABLE Line( lineID TEXT PRIMARY KEY, - canteenID TEXT, - name TEXT, - position INTEGER, + canteenID TEXT NOT NULL, + name TEXT NOT NULL, + position INTEGER NOT NULL, FOREIGN KEY(canteenID) REFERENCES Canteen(canteenID) ) '''; @@ -32,11 +32,12 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { /// The string to create a table for a mealplan. final String _mealplan = ''' CREATE TABLE MealPlan( - mealplanID TEXT PRIMARY KEY, - lineID TEXT, - date TEXT PRIMARY KEY, - isClosed BOOLEAN, - FOREIGN KEY(lineID) REFERENCES Line(lineID) + mealplanID TEXT, + lineID TEXT NOT NULL, + date TEXT, + isClosed BOOLEAN NOT NULL, + FOREIGN KEY(lineID) REFERENCES Line(lineID), + PRIMARY KEY(mealplanID, date) ) '''; @@ -44,17 +45,17 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { final String _meal = ''' CREATE TABLE Meal( mealID TEXT PRIMARY KEY, - mealplanID TEXT, - name TEXT, - foodtype TEXT CHECK(foodtype IN (${FoodType.values.map((type) => "'$type'").join(', ')})), - priceStudent INTEGER, - priceEmployee INTEGER, - pricePupil INTEGER, - priceGuest INTEGER, + mealplanID TEXT NOT NULL, + name TEXT NOT NULL, + foodtype TEXT NOT NULL CHECK(foodtype IN (${FoodType.values.map((type) => "'$type'").join(', ')})), + priceStudent INTEGER NOT NULL CHECK(priceStudent >= 0), + priceEmployee INTEGER NOT NULL CHECK(priceEmployee >= 0), + pricePupil INTEGER NOT NULL CHECK(pricePupil >= 0), + priceGuest INTEGER NOT NULL CHECK(priceGuest >= 0), individualRating INTEGER, - numberOfRatings INTEGER, + numberOfRatings INTEGER NOT NULL, averageRating DECIMAL(1,1), - lastServed TEXT, + lastServed TEXT NOT NULL, nextServed TEXT, relativeFrequency TEXT CHECK IN (${Frequency.values.map((frequency) => "'$frequency'").join(', ')}), FOREIGN KEY(mealplanID) REFERENCES MealPlan(mealplanID) @@ -65,13 +66,13 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { final String _side = ''' CREATE TABLE Side( sideID TEXT PRIMARY KEY, - mealID TEXT, - name TEXT, - foodtype TEXT CHECK(foodtype IN (${FoodType.values.map((type) => "'$type'").join(', ')})), - priceStudent INTEGER, - priceEmployee INTEGER, - pricePupil INTEGER, - priceGuest INTEGER, + mealID TEXT NOT NULL, + name TEXT NOT NULL, + foodtype TEXT NOT NULL CHECK(foodtype IN (${FoodType.values.map((type) => "'$type'").join(', ')})), + priceStudent INTEGER NOT NULL CHECK(priceStudent >= 0), + priceEmployee INTEGER NOT NULL CHECK(priceEmployee >= 0), + pricePupil INTEGER NOT NULL CHECK(pricePupil >= 0), + priceGuest INTEGER NOT NULL CHECK(priceGuest >= 0), FOREIGN KEY(mealID) REFERENCES Meal(mealID) ) '''; @@ -80,8 +81,8 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { final String _image = ''' CREATE TABLE Image( imageID TEXT PRIMARY KEY, - mealID TEXT, - url TEXT, + mealID TEXT NOT NULL, + url TEXT NOT NULL, FOREIGN KEY(mealID) REFERENCES Meal(mealID) ) '''; @@ -92,6 +93,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { mealID TEXT, additiveID TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), FOREIGN KEY(mealID) REFERENCES Meal(mealID), + PRIMARY KEY(mealID, additiveID) ) '''; @@ -101,6 +103,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { mealID TEXT, allergenID TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), FOREIGN KEY(mealID) REFERENCES Meal(mealID), + PRIMARY KEY(mealID, allergenID) ) '''; @@ -110,6 +113,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { sideID TEXT, additiveID TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), FOREIGN KEY(sideID) REFERENCES Side(sideID), + PRIMARY KEY(sideID, additiveID) ) '''; @@ -119,6 +123,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { sideID TEXT, allergenID TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), FOREIGN KEY(sideID) REFERENCES Side(sideID), + PRIMARY KEY(sideID, allergenID) ) '''; @@ -126,13 +131,13 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { final String _favorite = ''' CREATE TABLE Favorite( favoriteID TEXT PRIMARY KEY, - lineID TEXT, - lastDate TEXT, + lineID TEXT NOT NULL, + lastDate TEXT NOT NULL, foodtype TEXT CHECK(foodtype IN (${FoodType.values.map((type) => "'$type'").join(', ')})), - priceStudent INTEGER, - priceEmployee INTEGER, - pricePupil INTEGER, - priceGuest INTEGER, + priceStudent INTEGER CHECK(priceStudent > 0), + priceEmployee INTEGER CHECK(priceEmployee > 0), + pricePupil INTEGER CHECK(pricePupil > 0), + priceGuest INTEGER CHECK(priceGuest > 0), FOREIGN KEY(lineID) REFERENCES Line(lineID) ) '''; From 2e9961a34af3680a647066d7015e0bb793f66f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 4 Jul 2023 17:18:59 +0200 Subject: [PATCH 005/184] change of return values and initialise Canteen getter and setter --- .../local_storage/SharedPreferenceAccess.dart | 28 +++++++++---------- .../repository/interface/ILocalStorage.dart | 13 ++++----- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index dc698da5..30b65f25 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -2,7 +2,6 @@ import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; -import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; import 'package:app/view_model/repository/data_classes/settings/ColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; @@ -16,13 +15,7 @@ import '../../view_model/repository/data_classes/meal/FoodType.dart'; class SharedPreferenceAccess implements ILocalStorage { SharedPreferenceAccess(); - - @override - Future getCanteen() async { - // TODO: implement getColorScheme - throw UnimplementedError(); - } - + @override Future getClientIdentifier() async { final pref = await SharedPreferences.getInstance(); @@ -100,12 +93,6 @@ class SharedPreferenceAccess implements ILocalStorage { } - @override - Future setCanteen(Canteen canteen) { - // TODO: implement setCanteen - throw UnimplementedError(); - } - @override Future setClientIdentifier(String identifier) async { final pref = await SharedPreferences.getInstance(); @@ -142,4 +129,17 @@ class SharedPreferenceAccess implements ILocalStorage { final pref = await SharedPreferences.getInstance(); await pref.setString('priceCategory', category.toString()); } + + @override + Future getCanteen() async { + final pref = await SharedPreferences.getInstance(); + final canteen = pref.getString('canteen') ?? ""; + return Future.value(canteen); + } + + @override + Future setCanteen(String canteen) async { + final pref = await SharedPreferences.getInstance(); + await pref.setString('canteen', canteen); + } } \ No newline at end of file diff --git a/app/lib/view_model/repository/interface/ILocalStorage.dart b/app/lib/view_model/repository/interface/ILocalStorage.dart index 58c401ed..8a3e19e4 100644 --- a/app/lib/view_model/repository/interface/ILocalStorage.dart +++ b/app/lib/view_model/repository/interface/ILocalStorage.dart @@ -1,6 +1,5 @@ import '../data_classes/filter/FilterPreferences.dart'; -import '../data_classes/mealplan/Canteen.dart'; import '../data_classes/settings/ColorScheme.dart'; import '../data_classes/settings/MealPlanFormat.dart'; import '../data_classes/settings/PriceCategory.dart'; @@ -25,14 +24,14 @@ abstract class ILocalStorage { /// @return The result of the update. Future setFilterPreferences(FilterPreferences filter); - /// The saved Canteen is returned. - /// @return The saved Canteen. - Future getCanteen(); + /// The saved canteen id is returned. + /// @return The saved canteen id. + Future getCanteen(); - /// The committed Canteen is set. - /// @param canteen The new Canteen. + /// The committed id of the canteen is set. + /// @param canteen The id of the new canteen. /// @return The result of the update. - Future setCanteen(Canteen canteen); + Future setCanteen(String canteen); /// The saved ColorScheme is returned. /// @return The saved ColorScheme. From 3b3dcf50ce33b8e431e5dc3bda30e1f8161dbed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 4 Jul 2023 22:18:59 +0200 Subject: [PATCH 006/184] testing SharedPreferenceAccess --- .../local_storage/SharedPreferenceAccess.dart | 30 ++---- app/test/model/SharedPreferencesTest.dart | 100 ++++++++++++++++++ 2 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 app/test/model/SharedPreferencesTest.dart diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index 30b65f25..27d695c8 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -2,7 +2,7 @@ import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; -import 'package:app/view_model/repository/data_classes/settings/ColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:app/view_model/repository/interface/ILocalStorage.dart'; @@ -13,34 +13,31 @@ import '../../view_model/repository/data_classes/filter/Sorting.dart'; import '../../view_model/repository/data_classes/meal/FoodType.dart'; class SharedPreferenceAccess implements ILocalStorage { + SharedPreferences pref; + + SharedPreferenceAccess(this.pref); - SharedPreferenceAccess(); - @override Future getClientIdentifier() async { - final pref = await SharedPreferences.getInstance(); final clientIdentifier = pref.getString('clientIdentifier') ?? ""; return Future.value(clientIdentifier); } @override - Future getColorScheme() async { - final pref = await SharedPreferences.getInstance(); + Future getColorScheme() async { final colorScheme = pref.getString('colorScheme'); - return Future.value(ColorScheme.values.firstWhere((e) => e.toString() == colorScheme)); + return Future.value(MensaColorScheme.values.firstWhere((e) => e.toString() == colorScheme)); } @override Future getFilterPreferences() async { - final pref = await SharedPreferences.getInstance(); - // get data from shared preferences final categories = pref.getStringList('filterCategories'); final allergens = pref.getStringList('filterAllergens'); final price = pref.getInt('filterPrice'); final rating = pref.getInt('filterRating'); final frequency = pref.getStringList('filterFrequency'); - final onlyFavorites = pref.getBool('filterOnlyFavorites'); + final onlyFavorites = pref.getBool('filterFavorite'); final sortedBy = pref.getString('filterSort'); final ascending = pref.getBool('filterSortAscending'); @@ -61,7 +58,7 @@ class SharedPreferenceAccess implements ILocalStorage { } Sorting? sortedByEnum; - if (categories != null) { + if (sortedBy != null) { sortedByEnum = Sorting.values.firstWhere((e) => e.toString() == sortedBy); } @@ -80,14 +77,12 @@ class SharedPreferenceAccess implements ILocalStorage { @override Future getMealPlanFormat() async { - final pref = await SharedPreferences.getInstance(); final mealPlanFormat = pref.getString('mealPlanFormat'); return Future.value(MealPlanFormat.values.firstWhere((e) => e.toString() == mealPlanFormat)); } @override Future getPriceCategory() async { - final pref = await SharedPreferences.getInstance(); final priceCategory = pref.getString('priceCategory'); return Future.value(PriceCategory.values.firstWhere((e) => e.toString() == priceCategory)); @@ -95,19 +90,16 @@ class SharedPreferenceAccess implements ILocalStorage { @override Future setClientIdentifier(String identifier) async { - final pref = await SharedPreferences.getInstance(); await pref.setString('clientIdentifier', identifier); } @override - Future setColorScheme(ColorScheme scheme) async { - final pref = await SharedPreferences.getInstance(); + Future setColorScheme(MensaColorScheme scheme) async { await pref.setString('colorScheme', scheme.toString()); } @override Future setFilterPreferences(FilterPreferences filter) async { - final pref = await SharedPreferences.getInstance(); await pref.setStringList('filterCategories', List.of(filter.categories.map((e) => e.toString()))); await pref.setStringList('filterAllergens', List.of(filter.allergens.map((e) => e.toString()))); await pref.setInt('filterPrice', filter.price); @@ -120,26 +112,22 @@ class SharedPreferenceAccess implements ILocalStorage { @override Future setMealPlanFormat(MealPlanFormat format) async { - final pref = await SharedPreferences.getInstance(); await pref.setString('mealPlanFormat', format.toString()); } @override Future setPriceCategory(PriceCategory category) async { - final pref = await SharedPreferences.getInstance(); await pref.setString('priceCategory', category.toString()); } @override Future getCanteen() async { - final pref = await SharedPreferences.getInstance(); final canteen = pref.getString('canteen') ?? ""; return Future.value(canteen); } @override Future setCanteen(String canteen) async { - final pref = await SharedPreferences.getInstance(); await pref.setString('canteen', canteen); } } \ No newline at end of file diff --git a/app/test/model/SharedPreferencesTest.dart b/app/test/model/SharedPreferencesTest.dart new file mode 100644 index 00000000..39d6191e --- /dev/null +++ b/app/test/model/SharedPreferencesTest.dart @@ -0,0 +1,100 @@ + +import 'package:app/model/local_storage/SharedPreferenceAccess.dart'; +import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; +import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; +import 'package:app/view_model/repository/data_classes/filter/Sorting.dart'; +import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; +import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; +import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; +import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +Future main() async { + final Map values = {'counter': 1}; + SharedPreferences.setMockInitialValues(values); + SharedPreferenceAccess pref = SharedPreferenceAccess(await SharedPreferences.getInstance()); + + FilterPreferences filter = FilterPreferences( + categories: [FoodType.vegetarian, FoodType.vegan], + allergens: [Allergen.ca, Allergen.di], + price: 800, + rating: 3, + frequency: [Frequency.rare], + onlyFavorite: true, + sortedBy: Sorting.rating, + ascending: false + ); + late FilterPreferences filterResult; + + test('Client Identifier Test', () async { + String id = "test"; + pref.setClientIdentifier(id); + expect(await pref.getClientIdentifier(), id); + }); + + test('Meal Plan Format Test', () async { + MealPlanFormat format = MealPlanFormat.list; + pref.setMealPlanFormat(format); + expect(await pref.getMealPlanFormat(), format); + }); + + test('Color Scheme Test', () async { + MensaColorScheme scheme = MensaColorScheme.light; + pref.setColorScheme(scheme); + expect(await pref.getColorScheme(), scheme); + }); + + test('Price Category Test', () async { + PriceCategory category = PriceCategory.employee; + pref.setPriceCategory(category); + expect(await pref.getPriceCategory(), category); + }); + + test('Canteen Test', () async { + String canteen = "test"; + pref.setCanteen(canteen); + expect(await pref.getCanteen(), canteen); + }); + + setUp(() async { + pref.setFilterPreferences(filter); + filterResult = await pref.getFilterPreferences(); + }); + + group('FilterPreferences', () { + test('price', () { + expect(filterResult.price, filter.price); + }); + + test('rating', () { + expect(filterResult.rating, filter.rating); + }); + + test('onlyFavorite', () { + expect(filterResult.onlyFavorite, filter.onlyFavorite); + }); + + test('sortedBy', () { + expect(filterResult.sortedBy, filter.sortedBy); + }); + + test('ascending', () { + expect(filterResult.ascending, filter.ascending); + }); + + // test Lists + test('frequency', () { + expect(filterResult.frequency, filter.frequency); + }); + + test('allergens', () { + expect(filterResult.allergens, filter.allergens); + }); + + test('categories', () { + expect(filterResult.categories, filter.categories); + }); + }); +} \ No newline at end of file From afa709f8f55282bf36f3f3d7fde0792fe12c4c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 5 Jul 2023 09:37:29 +0200 Subject: [PATCH 007/184] Spelling --- app/lib/model/local_storage/SharedPreferenceAccess.dart | 8 ++++---- app/test/model/SharedPreferencesTest.dart | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index 27d695c8..005d2141 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -2,7 +2,7 @@ import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; -import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/ColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:app/view_model/repository/interface/ILocalStorage.dart'; @@ -24,9 +24,9 @@ class SharedPreferenceAccess implements ILocalStorage { } @override - Future getColorScheme() async { + Future getColorScheme() async { final colorScheme = pref.getString('colorScheme'); - return Future.value(MensaColorScheme.values.firstWhere((e) => e.toString() == colorScheme)); + return Future.value(ColorScheme.values.firstWhere((e) => e.toString() == colorScheme)); } @override @@ -94,7 +94,7 @@ class SharedPreferenceAccess implements ILocalStorage { } @override - Future setColorScheme(MensaColorScheme scheme) async { + Future setColorScheme(ColorScheme scheme) async { await pref.setString('colorScheme', scheme.toString()); } diff --git a/app/test/model/SharedPreferencesTest.dart b/app/test/model/SharedPreferencesTest.dart index 39d6191e..74ffd67b 100644 --- a/app/test/model/SharedPreferencesTest.dart +++ b/app/test/model/SharedPreferencesTest.dart @@ -6,7 +6,7 @@ import 'package:app/view_model/repository/data_classes/filter/Sorting.dart'; import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; -import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/ColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -41,7 +41,7 @@ Future main() async { }); test('Color Scheme Test', () async { - MensaColorScheme scheme = MensaColorScheme.light; + ColorScheme scheme = ColorScheme.light; pref.setColorScheme(scheme); expect(await pref.getColorScheme(), scheme); }); From 93ce7da4d7c302dcc5da86558fe90c0df6d35ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 5 Jul 2023 13:58:38 +0200 Subject: [PATCH 008/184] documentation --- .../local_storage/SharedPreferenceAccess.dart | 60 ++++++++++--------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index 005d2141..654f68eb 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -12,34 +12,38 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../../view_model/repository/data_classes/filter/Sorting.dart'; import '../../view_model/repository/data_classes/meal/FoodType.dart'; +/// This class accesses the shared preferences and uses it as local storage. class SharedPreferenceAccess implements ILocalStorage { - SharedPreferences pref; + final SharedPreferences _pref; - SharedPreferenceAccess(this.pref); + /// This constructor creates a new instance of the shared preference access. + /// @param _pref The shared preferences. + /// @return A new instance of the shared preference access. + SharedPreferenceAccess(this._pref); @override Future getClientIdentifier() async { - final clientIdentifier = pref.getString('clientIdentifier') ?? ""; + final clientIdentifier = _pref.getString('clientIdentifier') ?? ""; return Future.value(clientIdentifier); } @override Future getColorScheme() async { - final colorScheme = pref.getString('colorScheme'); + final colorScheme = _pref.getString('colorScheme'); return Future.value(ColorScheme.values.firstWhere((e) => e.toString() == colorScheme)); } @override Future getFilterPreferences() async { // get data from shared preferences - final categories = pref.getStringList('filterCategories'); - final allergens = pref.getStringList('filterAllergens'); - final price = pref.getInt('filterPrice'); - final rating = pref.getInt('filterRating'); - final frequency = pref.getStringList('filterFrequency'); - final onlyFavorites = pref.getBool('filterFavorite'); - final sortedBy = pref.getString('filterSort'); - final ascending = pref.getBool('filterSortAscending'); + final categories = _pref.getStringList('filterCategories'); + final allergens = _pref.getStringList('filterAllergens'); + final price = _pref.getInt('filterPrice'); + final rating = _pref.getInt('filterRating'); + final frequency = _pref.getStringList('filterFrequency'); + final onlyFavorites = _pref.getBool('filterFavorite'); + final sortedBy = _pref.getString('filterSort'); + final ascending = _pref.getBool('filterSortAscending'); // convert values to right format List? foodTypeEnum; @@ -77,57 +81,57 @@ class SharedPreferenceAccess implements ILocalStorage { @override Future getMealPlanFormat() async { - final mealPlanFormat = pref.getString('mealPlanFormat'); + final mealPlanFormat = _pref.getString('mealPlanFormat'); return Future.value(MealPlanFormat.values.firstWhere((e) => e.toString() == mealPlanFormat)); } @override Future getPriceCategory() async { - final priceCategory = pref.getString('priceCategory'); + final priceCategory = _pref.getString('priceCategory'); return Future.value(PriceCategory.values.firstWhere((e) => e.toString() == priceCategory)); } @override Future setClientIdentifier(String identifier) async { - await pref.setString('clientIdentifier', identifier); + await _pref.setString('clientIdentifier', identifier); } @override Future setColorScheme(ColorScheme scheme) async { - await pref.setString('colorScheme', scheme.toString()); + await _pref.setString('colorScheme', scheme.toString()); } @override Future setFilterPreferences(FilterPreferences filter) async { - await pref.setStringList('filterCategories', List.of(filter.categories.map((e) => e.toString()))); - await pref.setStringList('filterAllergens', List.of(filter.allergens.map((e) => e.toString()))); - await pref.setInt('filterPrice', filter.price); - await pref.setInt('filterRating', filter.rating); - await pref.setStringList('filterFrequency', List.of(filter.frequency.map((e) => e.toString()))); - await pref.setBool('filterFavorite', filter.onlyFavorite); - await pref.setString('filterSort', filter.sortedBy.toString()); - await pref.setBool('filterSortAscending', filter.ascending); + await _pref.setStringList('filterCategories', List.of(filter.categories.map((e) => e.toString()))); + await _pref.setStringList('filterAllergens', List.of(filter.allergens.map((e) => e.toString()))); + await _pref.setInt('filterPrice', filter.price); + await _pref.setInt('filterRating', filter.rating); + await _pref.setStringList('filterFrequency', List.of(filter.frequency.map((e) => e.toString()))); + await _pref.setBool('filterFavorite', filter.onlyFavorite); + await _pref.setString('filterSort', filter.sortedBy.toString()); + await _pref.setBool('filterSortAscending', filter.ascending); } @override Future setMealPlanFormat(MealPlanFormat format) async { - await pref.setString('mealPlanFormat', format.toString()); + await _pref.setString('mealPlanFormat', format.toString()); } @override Future setPriceCategory(PriceCategory category) async { - await pref.setString('priceCategory', category.toString()); + await _pref.setString('priceCategory', category.toString()); } @override Future getCanteen() async { - final canteen = pref.getString('canteen') ?? ""; + final canteen = _pref.getString('canteen') ?? ""; return Future.value(canteen); } @override Future setCanteen(String canteen) async { - await pref.setString('canteen', canteen); + await _pref.setString('canteen', canteen); } } \ No newline at end of file From 8e1f6991fa58fa8477c9c6a607a8d14cf77c4a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 5 Jul 2023 13:58:46 +0200 Subject: [PATCH 009/184] documentation --- app/test/model/SharedPreferencesTest.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/test/model/SharedPreferencesTest.dart b/app/test/model/SharedPreferencesTest.dart index 74ffd67b..9a0d4b44 100644 --- a/app/test/model/SharedPreferencesTest.dart +++ b/app/test/model/SharedPreferencesTest.dart @@ -11,9 +11,12 @@ import 'package:app/view_model/repository/data_classes/settings/PriceCategory.da import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; +/// This class tests the shared preferences access. Future main() async { + // set test environment final Map values = {'counter': 1}; SharedPreferences.setMockInitialValues(values); + SharedPreferenceAccess pref = SharedPreferenceAccess(await SharedPreferences.getInstance()); FilterPreferences filter = FilterPreferences( @@ -28,41 +31,48 @@ Future main() async { ); late FilterPreferences filterResult; + /// This method tests the access to the client identifier. test('Client Identifier Test', () async { String id = "test"; pref.setClientIdentifier(id); expect(await pref.getClientIdentifier(), id); }); + /// This method test the access to the meal plan format. test('Meal Plan Format Test', () async { MealPlanFormat format = MealPlanFormat.list; pref.setMealPlanFormat(format); expect(await pref.getMealPlanFormat(), format); }); + /// This method tests the access to the color scheme. test('Color Scheme Test', () async { ColorScheme scheme = ColorScheme.light; pref.setColorScheme(scheme); expect(await pref.getColorScheme(), scheme); }); + /// This method tests the access to the price category. test('Price Category Test', () async { PriceCategory category = PriceCategory.employee; pref.setPriceCategory(category); expect(await pref.getPriceCategory(), category); }); + /// This method tests the access to the canteen. test('Canteen Test', () async { String canteen = "test"; pref.setCanteen(canteen); expect(await pref.getCanteen(), canteen); }); + /// This method prepares the access to the filter preferences. setUp(() async { pref.setFilterPreferences(filter); filterResult = await pref.getFilterPreferences(); }); + /// This group tests the access to the filter preferences group('FilterPreferences', () { test('price', () { expect(filterResult.price, filter.price); From bc7dad35f86d7d44f26e5e60d8d1f20c429900e8 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Wed, 5 Jul 2023 16:21:05 +0200 Subject: [PATCH 010/184] first setup for GraphQlServerAccess --- app/.gitignore | 3 + app/README.md | 19 +- app/build.yaml | 7 + .../model/api_server/GraphQlServerAccess.dart | 100 ++++ .../requests/deleteDownvote.graphql | 3 + .../api_server/requests/deleteUpvote.graphql | 3 + .../model/api_server/requests/schema.graphql | 468 ++++++++++++++++++ app/pubspec.lock | 456 ++++++++++++++++- app/pubspec.yaml | 3 + 9 files changed, 1037 insertions(+), 25 deletions(-) create mode 100644 app/build.yaml create mode 100644 app/lib/model/api_server/GraphQlServerAccess.dart create mode 100644 app/lib/model/api_server/requests/deleteDownvote.graphql create mode 100644 app/lib/model/api_server/requests/deleteUpvote.graphql create mode 100644 app/lib/model/api_server/requests/schema.graphql diff --git a/app/.gitignore b/app/.gitignore index 24476c5d..c3b20f85 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -42,3 +42,6 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +# secrets +secret.json diff --git a/app/README.md b/app/README.md index b73cfb0e..eee495ec 100644 --- a/app/README.md +++ b/app/README.md @@ -1,16 +1,9 @@ -# app +# MensaApp-Frontend +Frontend application for viewing and interacting with meal plan data of the canteens of the Studierendenwerk Karlsruhe [^1]. -A new Flutter project. +[^1]: https://www.sw-ka.de/de/hochschulgastronomie/speiseplan/ -## Getting Started +## Building -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +### GraphQL +To generate the dart wrappers from `*.graphql` files run `dart run build_runner build`. \ No newline at end of file diff --git a/app/build.yaml b/app/build.yaml new file mode 100644 index 00000000..2bdc4716 --- /dev/null +++ b/app/build.yaml @@ -0,0 +1,7 @@ +targets: + $default: + builders: + graphql_codegen: + options: + clients: + - graphql_flutter \ No newline at end of file diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart new file mode 100644 index 00000000..bd18fa66 --- /dev/null +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -0,0 +1,100 @@ +import 'package:app/model/api_server/requests/deleteUpvote.graphql.dart'; +import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; + +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; + +import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; + +import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; + +import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; + +import 'package:app/view_model/repository/error_handling/Result.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; + +import '../../view_model/repository/interface/IServerAccess.dart'; +import 'requests/deleteDownvote.graphql.dart'; + +class GraphQlServerAccess implements IServerAccess { + final String _apiKey = const String.fromEnvironment('API_KEY'); + + final GraphQLClient _client = GraphQLClient( + link: HttpLink(const String.fromEnvironment('API_URL')), + cache: GraphQLCache()); + + GraphQlServerAccess._(); + + factory GraphQlServerAccess() { + return GraphQlServerAccess._(); + } + + @override + Future deleteDownvote(ImageData image) async { + // TODO auth + final result = await _client.mutate$RemoveDownvote( + Options$Mutation$RemoveDownvote( + variables: Variables$Mutation$RemoveDownvote(imageId: image.id))); + final parsedData = result.parsedData; + final success = parsedData?.removeDownvote ?? false; + return success; + } + + @override + Future deleteUpvote(ImageData image) async { + // TODO auth + final result = await _client.mutate$RemoveUpvote( + Options$Mutation$RemoveUpvote( + variables: Variables$Mutation$RemoveUpvote(imageId: image.id))); + final parsedData = result.parsedData; + final success = parsedData?.removeUpvote ?? false; + return success; + } + + @override + Future downvoteImage(ImageData image) { + // TODO: implement downvoteImage + throw UnimplementedError(); + } + + @override + Future> getMealFromId(String id) { + // TODO: implement getMealFromId + throw UnimplementedError(); + } + + @override + Future linkImage(String url, Meal meal) { + // TODO: implement linkImage + throw UnimplementedError(); + } + + @override + Future reportImage(ImageData image, ReportCategory reportReason) { + // TODO: implement reportImage + throw UnimplementedError(); + } + + @override + Future>> updateAll() { + // TODO: implement updateAll + throw UnimplementedError(); + } + + @override + Future>> updateCanteen(Canteen canteen, DateTime date) { + // TODO: implement updateCanteen + throw UnimplementedError(); + } + + @override + Future updateMealRating(int rating, Meal meal) { + // TODO: implement updateMealRating + throw UnimplementedError(); + } + + @override + Future upvoteImage(ImageData image) { + // TODO: implement upvoteImage + throw UnimplementedError(); + } +} diff --git a/app/lib/model/api_server/requests/deleteDownvote.graphql b/app/lib/model/api_server/requests/deleteDownvote.graphql new file mode 100644 index 00000000..0963d01d --- /dev/null +++ b/app/lib/model/api_server/requests/deleteDownvote.graphql @@ -0,0 +1,3 @@ +mutation RemoveDownvote($imageId: UUID!) { + removeDownvote(imageId: $imageId ) +} \ No newline at end of file diff --git a/app/lib/model/api_server/requests/deleteUpvote.graphql b/app/lib/model/api_server/requests/deleteUpvote.graphql new file mode 100644 index 00000000..8c825bf5 --- /dev/null +++ b/app/lib/model/api_server/requests/deleteUpvote.graphql @@ -0,0 +1,3 @@ +mutation RemoveUpvote($imageId: UUID!) { + removeUpvote(imageId: $imageId ) +} \ No newline at end of file diff --git a/app/lib/model/api_server/requests/schema.graphql b/app/lib/model/api_server/requests/schema.graphql new file mode 100644 index 00000000..1f085f44 --- /dev/null +++ b/app/lib/model/api_server/requests/schema.graphql @@ -0,0 +1,468 @@ +schema { + query: QueryRoot + mutation: MutationRoot +} + +# This enum lists every possible additive a meal can have. +enum Additive { + # This meal contains colorants. + COLORANT + + # This meal contains preserving agents. + PRESERVING_AGENTS + + # This meal contains antioxidant agents. + ANTIOXIDANT_AGENTS + + # This meal contains flavour enhancers. + FLAVOUR_ENHANCER + + # This meal contains phosphate. + PHOSPHATE + + # This meals surface is waxed. + SURFACE_WAXED + + # This meals contains sulphir. + SULPHUR + + # This meals contains artificially blackened olives. + ARTIFICIALLY_BLACKENED_OLIVES + + # This meals contains sweetener. + SWEETENER + + # This meals can be laxative if overused. + LAXATIVE_IF_OVERUSED + + # This meals contains phenylalanine. + PHENYLALANINE + + # This meals can contain alcohol. + ALCOHOL + + # This meals contains pressed meet. + PRESSED_MEET + + # This meals is glazed with cacao. + GLAZING_WITH_CACAO + + # This meals contains pressed fish. + PRESSED_FISH +} + +# This enum lists every possible allergen a meal can have. +enum Allergen { + # This meal contains cashews. + CA + + # This meal contains spelt and gluten. + DI + + # This meal contains eggs. + EI + + # This meal contains peanuts. + ER + + # This meal contains fish. + FI + + # This meal contains barley and barley gluten. + GE + + # This meal contains oat and oat gluten. + HF + + # This meal contains hazelnuts. + HA + + # This meal contains kamut and kamut gluten. + KA + + # This meal contains crustaceans. + KR + + # This meal contains lupin. + LU + + # This meal contains almonds. + MA + + # This meal contains milk / lactose. + ML + + # This meal contains brazil nuts. + PA + + # This meal contains pecans. + PE + + # This meal contains pistachios. + PI + + # This meal contains macadamia nuts. + QU + + # This meal contains rye and rye gluten. + RO + + # This meal contains sesame. + SA + + # This meal contains celery. + SE + + # This meal contains sulphite. + SF + + # This meal contains mustard. + SN + + # This meal contains soya. + SO + + # This meal contains walnuts. + WA + + # This meal contains wheat and wheat gluten. + WE + + # This meal contains molluscs. + WT + + # This meal contains animal rennet. + LA + + # This meal contains gelatin. + GL +} + +# Information about the provided authentication information. +type AuthInfo { + # My own user identifier. + clientId: UUID! + + # The provided api key identifier (first 10 symbols only!). + apiIdent: String! + + # The provided hash of a request. + hash: String! +} + +type Canteen { + # The id of the canteen. + id: UUID! + + # The name of the canteen. + name: String! + + # Provides the lines of the canteen. + lines: [Line!]! +} + +type Image { + # The id of the image. + id: UUID! + + # The url of the image. + url: String! + + # The rank of the image. Used for determining the order of images to be shown. + rank: Float! + + # The amount of users, who upvoted the image. + upvotes: Int! + + # The amount of users, who downvoted the image. + downvotes: Int! + personalUpvote: Boolean! + + # This attribute specifies whether or not the user downvoted the image. + personalDownvote: Boolean! +} + +type Line { + # The id of the line. + id: UUID! + + # The name of the line. + name: String! + + # Provides the canteen this line belongs to. + canteen: Canteen! + + # Provides the meals offered at this line on a given day. Requires a date. + meals(date: NaiveDate!): [Meal!]! +} + +type Meal { + # The identifier of the main course. + id: UUID! + + # The name of the main course. + name: String! + + # Type of this meal. + # Here the type of meat which is contained in the meal, or whether it is vegetarian or vegan, is specified. + mealType: MealType! + + # The ratings given by the users to the meal. + ratings: Ratings! + + # The prices of the dish each for the four groups of people students, employees, pupils and guests. + price: Price! + + # Some statistics for the meal. + statistics: MealStatistics! + + # Provides the allergens of this meal. + allergens: [Allergen!]! + + # Provides the additives of this meal + additives: [Additive!]! + + # Provides the images belonging to this meal + images: [Image!]! + + # Provides the sides belonging to this meal. + sides: [Side!]! + + # Provides the line this meal is served at. + line: Line! +} + +type MealStatistics { + # The date of the last time the meal was served. + lastServed: NaiveDate + + # The date of the next time the meal will be served. + nextServed: NaiveDate + + # The relative frequency with which the meal is offered. + relativeFrequency: Float! +} + +# This enum lists all the types a meal can be of. +enum MealType { + # This meal is vegan. + VEGAN + + # This meal is vegetarian. + VEGETARIAN + + # This meal contains beef. + BEEF + + # This meal contains beef from regional appropriate animal husbandry. + BEEF_AW + + # This meal contains pork. + PORK + + # This meal contains pork from regional appropriate animal husbandry. + PORK_AW + + # This meal contains fish. + FISH + + # It is unknown whether this meal contains any meat or not. + UNKNOWN +} + +type MutationRoot { + # This mutation adds an image to the specified main dish. + # The user has to be authenticated. + # + # `image_url` is a link to a Flickr image used to get information about it. + # + # If the meal does not exist, or the URL does not lead to Flickr + # or the image is not licenced under a [CC0](https://creativecommons.org/publicdomain/zero/1.0/) licence + # or another error occurred while adding the image an error message will be returned. + # + # If the image was added is successful, `true` is returned. + addImage( + # Id of the meal to link an image to. + mealId: UUID! + + # Flickr url to the image. + imageUrl: String! + ): Boolean! + + # This mutation either adds a rating to the specified main dish (if no such rating existed), or modifies an existing one. + # The user has to be authenticated. + # If the main dish does not exist, or any other error occurs in the process, an error message is returned. + # If the rating was successfully added or changed, 'true' is returned. + setRating( + # Id of the meal to rate to. + mealId: UUID! + + # The new rating of the main dish. + rating: Int! + ): Boolean! + + # This mutation adds an upvote to the specified image. + # The user has to be authenticated. + # If the image does not exist, or any other error occurs in the process, an error message is returned. + # If the upvote was successfully added, 'true' is returned. + addUpvote( + # Id of the image to add the upvote to. + imageId: UUID! + ): Boolean! + + # This mutation removes the upvote from the specified image. + # The user has to be authenticated. + # If the image does not exist, or any other error occurs in the process, an error message is returned. + # If the upvote was successfully removed, 'true' is returned. + removeUpvote( + # Id of the image to remove the upvote from. + imageId: UUID! + ): Boolean! + + # This mutation adds a downvote to the specified image. + # The user has to be authenticated. + # If the image does not exist, or any other error occurs in the process, an error message is returned. + # If the downvote was successfully added, 'true' is returned. + addDownvote( + # Id of the image to add the downvote to. + imageId: UUID! + ): Boolean! + + # This mutation removes the downvote from the specified image. + # The user has to be authenticated. + # If the image does not exist, or any other error occurs in the process, an error message is returned. + # If the downvote was successfully removed, 'true' is returned. + removeDownvote( + # Id of the image to remove the downvote from. + imageId: UUID! + ): Boolean! + + # This mutation adds a report to the specified image. + # The user has to be authenticated. + # If the image does not exist, or any other error occurs in the process, an error message is returned. + # If the report was successfully added, 'true' is returned. + reportImage( + # Id of the image to report. + imageId: UUID! + + # The reason for reporting the image. + reason: ReportReason! + ): Boolean! +} + +# ISO 8601 calendar date without timezone. +# Format: %Y-%m-%d +# +# # Examples +# +# * `1994-11-13` +# * `2000-02-24` +scalar NaiveDate + +type Price { + # The price of the meal for students. + student: Int! + + # The price of the meal for employees. + employee: Int! + + # The price of the meal for guests. + guest: Int! + + # The price of the meal for pupils. + pupil: Int! +} + +type QueryRoot { + # This query returns a list of all available canteens. + getCanteens: [Canteen!]! + + # This query returns the canteen identified by the specified ID. + # If there is no canteen with the specified ID, a null value is returned. + getCanteen( + # Id of the canteen to get. + canteenId: UUID! + ): Canteen + + # This query returns the main dish (including its price and sides) identified by the specified ID, the line and the date. + # If the main dish does not exist, or is not served at the specified line on the specified day, a null value is returned. + getMeal( + # Id of the meal to get. + mealId: UUID! + + # Id of the line at which the meal to get is to be offered. + lineId: UUID! + + # Date of the day on which the meal to get is to be offered. + date: NaiveDate! + ): Meal + + # This query returns the version of this API schema. It can also be used for health checks. + apiVersion: String! + + # This query returns the in the `Authorization` request header provided authentication information. + # It is intended for debugging purposes to check whether these information got passed correctly. + getMyAuth: AuthInfo +} + +type Ratings { + # The average rating of this meal. + averageRating: Float! + + # The total number of ratings for this meal. + ratingsCount: Int! + + # Provides this user's rating for the meal. + personalRating: Int +} + +# This enum lists all the predetermined reasons a image can be reported for. +enum ReportReason { + # This picture shows offensive content. + OFFENSIVE + + # This picture is an advert. + ADVERT + + # This picture does not show a meal. + NO_MEAL + + # This picture shows the wrong meal. + WRONG_MEAL + + # This picture violates my rights. + VIOLATES_RIGHTS + + # This picture should be removed for some other reason. + OTHER +} + +type Side { + # The id of the side + id: UUID! + + # The name of the side + name: String! + + # Here the type of meat which is contained in the side, or whether it is vegetarian or vegan, is specified. + mealType: MealType! + + # The price of the side + price: Price! + + # Provides the allergens of this side + allergens: [Allergen!]! + + # Provides the additives of this side + additives: [Additive!]! +} + +# A UUID is a unique 128-bit number, stored as 16 octets. UUIDs are parsed as +# Strings within GraphQL. UUIDs are used to assign unique identifiers to +# entities without requiring a central allocating authority. +# +# # References +# +# * [Wikipedia: Universally Unique Identifier](http://en.wikipedia.org/wiki/Universally_unique_identifier) +# * [RFC4122: A Universally Unique IDentifier (UUID) URN Namespace](http://tools.ietf.org/html/rfc4122) +scalar UUID diff --git a/app/pubspec.lock b/app/pubspec.lock index 0c4deba5..445351f8 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + url: "https://pub.dev" + source: hosted + version: "61.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + url: "https://pub.dev" + source: hosted + version: "5.13.0" args: dependency: transitive description: @@ -25,6 +41,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + url: "https://pub.dev" + source: hosted + version: "2.4.6" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" + url: "https://pub.dev" + source: hosted + version: "7.2.10" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" + url: "https://pub.dev" + source: hosted + version: "8.6.1" characters: dependency: transitive description: @@ -33,6 +113,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" clock: dependency: transitive description: @@ -41,14 +129,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189" + url: "https://pub.dev" + source: hosted + version: "4.5.0" collection: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" + source: hosted + version: "1.17.2" + connectivity_plus: + dependency: transitive + description: + name: connectivity_plus + sha256: b74247fad72c171381dbe700ca17da24deac637ab6d43c343b42867acb95c991 + url: "https://pub.dev" + source: hosted + version: "3.0.6" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.2.4" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -57,6 +185,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + dbus: + dependency: transitive + description: + name: dbus + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + url: "https://pub.dev" + source: hosted + version: "0.7.8" fake_async: dependency: transitive description: @@ -81,11 +225,27 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_hooks: + dependency: "direct main" + description: + name: flutter_hooks + sha256: "6a126f703b89499818d73305e4ce1e3de33b4ae1c5512e3b8eab4b986f46774c" + url: "https://pub.dev" + source: hosted + version: "0.18.6" flutter_i18n: dependency: "direct main" description: @@ -125,6 +285,126 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + gql: + dependency: transitive + description: + name: gql + sha256: "998304fbb88a3956cfea10cd27a56f8e5d4b3bc110f03c952c18a9310774e8bb" + url: "https://pub.dev" + source: hosted + version: "0.14.0" + gql_code_builder: + dependency: transitive + description: + name: gql_code_builder + sha256: "66fc942416d9703e59ac732b2cb4e8442261fc90e1fcf89881b5b796d0803d02" + url: "https://pub.dev" + source: hosted + version: "0.7.1" + gql_dedupe_link: + dependency: transitive + description: + name: gql_dedupe_link + sha256: "89681048cf956348e865da872a40081499b8c087fc84dd4d4b9c134bd70d27b3" + url: "https://pub.dev" + source: hosted + version: "2.0.3+1" + gql_error_link: + dependency: transitive + description: + name: gql_error_link + sha256: e7bfdd2b6232f3e15861cd96c2ad6b7c9c94693843b3dea18295136a5fb5b534 + url: "https://pub.dev" + source: hosted + version: "0.2.3+1" + gql_exec: + dependency: transitive + description: + name: gql_exec + sha256: "0d1fdb2e4154efbfc1dcf3f35ec36d19c8428ff0d560eb4c45b354f8f871dc50" + url: "https://pub.dev" + source: hosted + version: "0.4.3" + gql_http_link: + dependency: transitive + description: + name: gql_http_link + sha256: "89ef87b32947acf4189f564c095f1148b0ab9bb9996fe518716dbad66708b834" + url: "https://pub.dev" + source: hosted + version: "0.4.5" + gql_link: + dependency: transitive + description: + name: gql_link + sha256: f7973279126bc922d465c4f4da6ed93d187085e597b3480f5e14e74d28fe14bd + url: "https://pub.dev" + source: hosted + version: "0.5.1" + gql_transform_link: + dependency: transitive + description: + name: gql_transform_link + sha256: b1735a9a92d25a92960002a8b40dfaede95ec1e5ed848906125d69efd878661f + url: "https://pub.dev" + source: hosted + version: "0.2.2+1" + graphql: + dependency: "direct main" + description: + name: graphql + sha256: b061201579040e9548cec2bae17bbdea0ab30666cb4e7ba48b9675f14d982199 + url: "https://pub.dev" + source: hosted + version: "5.1.3" + graphql_codegen: + dependency: "direct dev" + description: + name: graphql_codegen + sha256: "572cfa1ca69050bf8763e8b9e151df6240f08b41e961916a2f88882b4ff897f5" + url: "https://pub.dev" + source: hosted + version: "0.12.2" + graphql_flutter: + dependency: "direct main" + description: + name: graphql_flutter + sha256: "06059ac9e8417c71582f05e28a59b1416d43959d34a6a0d9565341e3a362e117" + url: "https://pub.dev" + source: hosted + version: "5.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + hive: + dependency: transitive + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" http: dependency: transitive description: @@ -133,6 +413,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.13.6" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" http_parser: dependency: transitive description: @@ -145,10 +433,18 @@ packages: dependency: "direct main" description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" js: dependency: transitive description: @@ -157,6 +453,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" lints: dependency: transitive description: @@ -177,10 +481,10 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: @@ -197,6 +501,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + normalize: + dependency: transitive + description: + name: normalize + sha256: baf8caf2d8b745af5737cca6c24f7fe3cf3158897fdbcde9a909b9c8d3e2e5af + url: "https://pub.dev" + source: hosted + version: "0.7.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -285,6 +621,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" process: dependency: transitive description: @@ -293,6 +637,54 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.4" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" sky_engine: dependency: transitive description: flutter @@ -302,10 +694,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -318,10 +710,18 @@ packages: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -342,10 +742,18 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.1" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" toml: dependency: transitive description: @@ -362,6 +770,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" + source: hosted + version: "3.0.7" vector_graphics: dependency: transitive description: @@ -394,6 +810,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "3a969ddcc204a3e34e863d204b29c0752716f78b6f9cc8235083208d268a4ccd" + url: "https://pub.dev" + source: hosted + version: "2.2.0" win32: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index b29ec1a8..a5a14e41 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: sdk: flutter flutter_svg: ^2.0.7 + graphql_flutter: ^5.1.1 dev_dependencies: flutter_test: @@ -55,6 +56,8 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^2.0.0 + graphql_codegen: ^0.12.2 + build_runner: ^2.4.6 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 7c5db63671de861c99464a2ed0d6898f16aa919d Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Wed, 5 Jul 2023 16:45:23 +0200 Subject: [PATCH 011/184] added tests --- app/pubspec.lock | 84 ++++++++++++++++++- app/pubspec.yaml | 1 + .../api_server/GraphQlServerAccess_test.dart | 15 ++++ 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 app/test/model/api_server/GraphQlServerAccess_test.dart diff --git a/app/pubspec.lock b/app/pubspec.lock index 445351f8..0b29f06c 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -169,6 +169,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + url: "https://pub.dev" + source: hosted + version: "1.6.3" crypto: dependency: transitive description: @@ -239,7 +247,7 @@ packages: source: sdk version: "0.0.0" flutter_hooks: - dependency: "direct main" + dependency: transitive description: name: flutter_hooks sha256: "6a126f703b89499818d73305e4ce1e3de33b4ae1c5512e3b8eab4b986f46774c" @@ -366,7 +374,7 @@ packages: source: hosted version: "0.2.2+1" graphql: - dependency: "direct main" + dependency: transitive description: name: graphql sha256: b061201579040e9548cec2bae17bbdea0ab30666cb4e7ba48b9675f14d982199 @@ -517,6 +525,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" normalize: dependency: transitive description: @@ -677,6 +693,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" shelf_web_socket: dependency: transitive description: @@ -690,6 +722,22 @@ packages: description: flutter source: sdk version: "0.0.99" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" source_span: dependency: transitive description: @@ -738,6 +786,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + sha256: "67ec5684c7a19b2aba91d2831f3d305a6fd8e1504629c5818f8d64478abf4f38" + url: "https://pub.dev" + source: hosted + version: "1.24.4" test_api: dependency: transitive description: @@ -746,6 +802,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + test_core: + dependency: transitive + description: + name: test_core + sha256: "6b753899253c38ca0523bb0eccff3934ec83d011705dae717c61ecf209e333c9" + url: "https://pub.dev" + source: hosted + version: "0.5.4" timing: dependency: transitive description: @@ -810,6 +874,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b8c67f5fa3897b122cf60fe9ff314f7b0ef71eab25c5f8b771480bc338f48823 + url: "https://pub.dev" + source: hosted + version: "11.7.2" watcher: dependency: transitive description: @@ -826,6 +898,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + url: "https://pub.dev" + source: hosted + version: "1.2.0" win32: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index a5a14e41..f82a9065 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -58,6 +58,7 @@ dev_dependencies: flutter_lints: ^2.0.0 graphql_codegen: ^0.12.2 build_runner: ^2.4.6 + test: ^1.24.4 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart new file mode 100644 index 00000000..4eb9fc28 --- /dev/null +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -0,0 +1,15 @@ + +import 'package:app/model/api_server/GraphQlServerAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; +import 'package:test/test.dart'; + +void main() async { + final GraphQlServerAccess serverAccess = GraphQlServerAccess(); + + test('graphql', () async { + var deleted = await serverAccess.deleteDownvote(ImageData(id: "1234", url: "url", imageRank: 0, positiveRating: 0, negativeRating: 0)); + expect(deleted, true); + }); + + +} \ No newline at end of file From 10e707b9070b8ee5394f10d4c269db0c590614a1 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Wed, 5 Jul 2023 16:55:19 +0200 Subject: [PATCH 012/184] client id in constructor --- app/lib/model/api_server/GraphQlServerAccess.dart | 7 ++++--- app/test/model/api_server/GraphQlServerAccess_test.dart | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index bd18fa66..6f2f0f41 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -21,11 +21,12 @@ class GraphQlServerAccess implements IServerAccess { final GraphQLClient _client = GraphQLClient( link: HttpLink(const String.fromEnvironment('API_URL')), cache: GraphQLCache()); + final String _clientId; - GraphQlServerAccess._(); + GraphQlServerAccess._(this._clientId); - factory GraphQlServerAccess() { - return GraphQlServerAccess._(); + factory GraphQlServerAccess(String clientId) { + return GraphQlServerAccess._(clientId); } @override diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index 4eb9fc28..bc16b318 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -4,7 +4,7 @@ import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:test/test.dart'; void main() async { - final GraphQlServerAccess serverAccess = GraphQlServerAccess(); + final GraphQlServerAccess serverAccess = GraphQlServerAccess("1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); test('graphql', () async { var deleted = await serverAccess.deleteDownvote(ImageData(id: "1234", url: "url", imageRank: 0, positiveRating: 0, negativeRating: 0)); From 995978c22d449d1be7f9c20474c8bbe4bd877c09 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Thu, 6 Jul 2023 08:39:55 +0200 Subject: [PATCH 013/184] graphlq generated files --- .../requests/deleteDownvote.graphql.dart | 478 ++++++++++++++++++ .../requests/deleteUpvote.graphql.dart | 471 +++++++++++++++++ .../api_server/requests/schema.graphql.dart | 358 +++++++++++++ app/pubspec.lock | 12 +- app/secret.example.json | 4 + app/test/widget_test.dart | 30 -- 6 files changed, 1317 insertions(+), 36 deletions(-) create mode 100644 app/lib/model/api_server/requests/deleteDownvote.graphql.dart create mode 100644 app/lib/model/api_server/requests/deleteUpvote.graphql.dart create mode 100644 app/lib/model/api_server/requests/schema.graphql.dart create mode 100644 app/secret.example.json delete mode 100644 app/test/widget_test.dart diff --git a/app/lib/model/api_server/requests/deleteDownvote.graphql.dart b/app/lib/model/api_server/requests/deleteDownvote.graphql.dart new file mode 100644 index 00000000..e9fb75fb --- /dev/null +++ b/app/lib/model/api_server/requests/deleteDownvote.graphql.dart @@ -0,0 +1,478 @@ +import 'dart:async'; +import 'package:flutter/widgets.dart' as widgets; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; + +class Variables$Mutation$RemoveDownvote { + factory Variables$Mutation$RemoveDownvote({required String imageId}) => + Variables$Mutation$RemoveDownvote._({ + r'imageId': imageId, + }); + + Variables$Mutation$RemoveDownvote._(this._$data); + + factory Variables$Mutation$RemoveDownvote.fromJson( + Map data) { + final result$data = {}; + final l$imageId = data['imageId']; + result$data['imageId'] = (l$imageId as String); + return Variables$Mutation$RemoveDownvote._(result$data); + } + + Map _$data; + + String get imageId => (_$data['imageId'] as String); + Map toJson() { + final result$data = {}; + final l$imageId = imageId; + result$data['imageId'] = l$imageId; + return result$data; + } + + CopyWith$Variables$Mutation$RemoveDownvote + get copyWith => CopyWith$Variables$Mutation$RemoveDownvote( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$RemoveDownvote) || + runtimeType != other.runtimeType) { + return false; + } + final l$imageId = imageId; + final lOther$imageId = other.imageId; + if (l$imageId != lOther$imageId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$imageId = imageId; + return Object.hashAll([l$imageId]); + } +} + +abstract class CopyWith$Variables$Mutation$RemoveDownvote { + factory CopyWith$Variables$Mutation$RemoveDownvote( + Variables$Mutation$RemoveDownvote instance, + TRes Function(Variables$Mutation$RemoveDownvote) then, + ) = _CopyWithImpl$Variables$Mutation$RemoveDownvote; + + factory CopyWith$Variables$Mutation$RemoveDownvote.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$RemoveDownvote; + + TRes call({String? imageId}); +} + +class _CopyWithImpl$Variables$Mutation$RemoveDownvote + implements CopyWith$Variables$Mutation$RemoveDownvote { + _CopyWithImpl$Variables$Mutation$RemoveDownvote( + this._instance, + this._then, + ); + + final Variables$Mutation$RemoveDownvote _instance; + + final TRes Function(Variables$Mutation$RemoveDownvote) _then; + + static const _undefined = {}; + + TRes call({Object? imageId = _undefined}) => + _then(Variables$Mutation$RemoveDownvote._({ + ..._instance._$data, + if (imageId != _undefined && imageId != null) + 'imageId': (imageId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$RemoveDownvote + implements CopyWith$Variables$Mutation$RemoveDownvote { + _CopyWithStubImpl$Variables$Mutation$RemoveDownvote(this._res); + + TRes _res; + + call({String? imageId}) => _res; +} + +class Mutation$RemoveDownvote { + Mutation$RemoveDownvote({ + required this.removeDownvote, + this.$__typename = 'MutationRoot', + }); + + factory Mutation$RemoveDownvote.fromJson(Map json) { + final l$removeDownvote = json['removeDownvote']; + final l$$__typename = json['__typename']; + return Mutation$RemoveDownvote( + removeDownvote: (l$removeDownvote as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool removeDownvote; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$removeDownvote = removeDownvote; + _resultData['removeDownvote'] = l$removeDownvote; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$removeDownvote = removeDownvote; + final l$$__typename = $__typename; + return Object.hashAll([ + l$removeDownvote, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RemoveDownvote) || + runtimeType != other.runtimeType) { + return false; + } + final l$removeDownvote = removeDownvote; + final lOther$removeDownvote = other.removeDownvote; + if (l$removeDownvote != lOther$removeDownvote) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RemoveDownvote on Mutation$RemoveDownvote { + CopyWith$Mutation$RemoveDownvote get copyWith => + CopyWith$Mutation$RemoveDownvote( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RemoveDownvote { + factory CopyWith$Mutation$RemoveDownvote( + Mutation$RemoveDownvote instance, + TRes Function(Mutation$RemoveDownvote) then, + ) = _CopyWithImpl$Mutation$RemoveDownvote; + + factory CopyWith$Mutation$RemoveDownvote.stub(TRes res) = + _CopyWithStubImpl$Mutation$RemoveDownvote; + + TRes call({ + bool? removeDownvote, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$RemoveDownvote + implements CopyWith$Mutation$RemoveDownvote { + _CopyWithImpl$Mutation$RemoveDownvote( + this._instance, + this._then, + ); + + final Mutation$RemoveDownvote _instance; + + final TRes Function(Mutation$RemoveDownvote) _then; + + static const _undefined = {}; + + TRes call({ + Object? removeDownvote = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RemoveDownvote( + removeDownvote: removeDownvote == _undefined || removeDownvote == null + ? _instance.removeDownvote + : (removeDownvote as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$RemoveDownvote + implements CopyWith$Mutation$RemoveDownvote { + _CopyWithStubImpl$Mutation$RemoveDownvote(this._res); + + TRes _res; + + call({ + bool? removeDownvote, + String? $__typename, + }) => + _res; +} + +const documentNodeMutationRemoveDownvote = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RemoveDownvote'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'imageId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'removeDownvote'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'imageId'), + value: VariableNode(name: NameNode(value: 'imageId')), + ) + ], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Mutation$RemoveDownvote _parserFn$Mutation$RemoveDownvote( + Map data) => + Mutation$RemoveDownvote.fromJson(data); +typedef OnMutationCompleted$Mutation$RemoveDownvote = FutureOr Function( + Map?, + Mutation$RemoveDownvote?, +); + +class Options$Mutation$RemoveDownvote + extends graphql.MutationOptions { + Options$Mutation$RemoveDownvote({ + String? operationName, + required Variables$Mutation$RemoveDownvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveDownvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RemoveDownvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$RemoveDownvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRemoveDownvote, + parserFn: _parserFn$Mutation$RemoveDownvote, + ); + + final OnMutationCompleted$Mutation$RemoveDownvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RemoveDownvote + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RemoveDownvote({ + String? operationName, + required Variables$Mutation$RemoveDownvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveDownvote? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRemoveDownvote, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RemoveDownvote, + ); +} + +extension ClientExtension$Mutation$RemoveDownvote on graphql.GraphQLClient { + Future> mutate$RemoveDownvote( + Options$Mutation$RemoveDownvote options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$RemoveDownvote( + WatchOptions$Mutation$RemoveDownvote options) => + this.watchMutation(options); +} + +class Mutation$RemoveDownvote$HookResult { + Mutation$RemoveDownvote$HookResult( + this.runMutation, + this.result, + ); + + final RunMutation$Mutation$RemoveDownvote runMutation; + + final graphql.QueryResult result; +} + +Mutation$RemoveDownvote$HookResult useMutation$RemoveDownvote( + [WidgetOptions$Mutation$RemoveDownvote? options]) { + final result = graphql_flutter + .useMutation(options ?? WidgetOptions$Mutation$RemoveDownvote()); + return Mutation$RemoveDownvote$HookResult( + (variables, {optimisticResult, typedOptimisticResult}) => + result.runMutation( + variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result.result, + ); +} + +graphql.ObservableQuery + useWatchMutation$RemoveDownvote( + WatchOptions$Mutation$RemoveDownvote options) => + graphql_flutter.useWatchMutation(options); + +class WidgetOptions$Mutation$RemoveDownvote + extends graphql.MutationOptions { + WidgetOptions$Mutation$RemoveDownvote({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveDownvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RemoveDownvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$RemoveDownvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRemoveDownvote, + parserFn: _parserFn$Mutation$RemoveDownvote, + ); + + final OnMutationCompleted$Mutation$RemoveDownvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +typedef RunMutation$Mutation$RemoveDownvote + = graphql.MultiSourceResult Function( + Variables$Mutation$RemoveDownvote, { + Object? optimisticResult, + Mutation$RemoveDownvote? typedOptimisticResult, +}); +typedef Builder$Mutation$RemoveDownvote = widgets.Widget Function( + RunMutation$Mutation$RemoveDownvote, + graphql.QueryResult?, +); + +class Mutation$RemoveDownvote$Widget + extends graphql_flutter.Mutation { + Mutation$RemoveDownvote$Widget({ + widgets.Key? key, + WidgetOptions$Mutation$RemoveDownvote? options, + required Builder$Mutation$RemoveDownvote builder, + }) : super( + key: key, + options: options ?? WidgetOptions$Mutation$RemoveDownvote(), + builder: ( + run, + result, + ) => + builder( + ( + variables, { + optimisticResult, + typedOptimisticResult, + }) => + run( + variables.toJson(), + optimisticResult: + optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result, + ), + ); +} diff --git a/app/lib/model/api_server/requests/deleteUpvote.graphql.dart b/app/lib/model/api_server/requests/deleteUpvote.graphql.dart new file mode 100644 index 00000000..d53b6e76 --- /dev/null +++ b/app/lib/model/api_server/requests/deleteUpvote.graphql.dart @@ -0,0 +1,471 @@ +import 'dart:async'; +import 'package:flutter/widgets.dart' as widgets; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; + +class Variables$Mutation$RemoveUpvote { + factory Variables$Mutation$RemoveUpvote({required String imageId}) => + Variables$Mutation$RemoveUpvote._({ + r'imageId': imageId, + }); + + Variables$Mutation$RemoveUpvote._(this._$data); + + factory Variables$Mutation$RemoveUpvote.fromJson(Map data) { + final result$data = {}; + final l$imageId = data['imageId']; + result$data['imageId'] = (l$imageId as String); + return Variables$Mutation$RemoveUpvote._(result$data); + } + + Map _$data; + + String get imageId => (_$data['imageId'] as String); + Map toJson() { + final result$data = {}; + final l$imageId = imageId; + result$data['imageId'] = l$imageId; + return result$data; + } + + CopyWith$Variables$Mutation$RemoveUpvote + get copyWith => CopyWith$Variables$Mutation$RemoveUpvote( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$RemoveUpvote) || + runtimeType != other.runtimeType) { + return false; + } + final l$imageId = imageId; + final lOther$imageId = other.imageId; + if (l$imageId != lOther$imageId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$imageId = imageId; + return Object.hashAll([l$imageId]); + } +} + +abstract class CopyWith$Variables$Mutation$RemoveUpvote { + factory CopyWith$Variables$Mutation$RemoveUpvote( + Variables$Mutation$RemoveUpvote instance, + TRes Function(Variables$Mutation$RemoveUpvote) then, + ) = _CopyWithImpl$Variables$Mutation$RemoveUpvote; + + factory CopyWith$Variables$Mutation$RemoveUpvote.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$RemoveUpvote; + + TRes call({String? imageId}); +} + +class _CopyWithImpl$Variables$Mutation$RemoveUpvote + implements CopyWith$Variables$Mutation$RemoveUpvote { + _CopyWithImpl$Variables$Mutation$RemoveUpvote( + this._instance, + this._then, + ); + + final Variables$Mutation$RemoveUpvote _instance; + + final TRes Function(Variables$Mutation$RemoveUpvote) _then; + + static const _undefined = {}; + + TRes call({Object? imageId = _undefined}) => + _then(Variables$Mutation$RemoveUpvote._({ + ..._instance._$data, + if (imageId != _undefined && imageId != null) + 'imageId': (imageId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$RemoveUpvote + implements CopyWith$Variables$Mutation$RemoveUpvote { + _CopyWithStubImpl$Variables$Mutation$RemoveUpvote(this._res); + + TRes _res; + + call({String? imageId}) => _res; +} + +class Mutation$RemoveUpvote { + Mutation$RemoveUpvote({ + required this.removeUpvote, + this.$__typename = 'MutationRoot', + }); + + factory Mutation$RemoveUpvote.fromJson(Map json) { + final l$removeUpvote = json['removeUpvote']; + final l$$__typename = json['__typename']; + return Mutation$RemoveUpvote( + removeUpvote: (l$removeUpvote as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool removeUpvote; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$removeUpvote = removeUpvote; + _resultData['removeUpvote'] = l$removeUpvote; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$removeUpvote = removeUpvote; + final l$$__typename = $__typename; + return Object.hashAll([ + l$removeUpvote, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RemoveUpvote) || runtimeType != other.runtimeType) { + return false; + } + final l$removeUpvote = removeUpvote; + final lOther$removeUpvote = other.removeUpvote; + if (l$removeUpvote != lOther$removeUpvote) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RemoveUpvote on Mutation$RemoveUpvote { + CopyWith$Mutation$RemoveUpvote get copyWith => + CopyWith$Mutation$RemoveUpvote( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RemoveUpvote { + factory CopyWith$Mutation$RemoveUpvote( + Mutation$RemoveUpvote instance, + TRes Function(Mutation$RemoveUpvote) then, + ) = _CopyWithImpl$Mutation$RemoveUpvote; + + factory CopyWith$Mutation$RemoveUpvote.stub(TRes res) = + _CopyWithStubImpl$Mutation$RemoveUpvote; + + TRes call({ + bool? removeUpvote, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$RemoveUpvote + implements CopyWith$Mutation$RemoveUpvote { + _CopyWithImpl$Mutation$RemoveUpvote( + this._instance, + this._then, + ); + + final Mutation$RemoveUpvote _instance; + + final TRes Function(Mutation$RemoveUpvote) _then; + + static const _undefined = {}; + + TRes call({ + Object? removeUpvote = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RemoveUpvote( + removeUpvote: removeUpvote == _undefined || removeUpvote == null + ? _instance.removeUpvote + : (removeUpvote as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$RemoveUpvote + implements CopyWith$Mutation$RemoveUpvote { + _CopyWithStubImpl$Mutation$RemoveUpvote(this._res); + + TRes _res; + + call({ + bool? removeUpvote, + String? $__typename, + }) => + _res; +} + +const documentNodeMutationRemoveUpvote = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RemoveUpvote'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'imageId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'removeUpvote'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'imageId'), + value: VariableNode(name: NameNode(value: 'imageId')), + ) + ], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Mutation$RemoveUpvote _parserFn$Mutation$RemoveUpvote( + Map data) => + Mutation$RemoveUpvote.fromJson(data); +typedef OnMutationCompleted$Mutation$RemoveUpvote = FutureOr Function( + Map?, + Mutation$RemoveUpvote?, +); + +class Options$Mutation$RemoveUpvote + extends graphql.MutationOptions { + Options$Mutation$RemoveUpvote({ + String? operationName, + required Variables$Mutation$RemoveUpvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveUpvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RemoveUpvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$RemoveUpvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRemoveUpvote, + parserFn: _parserFn$Mutation$RemoveUpvote, + ); + + final OnMutationCompleted$Mutation$RemoveUpvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RemoveUpvote + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RemoveUpvote({ + String? operationName, + required Variables$Mutation$RemoveUpvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveUpvote? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRemoveUpvote, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RemoveUpvote, + ); +} + +extension ClientExtension$Mutation$RemoveUpvote on graphql.GraphQLClient { + Future> mutate$RemoveUpvote( + Options$Mutation$RemoveUpvote options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$RemoveUpvote( + WatchOptions$Mutation$RemoveUpvote options) => + this.watchMutation(options); +} + +class Mutation$RemoveUpvote$HookResult { + Mutation$RemoveUpvote$HookResult( + this.runMutation, + this.result, + ); + + final RunMutation$Mutation$RemoveUpvote runMutation; + + final graphql.QueryResult result; +} + +Mutation$RemoveUpvote$HookResult useMutation$RemoveUpvote( + [WidgetOptions$Mutation$RemoveUpvote? options]) { + final result = graphql_flutter + .useMutation(options ?? WidgetOptions$Mutation$RemoveUpvote()); + return Mutation$RemoveUpvote$HookResult( + (variables, {optimisticResult, typedOptimisticResult}) => + result.runMutation( + variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result.result, + ); +} + +graphql.ObservableQuery useWatchMutation$RemoveUpvote( + WatchOptions$Mutation$RemoveUpvote options) => + graphql_flutter.useWatchMutation(options); + +class WidgetOptions$Mutation$RemoveUpvote + extends graphql.MutationOptions { + WidgetOptions$Mutation$RemoveUpvote({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveUpvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RemoveUpvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$RemoveUpvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRemoveUpvote, + parserFn: _parserFn$Mutation$RemoveUpvote, + ); + + final OnMutationCompleted$Mutation$RemoveUpvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +typedef RunMutation$Mutation$RemoveUpvote + = graphql.MultiSourceResult Function( + Variables$Mutation$RemoveUpvote, { + Object? optimisticResult, + Mutation$RemoveUpvote? typedOptimisticResult, +}); +typedef Builder$Mutation$RemoveUpvote = widgets.Widget Function( + RunMutation$Mutation$RemoveUpvote, + graphql.QueryResult?, +); + +class Mutation$RemoveUpvote$Widget + extends graphql_flutter.Mutation { + Mutation$RemoveUpvote$Widget({ + widgets.Key? key, + WidgetOptions$Mutation$RemoveUpvote? options, + required Builder$Mutation$RemoveUpvote builder, + }) : super( + key: key, + options: options ?? WidgetOptions$Mutation$RemoveUpvote(), + builder: ( + run, + result, + ) => + builder( + ( + variables, { + optimisticResult, + typedOptimisticResult, + }) => + run( + variables.toJson(), + optimisticResult: + optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result, + ), + ); +} diff --git a/app/lib/model/api_server/requests/schema.graphql.dart b/app/lib/model/api_server/requests/schema.graphql.dart new file mode 100644 index 00000000..a5cba13f --- /dev/null +++ b/app/lib/model/api_server/requests/schema.graphql.dart @@ -0,0 +1,358 @@ +enum Enum$Additive { + COLORANT, + PRESERVING_AGENTS, + ANTIOXIDANT_AGENTS, + FLAVOUR_ENHANCER, + PHOSPHATE, + SURFACE_WAXED, + SULPHUR, + ARTIFICIALLY_BLACKENED_OLIVES, + SWEETENER, + LAXATIVE_IF_OVERUSED, + PHENYLALANINE, + ALCOHOL, + PRESSED_MEET, + GLAZING_WITH_CACAO, + PRESSED_FISH, + $unknown +} + +String toJson$Enum$Additive(Enum$Additive e) { + switch (e) { + case Enum$Additive.COLORANT: + return r'COLORANT'; + case Enum$Additive.PRESERVING_AGENTS: + return r'PRESERVING_AGENTS'; + case Enum$Additive.ANTIOXIDANT_AGENTS: + return r'ANTIOXIDANT_AGENTS'; + case Enum$Additive.FLAVOUR_ENHANCER: + return r'FLAVOUR_ENHANCER'; + case Enum$Additive.PHOSPHATE: + return r'PHOSPHATE'; + case Enum$Additive.SURFACE_WAXED: + return r'SURFACE_WAXED'; + case Enum$Additive.SULPHUR: + return r'SULPHUR'; + case Enum$Additive.ARTIFICIALLY_BLACKENED_OLIVES: + return r'ARTIFICIALLY_BLACKENED_OLIVES'; + case Enum$Additive.SWEETENER: + return r'SWEETENER'; + case Enum$Additive.LAXATIVE_IF_OVERUSED: + return r'LAXATIVE_IF_OVERUSED'; + case Enum$Additive.PHENYLALANINE: + return r'PHENYLALANINE'; + case Enum$Additive.ALCOHOL: + return r'ALCOHOL'; + case Enum$Additive.PRESSED_MEET: + return r'PRESSED_MEET'; + case Enum$Additive.GLAZING_WITH_CACAO: + return r'GLAZING_WITH_CACAO'; + case Enum$Additive.PRESSED_FISH: + return r'PRESSED_FISH'; + case Enum$Additive.$unknown: + return r'$unknown'; + } +} + +Enum$Additive fromJson$Enum$Additive(String value) { + switch (value) { + case r'COLORANT': + return Enum$Additive.COLORANT; + case r'PRESERVING_AGENTS': + return Enum$Additive.PRESERVING_AGENTS; + case r'ANTIOXIDANT_AGENTS': + return Enum$Additive.ANTIOXIDANT_AGENTS; + case r'FLAVOUR_ENHANCER': + return Enum$Additive.FLAVOUR_ENHANCER; + case r'PHOSPHATE': + return Enum$Additive.PHOSPHATE; + case r'SURFACE_WAXED': + return Enum$Additive.SURFACE_WAXED; + case r'SULPHUR': + return Enum$Additive.SULPHUR; + case r'ARTIFICIALLY_BLACKENED_OLIVES': + return Enum$Additive.ARTIFICIALLY_BLACKENED_OLIVES; + case r'SWEETENER': + return Enum$Additive.SWEETENER; + case r'LAXATIVE_IF_OVERUSED': + return Enum$Additive.LAXATIVE_IF_OVERUSED; + case r'PHENYLALANINE': + return Enum$Additive.PHENYLALANINE; + case r'ALCOHOL': + return Enum$Additive.ALCOHOL; + case r'PRESSED_MEET': + return Enum$Additive.PRESSED_MEET; + case r'GLAZING_WITH_CACAO': + return Enum$Additive.GLAZING_WITH_CACAO; + case r'PRESSED_FISH': + return Enum$Additive.PRESSED_FISH; + default: + return Enum$Additive.$unknown; + } +} + +enum Enum$Allergen { + CA, + DI, + EI, + ER, + FI, + GE, + HF, + HA, + KA, + KR, + LU, + MA, + ML, + PA, + PE, + PI, + QU, + RO, + SA, + SE, + SF, + SN, + SO, + WA, + WE, + WT, + LA, + GL, + $unknown +} + +String toJson$Enum$Allergen(Enum$Allergen e) { + switch (e) { + case Enum$Allergen.CA: + return r'CA'; + case Enum$Allergen.DI: + return r'DI'; + case Enum$Allergen.EI: + return r'EI'; + case Enum$Allergen.ER: + return r'ER'; + case Enum$Allergen.FI: + return r'FI'; + case Enum$Allergen.GE: + return r'GE'; + case Enum$Allergen.HF: + return r'HF'; + case Enum$Allergen.HA: + return r'HA'; + case Enum$Allergen.KA: + return r'KA'; + case Enum$Allergen.KR: + return r'KR'; + case Enum$Allergen.LU: + return r'LU'; + case Enum$Allergen.MA: + return r'MA'; + case Enum$Allergen.ML: + return r'ML'; + case Enum$Allergen.PA: + return r'PA'; + case Enum$Allergen.PE: + return r'PE'; + case Enum$Allergen.PI: + return r'PI'; + case Enum$Allergen.QU: + return r'QU'; + case Enum$Allergen.RO: + return r'RO'; + case Enum$Allergen.SA: + return r'SA'; + case Enum$Allergen.SE: + return r'SE'; + case Enum$Allergen.SF: + return r'SF'; + case Enum$Allergen.SN: + return r'SN'; + case Enum$Allergen.SO: + return r'SO'; + case Enum$Allergen.WA: + return r'WA'; + case Enum$Allergen.WE: + return r'WE'; + case Enum$Allergen.WT: + return r'WT'; + case Enum$Allergen.LA: + return r'LA'; + case Enum$Allergen.GL: + return r'GL'; + case Enum$Allergen.$unknown: + return r'$unknown'; + } +} + +Enum$Allergen fromJson$Enum$Allergen(String value) { + switch (value) { + case r'CA': + return Enum$Allergen.CA; + case r'DI': + return Enum$Allergen.DI; + case r'EI': + return Enum$Allergen.EI; + case r'ER': + return Enum$Allergen.ER; + case r'FI': + return Enum$Allergen.FI; + case r'GE': + return Enum$Allergen.GE; + case r'HF': + return Enum$Allergen.HF; + case r'HA': + return Enum$Allergen.HA; + case r'KA': + return Enum$Allergen.KA; + case r'KR': + return Enum$Allergen.KR; + case r'LU': + return Enum$Allergen.LU; + case r'MA': + return Enum$Allergen.MA; + case r'ML': + return Enum$Allergen.ML; + case r'PA': + return Enum$Allergen.PA; + case r'PE': + return Enum$Allergen.PE; + case r'PI': + return Enum$Allergen.PI; + case r'QU': + return Enum$Allergen.QU; + case r'RO': + return Enum$Allergen.RO; + case r'SA': + return Enum$Allergen.SA; + case r'SE': + return Enum$Allergen.SE; + case r'SF': + return Enum$Allergen.SF; + case r'SN': + return Enum$Allergen.SN; + case r'SO': + return Enum$Allergen.SO; + case r'WA': + return Enum$Allergen.WA; + case r'WE': + return Enum$Allergen.WE; + case r'WT': + return Enum$Allergen.WT; + case r'LA': + return Enum$Allergen.LA; + case r'GL': + return Enum$Allergen.GL; + default: + return Enum$Allergen.$unknown; + } +} + +enum Enum$MealType { + VEGAN, + VEGETARIAN, + BEEF, + BEEF_AW, + PORK, + PORK_AW, + FISH, + UNKNOWN, + $unknown +} + +String toJson$Enum$MealType(Enum$MealType e) { + switch (e) { + case Enum$MealType.VEGAN: + return r'VEGAN'; + case Enum$MealType.VEGETARIAN: + return r'VEGETARIAN'; + case Enum$MealType.BEEF: + return r'BEEF'; + case Enum$MealType.BEEF_AW: + return r'BEEF_AW'; + case Enum$MealType.PORK: + return r'PORK'; + case Enum$MealType.PORK_AW: + return r'PORK_AW'; + case Enum$MealType.FISH: + return r'FISH'; + case Enum$MealType.UNKNOWN: + return r'UNKNOWN'; + case Enum$MealType.$unknown: + return r'$unknown'; + } +} + +Enum$MealType fromJson$Enum$MealType(String value) { + switch (value) { + case r'VEGAN': + return Enum$MealType.VEGAN; + case r'VEGETARIAN': + return Enum$MealType.VEGETARIAN; + case r'BEEF': + return Enum$MealType.BEEF; + case r'BEEF_AW': + return Enum$MealType.BEEF_AW; + case r'PORK': + return Enum$MealType.PORK; + case r'PORK_AW': + return Enum$MealType.PORK_AW; + case r'FISH': + return Enum$MealType.FISH; + case r'UNKNOWN': + return Enum$MealType.UNKNOWN; + default: + return Enum$MealType.$unknown; + } +} + +enum Enum$ReportReason { + OFFENSIVE, + ADVERT, + NO_MEAL, + WRONG_MEAL, + VIOLATES_RIGHTS, + OTHER, + $unknown +} + +String toJson$Enum$ReportReason(Enum$ReportReason e) { + switch (e) { + case Enum$ReportReason.OFFENSIVE: + return r'OFFENSIVE'; + case Enum$ReportReason.ADVERT: + return r'ADVERT'; + case Enum$ReportReason.NO_MEAL: + return r'NO_MEAL'; + case Enum$ReportReason.WRONG_MEAL: + return r'WRONG_MEAL'; + case Enum$ReportReason.VIOLATES_RIGHTS: + return r'VIOLATES_RIGHTS'; + case Enum$ReportReason.OTHER: + return r'OTHER'; + case Enum$ReportReason.$unknown: + return r'$unknown'; + } +} + +Enum$ReportReason fromJson$Enum$ReportReason(String value) { + switch (value) { + case r'OFFENSIVE': + return Enum$ReportReason.OFFENSIVE; + case r'ADVERT': + return Enum$ReportReason.ADVERT; + case r'NO_MEAL': + return Enum$ReportReason.NO_MEAL; + case r'WRONG_MEAL': + return Enum$ReportReason.WRONG_MEAL; + case r'VIOLATES_RIGHTS': + return Enum$ReportReason.VIOLATES_RIGHTS; + case r'OTHER': + return Enum$ReportReason.OTHER; + default: + return Enum$ReportReason.$unknown; + } +} + +const possibleTypesMap = >{}; diff --git a/app/pubspec.lock b/app/pubspec.lock index 0b29f06c..56092572 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -266,10 +266,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_localizations: dependency: "direct main" description: flutter @@ -473,10 +473,10 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.1" logging: dependency: transitive description: @@ -910,10 +910,10 @@ packages: dependency: transitive description: name: win32 - sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee url: "https://pub.dev" source: hosted - version: "4.1.4" + version: "5.0.5" xdg_directories: dependency: transitive description: diff --git a/app/secret.example.json b/app/secret.example.json new file mode 100644 index 00000000..5d61e618 --- /dev/null +++ b/app/secret.example.json @@ -0,0 +1,4 @@ +{ + "API_URL": "mensa.yourdomain.com", + "API_KEY": "yourapikey" +} \ No newline at end of file diff --git a/app/test/widget_test.dart b/app/test/widget_test.dart deleted file mode 100644 index 4e2a713e..00000000 --- a/app/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:app/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} From 7e27cb9055b57214c60a502df8f966d6a59c1f50 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Thu, 6 Jul 2023 08:47:21 +0200 Subject: [PATCH 014/184] fixed tests --- app/pubspec.lock | 104 ++---------------- app/pubspec.yaml | 1 - .../api_server/GraphQlServerAccess_test.dart | 2 +- 3 files changed, 13 insertions(+), 94 deletions(-) diff --git a/app/pubspec.lock b/app/pubspec.lock index 56092572..977bdcc3 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.17.1" connectivity_plus: dependency: transitive description: @@ -169,14 +169,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" - coverage: - dependency: transitive - description: - name: coverage - sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" - url: "https://pub.dev" - source: hosted - version: "1.6.3" crypto: dependency: transitive description: @@ -441,10 +433,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.18.0" io: dependency: transitive description: @@ -489,10 +481,10 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -525,14 +517,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" - node_preamble: - dependency: transitive - description: - name: node_preamble - sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" - source: hosted - version: "2.0.2" normalize: dependency: transitive description: @@ -693,22 +677,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - shelf_static: - dependency: transitive - description: - name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e - url: "https://pub.dev" - source: hosted - version: "1.1.2" shelf_web_socket: dependency: transitive description: @@ -722,30 +690,14 @@ packages: description: flutter source: sdk version: "0.0.99" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - source_maps: - dependency: transitive - description: - name: source_maps - sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" - url: "https://pub.dev" - source: hosted - version: "0.10.12" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: @@ -758,10 +710,10 @@ packages: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" stream_transform: dependency: transitive description: @@ -786,30 +738,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" - test: - dependency: "direct dev" - description: - name: test - sha256: "67ec5684c7a19b2aba91d2831f3d305a6fd8e1504629c5818f8d64478abf4f38" - url: "https://pub.dev" - source: hosted - version: "1.24.4" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.6.1" - test_core: - dependency: transitive - description: - name: test_core - sha256: "6b753899253c38ca0523bb0eccff3934ec83d011705dae717c61ecf209e333c9" - url: "https://pub.dev" - source: hosted - version: "0.5.4" + version: "0.5.1" timing: dependency: transitive description: @@ -874,14 +810,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: b8c67f5fa3897b122cf60fe9ff314f7b0ef71eab25c5f8b771480bc338f48823 - url: "https://pub.dev" - source: hosted - version: "11.7.2" watcher: dependency: transitive description: @@ -898,14 +826,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" - url: "https://pub.dev" - source: hosted - version: "1.2.0" win32: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index f82a9065..a5a14e41 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -58,7 +58,6 @@ dev_dependencies: flutter_lints: ^2.0.0 graphql_codegen: ^0.12.2 build_runner: ^2.4.6 - test: ^1.24.4 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index bc16b318..262ebfc3 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -1,7 +1,7 @@ import 'package:app/model/api_server/GraphQlServerAccess.dart'; import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; void main() async { final GraphQlServerAccess serverAccess = GraphQlServerAccess("1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); From 2b74c13710f66b79ef523870b1f26655fd6c90c6 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Thu, 6 Jul 2023 08:42:47 +0200 Subject: [PATCH 015/184] removed file --- app/lib/model/api_server/GraphQlServerAccess | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app/lib/model/api_server/GraphQlServerAccess diff --git a/app/lib/model/api_server/GraphQlServerAccess b/app/lib/model/api_server/GraphQlServerAccess deleted file mode 100644 index e69de29b..00000000 From 7c012862a5b5571746a1c9efd6783ca5c5481545 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Thu, 6 Jul 2023 19:30:28 +0200 Subject: [PATCH 016/184] generated plugin files --- app/macos/Flutter/GeneratedPluginRegistrant.swift | 2 ++ app/windows/flutter/generated_plugin_registrant.cc | 3 +++ app/windows/flutter/generated_plugins.cmake | 1 + 3 files changed, 6 insertions(+) diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift index e777c67d..ec592a91 100644 --- a/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,10 @@ import FlutterMacOS import Foundation +import connectivity_plus import path_provider_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) } diff --git a/app/windows/flutter/generated_plugin_registrant.cc b/app/windows/flutter/generated_plugin_registrant.cc index 8b6d4680..8777c93d 100644 --- a/app/windows/flutter/generated_plugin_registrant.cc +++ b/app/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); } diff --git a/app/windows/flutter/generated_plugins.cmake b/app/windows/flutter/generated_plugins.cmake index b93c4c30..cc1361d8 100644 --- a/app/windows/flutter/generated_plugins.cmake +++ b/app/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + connectivity_plus ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From 7a26544a3c2bc6b457e11f1ec41ef52dc567021b Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Thu, 6 Jul 2023 19:57:26 +0200 Subject: [PATCH 017/184] implemented all mutations --- app/README.md | 19 +- .../model/api_server/GraphQlServerAccess.dart | 95 +- .../requests/deleteDownvote.graphql | 3 - .../requests/deleteDownvote.graphql.dart | 478 --- .../api_server/requests/deleteUpvote.graphql | 3 - .../requests/deleteUpvote.graphql.dart | 471 --- .../api_server/requests/mutations.graphql | 28 + .../requests/mutations.graphql.dart | 3401 +++++++++++++++++ .../model/api_server/requests/querys.graphql | 0 .../api_server/requests/querys.graphql.dart | 244 ++ app/secret.example.json | 2 +- .../api_server/GraphQlServerAccess_test.dart | 21 +- 12 files changed, 3774 insertions(+), 991 deletions(-) delete mode 100644 app/lib/model/api_server/requests/deleteDownvote.graphql delete mode 100644 app/lib/model/api_server/requests/deleteDownvote.graphql.dart delete mode 100644 app/lib/model/api_server/requests/deleteUpvote.graphql delete mode 100644 app/lib/model/api_server/requests/deleteUpvote.graphql.dart create mode 100644 app/lib/model/api_server/requests/mutations.graphql create mode 100644 app/lib/model/api_server/requests/mutations.graphql.dart create mode 100644 app/lib/model/api_server/requests/querys.graphql create mode 100644 app/lib/model/api_server/requests/querys.graphql.dart diff --git a/app/README.md b/app/README.md index eee495ec..63e294a3 100644 --- a/app/README.md +++ b/app/README.md @@ -1,9 +1,24 @@ # MensaApp-Frontend -Frontend application for viewing and interacting with meal plan data of the canteens of the Studierendenwerk Karlsruhe [^1]. + +Frontend application for viewing and interacting with meal plan data of the canteens of the +Studierendenwerk Karlsruhe [^1]. [^1]: https://www.sw-ka.de/de/hochschulgastronomie/speiseplan/ ## Building ### GraphQL -To generate the dart wrappers from `*.graphql` files run `dart run build_runner build`. \ No newline at end of file + +To generate the dart wrappers from `*.graphql` files run `dart run build_runner build`. + +### Tests + +### Graphql + +To run graphql test, a api endpoint must be specified in an `secret.json` file, an example of which +can be found in `secret.example.env`. +| ⚠️ **Important** | These secrets must AT NO POINT be check in to version control! | +| -- | -- | + +Then, you need to specify the files location when running +tests: `flutter test --dart-define-from-file=.\secret.json` \ No newline at end of file diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index 6f2f0f41..fc9ebf0e 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -1,4 +1,4 @@ -import 'package:app/model/api_server/requests/deleteUpvote.graphql.dart'; +import 'package:app/model/api_server/requests/schema.graphql.dart'; import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; @@ -13,7 +13,7 @@ import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import '../../view_model/repository/interface/IServerAccess.dart'; -import 'requests/deleteDownvote.graphql.dart'; +import 'requests/mutations.graphql.dart'; class GraphQlServerAccess implements IServerAccess { final String _apiKey = const String.fromEnvironment('API_KEY'); @@ -29,6 +29,8 @@ class GraphQlServerAccess implements IServerAccess { return GraphQlServerAccess._(clientId); } + // ---------------------- mutations ---------------------- + @override Future deleteDownvote(ImageData image) async { // TODO auth @@ -36,8 +38,7 @@ class GraphQlServerAccess implements IServerAccess { Options$Mutation$RemoveDownvote( variables: Variables$Mutation$RemoveDownvote(imageId: image.id))); final parsedData = result.parsedData; - final success = parsedData?.removeDownvote ?? false; - return success; + return parsedData?.removeDownvote ?? false; } @override @@ -47,55 +48,95 @@ class GraphQlServerAccess implements IServerAccess { Options$Mutation$RemoveUpvote( variables: Variables$Mutation$RemoveUpvote(imageId: image.id))); final parsedData = result.parsedData; - final success = parsedData?.removeUpvote ?? false; - return success; + return parsedData?.removeUpvote ?? false; } @override - Future downvoteImage(ImageData image) { - // TODO: implement downvoteImage - throw UnimplementedError(); + Future downvoteImage(ImageData image) async { + // TODO auth + final result = await _client.mutate$AddDownvote( + Options$Mutation$AddDownvote( + variables: Variables$Mutation$AddDownvote(imageId: image.id))); + final parsedData = result.parsedData; + return parsedData?.addDownvote ?? false; } @override - Future> getMealFromId(String id) { - // TODO: implement getMealFromId - throw UnimplementedError(); + Future upvoteImage(ImageData image) async { + // TODO auth + final result = await _client.mutate$AddUpvote(Options$Mutation$AddUpvote( + variables: Variables$Mutation$AddUpvote(imageId: image.id))); + final parsedData = result.parsedData; + return parsedData?.addUpvote ?? false; } @override - Future linkImage(String url, Meal meal) { - // TODO: implement linkImage - throw UnimplementedError(); + Future linkImage(String url, Meal meal) async { + // TODO: auth + final result = await _client.mutate$LinkImage(Options$Mutation$LinkImage( + variables: + Variables$Mutation$LinkImage(imageUrl: url, mealId: meal.id))); + final parsedData = result.parsedData; + return parsedData?.addImage ?? false; } @override - Future reportImage(ImageData image, ReportCategory reportReason) { - // TODO: implement reportImage - throw UnimplementedError(); + Future reportImage(ImageData image, ReportCategory reportReason) async { + // TODO: auth + final result = await _client.mutate$ReportImage( + Options$Mutation$ReportImage( + variables: Variables$Mutation$ReportImage( + imageId: image.id, + reason: _convertToReportReason(reportReason)))); + final parsedData = result.parsedData; + return parsedData?.reportImage ?? false; } @override - Future>> updateAll() { - // TODO: implement updateAll - throw UnimplementedError(); + Future updateMealRating(int rating, Meal meal) async { + // TODO: auth + final result = await _client.mutate$UpdateRating( + Options$Mutation$UpdateRating( + variables: Variables$Mutation$UpdateRating( + mealId: meal.id, rating: rating))); + final parsedData = result.parsedData; + return parsedData?.setRating ?? false; } + // ---------------------- queries ---------------------- @override - Future>> updateCanteen(Canteen canteen, DateTime date) { - // TODO: implement updateCanteen + Future> getMealFromId(String id) async { + // TODO: implement getMealFromId throw UnimplementedError(); } @override - Future updateMealRating(int rating, Meal meal) { - // TODO: implement updateMealRating + Future>> updateAll() async { + // TODO: implement updateAll throw UnimplementedError(); } @override - Future upvoteImage(ImageData image) { - // TODO: implement upvoteImage + Future>> updateCanteen( + Canteen canteen, DateTime date) async { + // TODO: implement updateCanteen throw UnimplementedError(); } } + +Enum$ReportReason _convertToReportReason(ReportCategory reportReason) { + switch (reportReason) { + case ReportCategory.offensive: + return Enum$ReportReason.OFFENSIVE; + case ReportCategory.advert: + return Enum$ReportReason.ADVERT; + case ReportCategory.noMeal: + return Enum$ReportReason.NO_MEAL; + case ReportCategory.violatesRights: + return Enum$ReportReason.VIOLATES_RIGHTS; + case ReportCategory.wrongMeal: + return Enum$ReportReason.WRONG_MEAL; + case ReportCategory.other: + return Enum$ReportReason.OTHER; + } +} diff --git a/app/lib/model/api_server/requests/deleteDownvote.graphql b/app/lib/model/api_server/requests/deleteDownvote.graphql deleted file mode 100644 index 0963d01d..00000000 --- a/app/lib/model/api_server/requests/deleteDownvote.graphql +++ /dev/null @@ -1,3 +0,0 @@ -mutation RemoveDownvote($imageId: UUID!) { - removeDownvote(imageId: $imageId ) -} \ No newline at end of file diff --git a/app/lib/model/api_server/requests/deleteDownvote.graphql.dart b/app/lib/model/api_server/requests/deleteDownvote.graphql.dart deleted file mode 100644 index e9fb75fb..00000000 --- a/app/lib/model/api_server/requests/deleteDownvote.graphql.dart +++ /dev/null @@ -1,478 +0,0 @@ -import 'dart:async'; -import 'package:flutter/widgets.dart' as widgets; -import 'package:gql/ast.dart'; -import 'package:graphql/client.dart' as graphql; -import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; - -class Variables$Mutation$RemoveDownvote { - factory Variables$Mutation$RemoveDownvote({required String imageId}) => - Variables$Mutation$RemoveDownvote._({ - r'imageId': imageId, - }); - - Variables$Mutation$RemoveDownvote._(this._$data); - - factory Variables$Mutation$RemoveDownvote.fromJson( - Map data) { - final result$data = {}; - final l$imageId = data['imageId']; - result$data['imageId'] = (l$imageId as String); - return Variables$Mutation$RemoveDownvote._(result$data); - } - - Map _$data; - - String get imageId => (_$data['imageId'] as String); - Map toJson() { - final result$data = {}; - final l$imageId = imageId; - result$data['imageId'] = l$imageId; - return result$data; - } - - CopyWith$Variables$Mutation$RemoveDownvote - get copyWith => CopyWith$Variables$Mutation$RemoveDownvote( - this, - (i) => i, - ); - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (!(other is Variables$Mutation$RemoveDownvote) || - runtimeType != other.runtimeType) { - return false; - } - final l$imageId = imageId; - final lOther$imageId = other.imageId; - if (l$imageId != lOther$imageId) { - return false; - } - return true; - } - - @override - int get hashCode { - final l$imageId = imageId; - return Object.hashAll([l$imageId]); - } -} - -abstract class CopyWith$Variables$Mutation$RemoveDownvote { - factory CopyWith$Variables$Mutation$RemoveDownvote( - Variables$Mutation$RemoveDownvote instance, - TRes Function(Variables$Mutation$RemoveDownvote) then, - ) = _CopyWithImpl$Variables$Mutation$RemoveDownvote; - - factory CopyWith$Variables$Mutation$RemoveDownvote.stub(TRes res) = - _CopyWithStubImpl$Variables$Mutation$RemoveDownvote; - - TRes call({String? imageId}); -} - -class _CopyWithImpl$Variables$Mutation$RemoveDownvote - implements CopyWith$Variables$Mutation$RemoveDownvote { - _CopyWithImpl$Variables$Mutation$RemoveDownvote( - this._instance, - this._then, - ); - - final Variables$Mutation$RemoveDownvote _instance; - - final TRes Function(Variables$Mutation$RemoveDownvote) _then; - - static const _undefined = {}; - - TRes call({Object? imageId = _undefined}) => - _then(Variables$Mutation$RemoveDownvote._({ - ..._instance._$data, - if (imageId != _undefined && imageId != null) - 'imageId': (imageId as String), - })); -} - -class _CopyWithStubImpl$Variables$Mutation$RemoveDownvote - implements CopyWith$Variables$Mutation$RemoveDownvote { - _CopyWithStubImpl$Variables$Mutation$RemoveDownvote(this._res); - - TRes _res; - - call({String? imageId}) => _res; -} - -class Mutation$RemoveDownvote { - Mutation$RemoveDownvote({ - required this.removeDownvote, - this.$__typename = 'MutationRoot', - }); - - factory Mutation$RemoveDownvote.fromJson(Map json) { - final l$removeDownvote = json['removeDownvote']; - final l$$__typename = json['__typename']; - return Mutation$RemoveDownvote( - removeDownvote: (l$removeDownvote as bool), - $__typename: (l$$__typename as String), - ); - } - - final bool removeDownvote; - - final String $__typename; - - Map toJson() { - final _resultData = {}; - final l$removeDownvote = removeDownvote; - _resultData['removeDownvote'] = l$removeDownvote; - final l$$__typename = $__typename; - _resultData['__typename'] = l$$__typename; - return _resultData; - } - - @override - int get hashCode { - final l$removeDownvote = removeDownvote; - final l$$__typename = $__typename; - return Object.hashAll([ - l$removeDownvote, - l$$__typename, - ]); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (!(other is Mutation$RemoveDownvote) || - runtimeType != other.runtimeType) { - return false; - } - final l$removeDownvote = removeDownvote; - final lOther$removeDownvote = other.removeDownvote; - if (l$removeDownvote != lOther$removeDownvote) { - return false; - } - final l$$__typename = $__typename; - final lOther$$__typename = other.$__typename; - if (l$$__typename != lOther$$__typename) { - return false; - } - return true; - } -} - -extension UtilityExtension$Mutation$RemoveDownvote on Mutation$RemoveDownvote { - CopyWith$Mutation$RemoveDownvote get copyWith => - CopyWith$Mutation$RemoveDownvote( - this, - (i) => i, - ); -} - -abstract class CopyWith$Mutation$RemoveDownvote { - factory CopyWith$Mutation$RemoveDownvote( - Mutation$RemoveDownvote instance, - TRes Function(Mutation$RemoveDownvote) then, - ) = _CopyWithImpl$Mutation$RemoveDownvote; - - factory CopyWith$Mutation$RemoveDownvote.stub(TRes res) = - _CopyWithStubImpl$Mutation$RemoveDownvote; - - TRes call({ - bool? removeDownvote, - String? $__typename, - }); -} - -class _CopyWithImpl$Mutation$RemoveDownvote - implements CopyWith$Mutation$RemoveDownvote { - _CopyWithImpl$Mutation$RemoveDownvote( - this._instance, - this._then, - ); - - final Mutation$RemoveDownvote _instance; - - final TRes Function(Mutation$RemoveDownvote) _then; - - static const _undefined = {}; - - TRes call({ - Object? removeDownvote = _undefined, - Object? $__typename = _undefined, - }) => - _then(Mutation$RemoveDownvote( - removeDownvote: removeDownvote == _undefined || removeDownvote == null - ? _instance.removeDownvote - : (removeDownvote as bool), - $__typename: $__typename == _undefined || $__typename == null - ? _instance.$__typename - : ($__typename as String), - )); -} - -class _CopyWithStubImpl$Mutation$RemoveDownvote - implements CopyWith$Mutation$RemoveDownvote { - _CopyWithStubImpl$Mutation$RemoveDownvote(this._res); - - TRes _res; - - call({ - bool? removeDownvote, - String? $__typename, - }) => - _res; -} - -const documentNodeMutationRemoveDownvote = DocumentNode(definitions: [ - OperationDefinitionNode( - type: OperationType.mutation, - name: NameNode(value: 'RemoveDownvote'), - variableDefinitions: [ - VariableDefinitionNode( - variable: VariableNode(name: NameNode(value: 'imageId')), - type: NamedTypeNode( - name: NameNode(value: 'UUID'), - isNonNull: true, - ), - defaultValue: DefaultValueNode(value: null), - directives: [], - ) - ], - directives: [], - selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'removeDownvote'), - alias: null, - arguments: [ - ArgumentNode( - name: NameNode(value: 'imageId'), - value: VariableNode(name: NameNode(value: 'imageId')), - ) - ], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: '__typename'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - ]), - ), -]); -Mutation$RemoveDownvote _parserFn$Mutation$RemoveDownvote( - Map data) => - Mutation$RemoveDownvote.fromJson(data); -typedef OnMutationCompleted$Mutation$RemoveDownvote = FutureOr Function( - Map?, - Mutation$RemoveDownvote?, -); - -class Options$Mutation$RemoveDownvote - extends graphql.MutationOptions { - Options$Mutation$RemoveDownvote({ - String? operationName, - required Variables$Mutation$RemoveDownvote variables, - graphql.FetchPolicy? fetchPolicy, - graphql.ErrorPolicy? errorPolicy, - graphql.CacheRereadPolicy? cacheRereadPolicy, - Object? optimisticResult, - Mutation$RemoveDownvote? typedOptimisticResult, - graphql.Context? context, - OnMutationCompleted$Mutation$RemoveDownvote? onCompleted, - graphql.OnMutationUpdate? update, - graphql.OnError? onError, - }) : onCompletedWithParsed = onCompleted, - super( - variables: variables.toJson(), - operationName: operationName, - fetchPolicy: fetchPolicy, - errorPolicy: errorPolicy, - cacheRereadPolicy: cacheRereadPolicy, - optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), - context: context, - onCompleted: onCompleted == null - ? null - : (data) => onCompleted( - data, - data == null - ? null - : _parserFn$Mutation$RemoveDownvote(data), - ), - update: update, - onError: onError, - document: documentNodeMutationRemoveDownvote, - parserFn: _parserFn$Mutation$RemoveDownvote, - ); - - final OnMutationCompleted$Mutation$RemoveDownvote? onCompletedWithParsed; - - @override - List get properties => [ - ...super.onCompleted == null - ? super.properties - : super.properties.where((property) => property != onCompleted), - onCompletedWithParsed, - ]; -} - -class WatchOptions$Mutation$RemoveDownvote - extends graphql.WatchQueryOptions { - WatchOptions$Mutation$RemoveDownvote({ - String? operationName, - required Variables$Mutation$RemoveDownvote variables, - graphql.FetchPolicy? fetchPolicy, - graphql.ErrorPolicy? errorPolicy, - graphql.CacheRereadPolicy? cacheRereadPolicy, - Object? optimisticResult, - Mutation$RemoveDownvote? typedOptimisticResult, - graphql.Context? context, - Duration? pollInterval, - bool? eagerlyFetchResults, - bool carryForwardDataOnException = true, - bool fetchResults = false, - }) : super( - variables: variables.toJson(), - operationName: operationName, - fetchPolicy: fetchPolicy, - errorPolicy: errorPolicy, - cacheRereadPolicy: cacheRereadPolicy, - optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), - context: context, - document: documentNodeMutationRemoveDownvote, - pollInterval: pollInterval, - eagerlyFetchResults: eagerlyFetchResults, - carryForwardDataOnException: carryForwardDataOnException, - fetchResults: fetchResults, - parserFn: _parserFn$Mutation$RemoveDownvote, - ); -} - -extension ClientExtension$Mutation$RemoveDownvote on graphql.GraphQLClient { - Future> mutate$RemoveDownvote( - Options$Mutation$RemoveDownvote options) async => - await this.mutate(options); - graphql.ObservableQuery watchMutation$RemoveDownvote( - WatchOptions$Mutation$RemoveDownvote options) => - this.watchMutation(options); -} - -class Mutation$RemoveDownvote$HookResult { - Mutation$RemoveDownvote$HookResult( - this.runMutation, - this.result, - ); - - final RunMutation$Mutation$RemoveDownvote runMutation; - - final graphql.QueryResult result; -} - -Mutation$RemoveDownvote$HookResult useMutation$RemoveDownvote( - [WidgetOptions$Mutation$RemoveDownvote? options]) { - final result = graphql_flutter - .useMutation(options ?? WidgetOptions$Mutation$RemoveDownvote()); - return Mutation$RemoveDownvote$HookResult( - (variables, {optimisticResult, typedOptimisticResult}) => - result.runMutation( - variables.toJson(), - optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), - ), - result.result, - ); -} - -graphql.ObservableQuery - useWatchMutation$RemoveDownvote( - WatchOptions$Mutation$RemoveDownvote options) => - graphql_flutter.useWatchMutation(options); - -class WidgetOptions$Mutation$RemoveDownvote - extends graphql.MutationOptions { - WidgetOptions$Mutation$RemoveDownvote({ - String? operationName, - graphql.FetchPolicy? fetchPolicy, - graphql.ErrorPolicy? errorPolicy, - graphql.CacheRereadPolicy? cacheRereadPolicy, - Object? optimisticResult, - Mutation$RemoveDownvote? typedOptimisticResult, - graphql.Context? context, - OnMutationCompleted$Mutation$RemoveDownvote? onCompleted, - graphql.OnMutationUpdate? update, - graphql.OnError? onError, - }) : onCompletedWithParsed = onCompleted, - super( - operationName: operationName, - fetchPolicy: fetchPolicy, - errorPolicy: errorPolicy, - cacheRereadPolicy: cacheRereadPolicy, - optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), - context: context, - onCompleted: onCompleted == null - ? null - : (data) => onCompleted( - data, - data == null - ? null - : _parserFn$Mutation$RemoveDownvote(data), - ), - update: update, - onError: onError, - document: documentNodeMutationRemoveDownvote, - parserFn: _parserFn$Mutation$RemoveDownvote, - ); - - final OnMutationCompleted$Mutation$RemoveDownvote? onCompletedWithParsed; - - @override - List get properties => [ - ...super.onCompleted == null - ? super.properties - : super.properties.where((property) => property != onCompleted), - onCompletedWithParsed, - ]; -} - -typedef RunMutation$Mutation$RemoveDownvote - = graphql.MultiSourceResult Function( - Variables$Mutation$RemoveDownvote, { - Object? optimisticResult, - Mutation$RemoveDownvote? typedOptimisticResult, -}); -typedef Builder$Mutation$RemoveDownvote = widgets.Widget Function( - RunMutation$Mutation$RemoveDownvote, - graphql.QueryResult?, -); - -class Mutation$RemoveDownvote$Widget - extends graphql_flutter.Mutation { - Mutation$RemoveDownvote$Widget({ - widgets.Key? key, - WidgetOptions$Mutation$RemoveDownvote? options, - required Builder$Mutation$RemoveDownvote builder, - }) : super( - key: key, - options: options ?? WidgetOptions$Mutation$RemoveDownvote(), - builder: ( - run, - result, - ) => - builder( - ( - variables, { - optimisticResult, - typedOptimisticResult, - }) => - run( - variables.toJson(), - optimisticResult: - optimisticResult ?? typedOptimisticResult?.toJson(), - ), - result, - ), - ); -} diff --git a/app/lib/model/api_server/requests/deleteUpvote.graphql b/app/lib/model/api_server/requests/deleteUpvote.graphql deleted file mode 100644 index 8c825bf5..00000000 --- a/app/lib/model/api_server/requests/deleteUpvote.graphql +++ /dev/null @@ -1,3 +0,0 @@ -mutation RemoveUpvote($imageId: UUID!) { - removeUpvote(imageId: $imageId ) -} \ No newline at end of file diff --git a/app/lib/model/api_server/requests/deleteUpvote.graphql.dart b/app/lib/model/api_server/requests/deleteUpvote.graphql.dart deleted file mode 100644 index d53b6e76..00000000 --- a/app/lib/model/api_server/requests/deleteUpvote.graphql.dart +++ /dev/null @@ -1,471 +0,0 @@ -import 'dart:async'; -import 'package:flutter/widgets.dart' as widgets; -import 'package:gql/ast.dart'; -import 'package:graphql/client.dart' as graphql; -import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; - -class Variables$Mutation$RemoveUpvote { - factory Variables$Mutation$RemoveUpvote({required String imageId}) => - Variables$Mutation$RemoveUpvote._({ - r'imageId': imageId, - }); - - Variables$Mutation$RemoveUpvote._(this._$data); - - factory Variables$Mutation$RemoveUpvote.fromJson(Map data) { - final result$data = {}; - final l$imageId = data['imageId']; - result$data['imageId'] = (l$imageId as String); - return Variables$Mutation$RemoveUpvote._(result$data); - } - - Map _$data; - - String get imageId => (_$data['imageId'] as String); - Map toJson() { - final result$data = {}; - final l$imageId = imageId; - result$data['imageId'] = l$imageId; - return result$data; - } - - CopyWith$Variables$Mutation$RemoveUpvote - get copyWith => CopyWith$Variables$Mutation$RemoveUpvote( - this, - (i) => i, - ); - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (!(other is Variables$Mutation$RemoveUpvote) || - runtimeType != other.runtimeType) { - return false; - } - final l$imageId = imageId; - final lOther$imageId = other.imageId; - if (l$imageId != lOther$imageId) { - return false; - } - return true; - } - - @override - int get hashCode { - final l$imageId = imageId; - return Object.hashAll([l$imageId]); - } -} - -abstract class CopyWith$Variables$Mutation$RemoveUpvote { - factory CopyWith$Variables$Mutation$RemoveUpvote( - Variables$Mutation$RemoveUpvote instance, - TRes Function(Variables$Mutation$RemoveUpvote) then, - ) = _CopyWithImpl$Variables$Mutation$RemoveUpvote; - - factory CopyWith$Variables$Mutation$RemoveUpvote.stub(TRes res) = - _CopyWithStubImpl$Variables$Mutation$RemoveUpvote; - - TRes call({String? imageId}); -} - -class _CopyWithImpl$Variables$Mutation$RemoveUpvote - implements CopyWith$Variables$Mutation$RemoveUpvote { - _CopyWithImpl$Variables$Mutation$RemoveUpvote( - this._instance, - this._then, - ); - - final Variables$Mutation$RemoveUpvote _instance; - - final TRes Function(Variables$Mutation$RemoveUpvote) _then; - - static const _undefined = {}; - - TRes call({Object? imageId = _undefined}) => - _then(Variables$Mutation$RemoveUpvote._({ - ..._instance._$data, - if (imageId != _undefined && imageId != null) - 'imageId': (imageId as String), - })); -} - -class _CopyWithStubImpl$Variables$Mutation$RemoveUpvote - implements CopyWith$Variables$Mutation$RemoveUpvote { - _CopyWithStubImpl$Variables$Mutation$RemoveUpvote(this._res); - - TRes _res; - - call({String? imageId}) => _res; -} - -class Mutation$RemoveUpvote { - Mutation$RemoveUpvote({ - required this.removeUpvote, - this.$__typename = 'MutationRoot', - }); - - factory Mutation$RemoveUpvote.fromJson(Map json) { - final l$removeUpvote = json['removeUpvote']; - final l$$__typename = json['__typename']; - return Mutation$RemoveUpvote( - removeUpvote: (l$removeUpvote as bool), - $__typename: (l$$__typename as String), - ); - } - - final bool removeUpvote; - - final String $__typename; - - Map toJson() { - final _resultData = {}; - final l$removeUpvote = removeUpvote; - _resultData['removeUpvote'] = l$removeUpvote; - final l$$__typename = $__typename; - _resultData['__typename'] = l$$__typename; - return _resultData; - } - - @override - int get hashCode { - final l$removeUpvote = removeUpvote; - final l$$__typename = $__typename; - return Object.hashAll([ - l$removeUpvote, - l$$__typename, - ]); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (!(other is Mutation$RemoveUpvote) || runtimeType != other.runtimeType) { - return false; - } - final l$removeUpvote = removeUpvote; - final lOther$removeUpvote = other.removeUpvote; - if (l$removeUpvote != lOther$removeUpvote) { - return false; - } - final l$$__typename = $__typename; - final lOther$$__typename = other.$__typename; - if (l$$__typename != lOther$$__typename) { - return false; - } - return true; - } -} - -extension UtilityExtension$Mutation$RemoveUpvote on Mutation$RemoveUpvote { - CopyWith$Mutation$RemoveUpvote get copyWith => - CopyWith$Mutation$RemoveUpvote( - this, - (i) => i, - ); -} - -abstract class CopyWith$Mutation$RemoveUpvote { - factory CopyWith$Mutation$RemoveUpvote( - Mutation$RemoveUpvote instance, - TRes Function(Mutation$RemoveUpvote) then, - ) = _CopyWithImpl$Mutation$RemoveUpvote; - - factory CopyWith$Mutation$RemoveUpvote.stub(TRes res) = - _CopyWithStubImpl$Mutation$RemoveUpvote; - - TRes call({ - bool? removeUpvote, - String? $__typename, - }); -} - -class _CopyWithImpl$Mutation$RemoveUpvote - implements CopyWith$Mutation$RemoveUpvote { - _CopyWithImpl$Mutation$RemoveUpvote( - this._instance, - this._then, - ); - - final Mutation$RemoveUpvote _instance; - - final TRes Function(Mutation$RemoveUpvote) _then; - - static const _undefined = {}; - - TRes call({ - Object? removeUpvote = _undefined, - Object? $__typename = _undefined, - }) => - _then(Mutation$RemoveUpvote( - removeUpvote: removeUpvote == _undefined || removeUpvote == null - ? _instance.removeUpvote - : (removeUpvote as bool), - $__typename: $__typename == _undefined || $__typename == null - ? _instance.$__typename - : ($__typename as String), - )); -} - -class _CopyWithStubImpl$Mutation$RemoveUpvote - implements CopyWith$Mutation$RemoveUpvote { - _CopyWithStubImpl$Mutation$RemoveUpvote(this._res); - - TRes _res; - - call({ - bool? removeUpvote, - String? $__typename, - }) => - _res; -} - -const documentNodeMutationRemoveUpvote = DocumentNode(definitions: [ - OperationDefinitionNode( - type: OperationType.mutation, - name: NameNode(value: 'RemoveUpvote'), - variableDefinitions: [ - VariableDefinitionNode( - variable: VariableNode(name: NameNode(value: 'imageId')), - type: NamedTypeNode( - name: NameNode(value: 'UUID'), - isNonNull: true, - ), - defaultValue: DefaultValueNode(value: null), - directives: [], - ) - ], - directives: [], - selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'removeUpvote'), - alias: null, - arguments: [ - ArgumentNode( - name: NameNode(value: 'imageId'), - value: VariableNode(name: NameNode(value: 'imageId')), - ) - ], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: '__typename'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - ]), - ), -]); -Mutation$RemoveUpvote _parserFn$Mutation$RemoveUpvote( - Map data) => - Mutation$RemoveUpvote.fromJson(data); -typedef OnMutationCompleted$Mutation$RemoveUpvote = FutureOr Function( - Map?, - Mutation$RemoveUpvote?, -); - -class Options$Mutation$RemoveUpvote - extends graphql.MutationOptions { - Options$Mutation$RemoveUpvote({ - String? operationName, - required Variables$Mutation$RemoveUpvote variables, - graphql.FetchPolicy? fetchPolicy, - graphql.ErrorPolicy? errorPolicy, - graphql.CacheRereadPolicy? cacheRereadPolicy, - Object? optimisticResult, - Mutation$RemoveUpvote? typedOptimisticResult, - graphql.Context? context, - OnMutationCompleted$Mutation$RemoveUpvote? onCompleted, - graphql.OnMutationUpdate? update, - graphql.OnError? onError, - }) : onCompletedWithParsed = onCompleted, - super( - variables: variables.toJson(), - operationName: operationName, - fetchPolicy: fetchPolicy, - errorPolicy: errorPolicy, - cacheRereadPolicy: cacheRereadPolicy, - optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), - context: context, - onCompleted: onCompleted == null - ? null - : (data) => onCompleted( - data, - data == null ? null : _parserFn$Mutation$RemoveUpvote(data), - ), - update: update, - onError: onError, - document: documentNodeMutationRemoveUpvote, - parserFn: _parserFn$Mutation$RemoveUpvote, - ); - - final OnMutationCompleted$Mutation$RemoveUpvote? onCompletedWithParsed; - - @override - List get properties => [ - ...super.onCompleted == null - ? super.properties - : super.properties.where((property) => property != onCompleted), - onCompletedWithParsed, - ]; -} - -class WatchOptions$Mutation$RemoveUpvote - extends graphql.WatchQueryOptions { - WatchOptions$Mutation$RemoveUpvote({ - String? operationName, - required Variables$Mutation$RemoveUpvote variables, - graphql.FetchPolicy? fetchPolicy, - graphql.ErrorPolicy? errorPolicy, - graphql.CacheRereadPolicy? cacheRereadPolicy, - Object? optimisticResult, - Mutation$RemoveUpvote? typedOptimisticResult, - graphql.Context? context, - Duration? pollInterval, - bool? eagerlyFetchResults, - bool carryForwardDataOnException = true, - bool fetchResults = false, - }) : super( - variables: variables.toJson(), - operationName: operationName, - fetchPolicy: fetchPolicy, - errorPolicy: errorPolicy, - cacheRereadPolicy: cacheRereadPolicy, - optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), - context: context, - document: documentNodeMutationRemoveUpvote, - pollInterval: pollInterval, - eagerlyFetchResults: eagerlyFetchResults, - carryForwardDataOnException: carryForwardDataOnException, - fetchResults: fetchResults, - parserFn: _parserFn$Mutation$RemoveUpvote, - ); -} - -extension ClientExtension$Mutation$RemoveUpvote on graphql.GraphQLClient { - Future> mutate$RemoveUpvote( - Options$Mutation$RemoveUpvote options) async => - await this.mutate(options); - graphql.ObservableQuery watchMutation$RemoveUpvote( - WatchOptions$Mutation$RemoveUpvote options) => - this.watchMutation(options); -} - -class Mutation$RemoveUpvote$HookResult { - Mutation$RemoveUpvote$HookResult( - this.runMutation, - this.result, - ); - - final RunMutation$Mutation$RemoveUpvote runMutation; - - final graphql.QueryResult result; -} - -Mutation$RemoveUpvote$HookResult useMutation$RemoveUpvote( - [WidgetOptions$Mutation$RemoveUpvote? options]) { - final result = graphql_flutter - .useMutation(options ?? WidgetOptions$Mutation$RemoveUpvote()); - return Mutation$RemoveUpvote$HookResult( - (variables, {optimisticResult, typedOptimisticResult}) => - result.runMutation( - variables.toJson(), - optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), - ), - result.result, - ); -} - -graphql.ObservableQuery useWatchMutation$RemoveUpvote( - WatchOptions$Mutation$RemoveUpvote options) => - graphql_flutter.useWatchMutation(options); - -class WidgetOptions$Mutation$RemoveUpvote - extends graphql.MutationOptions { - WidgetOptions$Mutation$RemoveUpvote({ - String? operationName, - graphql.FetchPolicy? fetchPolicy, - graphql.ErrorPolicy? errorPolicy, - graphql.CacheRereadPolicy? cacheRereadPolicy, - Object? optimisticResult, - Mutation$RemoveUpvote? typedOptimisticResult, - graphql.Context? context, - OnMutationCompleted$Mutation$RemoveUpvote? onCompleted, - graphql.OnMutationUpdate? update, - graphql.OnError? onError, - }) : onCompletedWithParsed = onCompleted, - super( - operationName: operationName, - fetchPolicy: fetchPolicy, - errorPolicy: errorPolicy, - cacheRereadPolicy: cacheRereadPolicy, - optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), - context: context, - onCompleted: onCompleted == null - ? null - : (data) => onCompleted( - data, - data == null ? null : _parserFn$Mutation$RemoveUpvote(data), - ), - update: update, - onError: onError, - document: documentNodeMutationRemoveUpvote, - parserFn: _parserFn$Mutation$RemoveUpvote, - ); - - final OnMutationCompleted$Mutation$RemoveUpvote? onCompletedWithParsed; - - @override - List get properties => [ - ...super.onCompleted == null - ? super.properties - : super.properties.where((property) => property != onCompleted), - onCompletedWithParsed, - ]; -} - -typedef RunMutation$Mutation$RemoveUpvote - = graphql.MultiSourceResult Function( - Variables$Mutation$RemoveUpvote, { - Object? optimisticResult, - Mutation$RemoveUpvote? typedOptimisticResult, -}); -typedef Builder$Mutation$RemoveUpvote = widgets.Widget Function( - RunMutation$Mutation$RemoveUpvote, - graphql.QueryResult?, -); - -class Mutation$RemoveUpvote$Widget - extends graphql_flutter.Mutation { - Mutation$RemoveUpvote$Widget({ - widgets.Key? key, - WidgetOptions$Mutation$RemoveUpvote? options, - required Builder$Mutation$RemoveUpvote builder, - }) : super( - key: key, - options: options ?? WidgetOptions$Mutation$RemoveUpvote(), - builder: ( - run, - result, - ) => - builder( - ( - variables, { - optimisticResult, - typedOptimisticResult, - }) => - run( - variables.toJson(), - optimisticResult: - optimisticResult ?? typedOptimisticResult?.toJson(), - ), - result, - ), - ); -} diff --git a/app/lib/model/api_server/requests/mutations.graphql b/app/lib/model/api_server/requests/mutations.graphql new file mode 100644 index 00000000..814b697e --- /dev/null +++ b/app/lib/model/api_server/requests/mutations.graphql @@ -0,0 +1,28 @@ +mutation RemoveDownvote($imageId: UUID!) { + removeDownvote(imageId: $imageId) +} + +mutation RemoveUpvote($imageId: UUID!) { + removeUpvote(imageId: $imageId) +} + +mutation AddDownvote($imageId: UUID!) { + addDownvote(imageId: $imageId) +} + +mutation AddUpvote($imageId: UUID!) { + addUpvote(imageId: $imageId) +} + +mutation LinkImage($mealId: UUID!, $imageUrl: String!) { + addImage(mealId: $mealId, imageUrl: $imageUrl) +} + +mutation ReportImage($imageId: UUID!, $reason: ReportReason!) { + reportImage(imageId: $imageId, reason: $reason) +} + +mutation UpdateRating($mealId: UUID!, $rating: Int!) { + setRating(mealId: $mealId, rating: $rating) +} + diff --git a/app/lib/model/api_server/requests/mutations.graphql.dart b/app/lib/model/api_server/requests/mutations.graphql.dart new file mode 100644 index 00000000..ac011252 --- /dev/null +++ b/app/lib/model/api_server/requests/mutations.graphql.dart @@ -0,0 +1,3401 @@ +import 'dart:async'; +import 'package:flutter/widgets.dart' as widgets; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; +import 'schema.graphql.dart'; + +class Variables$Mutation$RemoveDownvote { + factory Variables$Mutation$RemoveDownvote({required String imageId}) => + Variables$Mutation$RemoveDownvote._({ + r'imageId': imageId, + }); + + Variables$Mutation$RemoveDownvote._(this._$data); + + factory Variables$Mutation$RemoveDownvote.fromJson( + Map data) { + final result$data = {}; + final l$imageId = data['imageId']; + result$data['imageId'] = (l$imageId as String); + return Variables$Mutation$RemoveDownvote._(result$data); + } + + Map _$data; + + String get imageId => (_$data['imageId'] as String); + Map toJson() { + final result$data = {}; + final l$imageId = imageId; + result$data['imageId'] = l$imageId; + return result$data; + } + + CopyWith$Variables$Mutation$RemoveDownvote + get copyWith => CopyWith$Variables$Mutation$RemoveDownvote( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$RemoveDownvote) || + runtimeType != other.runtimeType) { + return false; + } + final l$imageId = imageId; + final lOther$imageId = other.imageId; + if (l$imageId != lOther$imageId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$imageId = imageId; + return Object.hashAll([l$imageId]); + } +} + +abstract class CopyWith$Variables$Mutation$RemoveDownvote { + factory CopyWith$Variables$Mutation$RemoveDownvote( + Variables$Mutation$RemoveDownvote instance, + TRes Function(Variables$Mutation$RemoveDownvote) then, + ) = _CopyWithImpl$Variables$Mutation$RemoveDownvote; + + factory CopyWith$Variables$Mutation$RemoveDownvote.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$RemoveDownvote; + + TRes call({String? imageId}); +} + +class _CopyWithImpl$Variables$Mutation$RemoveDownvote + implements CopyWith$Variables$Mutation$RemoveDownvote { + _CopyWithImpl$Variables$Mutation$RemoveDownvote( + this._instance, + this._then, + ); + + final Variables$Mutation$RemoveDownvote _instance; + + final TRes Function(Variables$Mutation$RemoveDownvote) _then; + + static const _undefined = {}; + + TRes call({Object? imageId = _undefined}) => + _then(Variables$Mutation$RemoveDownvote._({ + ..._instance._$data, + if (imageId != _undefined && imageId != null) + 'imageId': (imageId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$RemoveDownvote + implements CopyWith$Variables$Mutation$RemoveDownvote { + _CopyWithStubImpl$Variables$Mutation$RemoveDownvote(this._res); + + TRes _res; + + call({String? imageId}) => _res; +} + +class Mutation$RemoveDownvote { + Mutation$RemoveDownvote({ + required this.removeDownvote, + this.$__typename = 'MutationRoot', + }); + + factory Mutation$RemoveDownvote.fromJson(Map json) { + final l$removeDownvote = json['removeDownvote']; + final l$$__typename = json['__typename']; + return Mutation$RemoveDownvote( + removeDownvote: (l$removeDownvote as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool removeDownvote; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$removeDownvote = removeDownvote; + _resultData['removeDownvote'] = l$removeDownvote; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$removeDownvote = removeDownvote; + final l$$__typename = $__typename; + return Object.hashAll([ + l$removeDownvote, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RemoveDownvote) || + runtimeType != other.runtimeType) { + return false; + } + final l$removeDownvote = removeDownvote; + final lOther$removeDownvote = other.removeDownvote; + if (l$removeDownvote != lOther$removeDownvote) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RemoveDownvote on Mutation$RemoveDownvote { + CopyWith$Mutation$RemoveDownvote get copyWith => + CopyWith$Mutation$RemoveDownvote( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RemoveDownvote { + factory CopyWith$Mutation$RemoveDownvote( + Mutation$RemoveDownvote instance, + TRes Function(Mutation$RemoveDownvote) then, + ) = _CopyWithImpl$Mutation$RemoveDownvote; + + factory CopyWith$Mutation$RemoveDownvote.stub(TRes res) = + _CopyWithStubImpl$Mutation$RemoveDownvote; + + TRes call({ + bool? removeDownvote, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$RemoveDownvote + implements CopyWith$Mutation$RemoveDownvote { + _CopyWithImpl$Mutation$RemoveDownvote( + this._instance, + this._then, + ); + + final Mutation$RemoveDownvote _instance; + + final TRes Function(Mutation$RemoveDownvote) _then; + + static const _undefined = {}; + + TRes call({ + Object? removeDownvote = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RemoveDownvote( + removeDownvote: removeDownvote == _undefined || removeDownvote == null + ? _instance.removeDownvote + : (removeDownvote as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$RemoveDownvote + implements CopyWith$Mutation$RemoveDownvote { + _CopyWithStubImpl$Mutation$RemoveDownvote(this._res); + + TRes _res; + + call({ + bool? removeDownvote, + String? $__typename, + }) => + _res; +} + +const documentNodeMutationRemoveDownvote = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RemoveDownvote'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'imageId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'removeDownvote'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'imageId'), + value: VariableNode(name: NameNode(value: 'imageId')), + ) + ], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Mutation$RemoveDownvote _parserFn$Mutation$RemoveDownvote( + Map data) => + Mutation$RemoveDownvote.fromJson(data); +typedef OnMutationCompleted$Mutation$RemoveDownvote = FutureOr Function( + Map?, + Mutation$RemoveDownvote?, +); + +class Options$Mutation$RemoveDownvote + extends graphql.MutationOptions { + Options$Mutation$RemoveDownvote({ + String? operationName, + required Variables$Mutation$RemoveDownvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveDownvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RemoveDownvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$RemoveDownvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRemoveDownvote, + parserFn: _parserFn$Mutation$RemoveDownvote, + ); + + final OnMutationCompleted$Mutation$RemoveDownvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RemoveDownvote + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RemoveDownvote({ + String? operationName, + required Variables$Mutation$RemoveDownvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveDownvote? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRemoveDownvote, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RemoveDownvote, + ); +} + +extension ClientExtension$Mutation$RemoveDownvote on graphql.GraphQLClient { + Future> mutate$RemoveDownvote( + Options$Mutation$RemoveDownvote options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$RemoveDownvote( + WatchOptions$Mutation$RemoveDownvote options) => + this.watchMutation(options); +} + +class Mutation$RemoveDownvote$HookResult { + Mutation$RemoveDownvote$HookResult( + this.runMutation, + this.result, + ); + + final RunMutation$Mutation$RemoveDownvote runMutation; + + final graphql.QueryResult result; +} + +Mutation$RemoveDownvote$HookResult useMutation$RemoveDownvote( + [WidgetOptions$Mutation$RemoveDownvote? options]) { + final result = graphql_flutter + .useMutation(options ?? WidgetOptions$Mutation$RemoveDownvote()); + return Mutation$RemoveDownvote$HookResult( + (variables, {optimisticResult, typedOptimisticResult}) => + result.runMutation( + variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result.result, + ); +} + +graphql.ObservableQuery + useWatchMutation$RemoveDownvote( + WatchOptions$Mutation$RemoveDownvote options) => + graphql_flutter.useWatchMutation(options); + +class WidgetOptions$Mutation$RemoveDownvote + extends graphql.MutationOptions { + WidgetOptions$Mutation$RemoveDownvote({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveDownvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RemoveDownvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$RemoveDownvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRemoveDownvote, + parserFn: _parserFn$Mutation$RemoveDownvote, + ); + + final OnMutationCompleted$Mutation$RemoveDownvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +typedef RunMutation$Mutation$RemoveDownvote + = graphql.MultiSourceResult Function( + Variables$Mutation$RemoveDownvote, { + Object? optimisticResult, + Mutation$RemoveDownvote? typedOptimisticResult, +}); +typedef Builder$Mutation$RemoveDownvote = widgets.Widget Function( + RunMutation$Mutation$RemoveDownvote, + graphql.QueryResult?, +); + +class Mutation$RemoveDownvote$Widget + extends graphql_flutter.Mutation { + Mutation$RemoveDownvote$Widget({ + widgets.Key? key, + WidgetOptions$Mutation$RemoveDownvote? options, + required Builder$Mutation$RemoveDownvote builder, + }) : super( + key: key, + options: options ?? WidgetOptions$Mutation$RemoveDownvote(), + builder: ( + run, + result, + ) => + builder( + ( + variables, { + optimisticResult, + typedOptimisticResult, + }) => + run( + variables.toJson(), + optimisticResult: + optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result, + ), + ); +} + +class Variables$Mutation$RemoveUpvote { + factory Variables$Mutation$RemoveUpvote({required String imageId}) => + Variables$Mutation$RemoveUpvote._({ + r'imageId': imageId, + }); + + Variables$Mutation$RemoveUpvote._(this._$data); + + factory Variables$Mutation$RemoveUpvote.fromJson(Map data) { + final result$data = {}; + final l$imageId = data['imageId']; + result$data['imageId'] = (l$imageId as String); + return Variables$Mutation$RemoveUpvote._(result$data); + } + + Map _$data; + + String get imageId => (_$data['imageId'] as String); + Map toJson() { + final result$data = {}; + final l$imageId = imageId; + result$data['imageId'] = l$imageId; + return result$data; + } + + CopyWith$Variables$Mutation$RemoveUpvote + get copyWith => CopyWith$Variables$Mutation$RemoveUpvote( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$RemoveUpvote) || + runtimeType != other.runtimeType) { + return false; + } + final l$imageId = imageId; + final lOther$imageId = other.imageId; + if (l$imageId != lOther$imageId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$imageId = imageId; + return Object.hashAll([l$imageId]); + } +} + +abstract class CopyWith$Variables$Mutation$RemoveUpvote { + factory CopyWith$Variables$Mutation$RemoveUpvote( + Variables$Mutation$RemoveUpvote instance, + TRes Function(Variables$Mutation$RemoveUpvote) then, + ) = _CopyWithImpl$Variables$Mutation$RemoveUpvote; + + factory CopyWith$Variables$Mutation$RemoveUpvote.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$RemoveUpvote; + + TRes call({String? imageId}); +} + +class _CopyWithImpl$Variables$Mutation$RemoveUpvote + implements CopyWith$Variables$Mutation$RemoveUpvote { + _CopyWithImpl$Variables$Mutation$RemoveUpvote( + this._instance, + this._then, + ); + + final Variables$Mutation$RemoveUpvote _instance; + + final TRes Function(Variables$Mutation$RemoveUpvote) _then; + + static const _undefined = {}; + + TRes call({Object? imageId = _undefined}) => + _then(Variables$Mutation$RemoveUpvote._({ + ..._instance._$data, + if (imageId != _undefined && imageId != null) + 'imageId': (imageId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$RemoveUpvote + implements CopyWith$Variables$Mutation$RemoveUpvote { + _CopyWithStubImpl$Variables$Mutation$RemoveUpvote(this._res); + + TRes _res; + + call({String? imageId}) => _res; +} + +class Mutation$RemoveUpvote { + Mutation$RemoveUpvote({ + required this.removeUpvote, + this.$__typename = 'MutationRoot', + }); + + factory Mutation$RemoveUpvote.fromJson(Map json) { + final l$removeUpvote = json['removeUpvote']; + final l$$__typename = json['__typename']; + return Mutation$RemoveUpvote( + removeUpvote: (l$removeUpvote as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool removeUpvote; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$removeUpvote = removeUpvote; + _resultData['removeUpvote'] = l$removeUpvote; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$removeUpvote = removeUpvote; + final l$$__typename = $__typename; + return Object.hashAll([ + l$removeUpvote, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RemoveUpvote) || runtimeType != other.runtimeType) { + return false; + } + final l$removeUpvote = removeUpvote; + final lOther$removeUpvote = other.removeUpvote; + if (l$removeUpvote != lOther$removeUpvote) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RemoveUpvote on Mutation$RemoveUpvote { + CopyWith$Mutation$RemoveUpvote get copyWith => + CopyWith$Mutation$RemoveUpvote( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RemoveUpvote { + factory CopyWith$Mutation$RemoveUpvote( + Mutation$RemoveUpvote instance, + TRes Function(Mutation$RemoveUpvote) then, + ) = _CopyWithImpl$Mutation$RemoveUpvote; + + factory CopyWith$Mutation$RemoveUpvote.stub(TRes res) = + _CopyWithStubImpl$Mutation$RemoveUpvote; + + TRes call({ + bool? removeUpvote, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$RemoveUpvote + implements CopyWith$Mutation$RemoveUpvote { + _CopyWithImpl$Mutation$RemoveUpvote( + this._instance, + this._then, + ); + + final Mutation$RemoveUpvote _instance; + + final TRes Function(Mutation$RemoveUpvote) _then; + + static const _undefined = {}; + + TRes call({ + Object? removeUpvote = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RemoveUpvote( + removeUpvote: removeUpvote == _undefined || removeUpvote == null + ? _instance.removeUpvote + : (removeUpvote as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$RemoveUpvote + implements CopyWith$Mutation$RemoveUpvote { + _CopyWithStubImpl$Mutation$RemoveUpvote(this._res); + + TRes _res; + + call({ + bool? removeUpvote, + String? $__typename, + }) => + _res; +} + +const documentNodeMutationRemoveUpvote = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RemoveUpvote'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'imageId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'removeUpvote'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'imageId'), + value: VariableNode(name: NameNode(value: 'imageId')), + ) + ], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Mutation$RemoveUpvote _parserFn$Mutation$RemoveUpvote( + Map data) => + Mutation$RemoveUpvote.fromJson(data); +typedef OnMutationCompleted$Mutation$RemoveUpvote = FutureOr Function( + Map?, + Mutation$RemoveUpvote?, +); + +class Options$Mutation$RemoveUpvote + extends graphql.MutationOptions { + Options$Mutation$RemoveUpvote({ + String? operationName, + required Variables$Mutation$RemoveUpvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveUpvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RemoveUpvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$RemoveUpvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRemoveUpvote, + parserFn: _parserFn$Mutation$RemoveUpvote, + ); + + final OnMutationCompleted$Mutation$RemoveUpvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RemoveUpvote + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RemoveUpvote({ + String? operationName, + required Variables$Mutation$RemoveUpvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveUpvote? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRemoveUpvote, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RemoveUpvote, + ); +} + +extension ClientExtension$Mutation$RemoveUpvote on graphql.GraphQLClient { + Future> mutate$RemoveUpvote( + Options$Mutation$RemoveUpvote options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$RemoveUpvote( + WatchOptions$Mutation$RemoveUpvote options) => + this.watchMutation(options); +} + +class Mutation$RemoveUpvote$HookResult { + Mutation$RemoveUpvote$HookResult( + this.runMutation, + this.result, + ); + + final RunMutation$Mutation$RemoveUpvote runMutation; + + final graphql.QueryResult result; +} + +Mutation$RemoveUpvote$HookResult useMutation$RemoveUpvote( + [WidgetOptions$Mutation$RemoveUpvote? options]) { + final result = graphql_flutter + .useMutation(options ?? WidgetOptions$Mutation$RemoveUpvote()); + return Mutation$RemoveUpvote$HookResult( + (variables, {optimisticResult, typedOptimisticResult}) => + result.runMutation( + variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result.result, + ); +} + +graphql.ObservableQuery useWatchMutation$RemoveUpvote( + WatchOptions$Mutation$RemoveUpvote options) => + graphql_flutter.useWatchMutation(options); + +class WidgetOptions$Mutation$RemoveUpvote + extends graphql.MutationOptions { + WidgetOptions$Mutation$RemoveUpvote({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveUpvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RemoveUpvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$RemoveUpvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRemoveUpvote, + parserFn: _parserFn$Mutation$RemoveUpvote, + ); + + final OnMutationCompleted$Mutation$RemoveUpvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +typedef RunMutation$Mutation$RemoveUpvote + = graphql.MultiSourceResult Function( + Variables$Mutation$RemoveUpvote, { + Object? optimisticResult, + Mutation$RemoveUpvote? typedOptimisticResult, +}); +typedef Builder$Mutation$RemoveUpvote = widgets.Widget Function( + RunMutation$Mutation$RemoveUpvote, + graphql.QueryResult?, +); + +class Mutation$RemoveUpvote$Widget + extends graphql_flutter.Mutation { + Mutation$RemoveUpvote$Widget({ + widgets.Key? key, + WidgetOptions$Mutation$RemoveUpvote? options, + required Builder$Mutation$RemoveUpvote builder, + }) : super( + key: key, + options: options ?? WidgetOptions$Mutation$RemoveUpvote(), + builder: ( + run, + result, + ) => + builder( + ( + variables, { + optimisticResult, + typedOptimisticResult, + }) => + run( + variables.toJson(), + optimisticResult: + optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result, + ), + ); +} + +class Variables$Mutation$AddDownvote { + factory Variables$Mutation$AddDownvote({required String imageId}) => + Variables$Mutation$AddDownvote._({ + r'imageId': imageId, + }); + + Variables$Mutation$AddDownvote._(this._$data); + + factory Variables$Mutation$AddDownvote.fromJson(Map data) { + final result$data = {}; + final l$imageId = data['imageId']; + result$data['imageId'] = (l$imageId as String); + return Variables$Mutation$AddDownvote._(result$data); + } + + Map _$data; + + String get imageId => (_$data['imageId'] as String); + Map toJson() { + final result$data = {}; + final l$imageId = imageId; + result$data['imageId'] = l$imageId; + return result$data; + } + + CopyWith$Variables$Mutation$AddDownvote + get copyWith => CopyWith$Variables$Mutation$AddDownvote( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$AddDownvote) || + runtimeType != other.runtimeType) { + return false; + } + final l$imageId = imageId; + final lOther$imageId = other.imageId; + if (l$imageId != lOther$imageId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$imageId = imageId; + return Object.hashAll([l$imageId]); + } +} + +abstract class CopyWith$Variables$Mutation$AddDownvote { + factory CopyWith$Variables$Mutation$AddDownvote( + Variables$Mutation$AddDownvote instance, + TRes Function(Variables$Mutation$AddDownvote) then, + ) = _CopyWithImpl$Variables$Mutation$AddDownvote; + + factory CopyWith$Variables$Mutation$AddDownvote.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$AddDownvote; + + TRes call({String? imageId}); +} + +class _CopyWithImpl$Variables$Mutation$AddDownvote + implements CopyWith$Variables$Mutation$AddDownvote { + _CopyWithImpl$Variables$Mutation$AddDownvote( + this._instance, + this._then, + ); + + final Variables$Mutation$AddDownvote _instance; + + final TRes Function(Variables$Mutation$AddDownvote) _then; + + static const _undefined = {}; + + TRes call({Object? imageId = _undefined}) => + _then(Variables$Mutation$AddDownvote._({ + ..._instance._$data, + if (imageId != _undefined && imageId != null) + 'imageId': (imageId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$AddDownvote + implements CopyWith$Variables$Mutation$AddDownvote { + _CopyWithStubImpl$Variables$Mutation$AddDownvote(this._res); + + TRes _res; + + call({String? imageId}) => _res; +} + +class Mutation$AddDownvote { + Mutation$AddDownvote({ + required this.addDownvote, + this.$__typename = 'MutationRoot', + }); + + factory Mutation$AddDownvote.fromJson(Map json) { + final l$addDownvote = json['addDownvote']; + final l$$__typename = json['__typename']; + return Mutation$AddDownvote( + addDownvote: (l$addDownvote as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool addDownvote; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$addDownvote = addDownvote; + _resultData['addDownvote'] = l$addDownvote; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$addDownvote = addDownvote; + final l$$__typename = $__typename; + return Object.hashAll([ + l$addDownvote, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$AddDownvote) || runtimeType != other.runtimeType) { + return false; + } + final l$addDownvote = addDownvote; + final lOther$addDownvote = other.addDownvote; + if (l$addDownvote != lOther$addDownvote) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$AddDownvote on Mutation$AddDownvote { + CopyWith$Mutation$AddDownvote get copyWith => + CopyWith$Mutation$AddDownvote( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$AddDownvote { + factory CopyWith$Mutation$AddDownvote( + Mutation$AddDownvote instance, + TRes Function(Mutation$AddDownvote) then, + ) = _CopyWithImpl$Mutation$AddDownvote; + + factory CopyWith$Mutation$AddDownvote.stub(TRes res) = + _CopyWithStubImpl$Mutation$AddDownvote; + + TRes call({ + bool? addDownvote, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$AddDownvote + implements CopyWith$Mutation$AddDownvote { + _CopyWithImpl$Mutation$AddDownvote( + this._instance, + this._then, + ); + + final Mutation$AddDownvote _instance; + + final TRes Function(Mutation$AddDownvote) _then; + + static const _undefined = {}; + + TRes call({ + Object? addDownvote = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$AddDownvote( + addDownvote: addDownvote == _undefined || addDownvote == null + ? _instance.addDownvote + : (addDownvote as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$AddDownvote + implements CopyWith$Mutation$AddDownvote { + _CopyWithStubImpl$Mutation$AddDownvote(this._res); + + TRes _res; + + call({ + bool? addDownvote, + String? $__typename, + }) => + _res; +} + +const documentNodeMutationAddDownvote = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'AddDownvote'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'imageId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'addDownvote'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'imageId'), + value: VariableNode(name: NameNode(value: 'imageId')), + ) + ], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Mutation$AddDownvote _parserFn$Mutation$AddDownvote( + Map data) => + Mutation$AddDownvote.fromJson(data); +typedef OnMutationCompleted$Mutation$AddDownvote = FutureOr Function( + Map?, + Mutation$AddDownvote?, +); + +class Options$Mutation$AddDownvote + extends graphql.MutationOptions { + Options$Mutation$AddDownvote({ + String? operationName, + required Variables$Mutation$AddDownvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$AddDownvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$AddDownvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$AddDownvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationAddDownvote, + parserFn: _parserFn$Mutation$AddDownvote, + ); + + final OnMutationCompleted$Mutation$AddDownvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$AddDownvote + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$AddDownvote({ + String? operationName, + required Variables$Mutation$AddDownvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$AddDownvote? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationAddDownvote, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$AddDownvote, + ); +} + +extension ClientExtension$Mutation$AddDownvote on graphql.GraphQLClient { + Future> mutate$AddDownvote( + Options$Mutation$AddDownvote options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$AddDownvote( + WatchOptions$Mutation$AddDownvote options) => + this.watchMutation(options); +} + +class Mutation$AddDownvote$HookResult { + Mutation$AddDownvote$HookResult( + this.runMutation, + this.result, + ); + + final RunMutation$Mutation$AddDownvote runMutation; + + final graphql.QueryResult result; +} + +Mutation$AddDownvote$HookResult useMutation$AddDownvote( + [WidgetOptions$Mutation$AddDownvote? options]) { + final result = graphql_flutter + .useMutation(options ?? WidgetOptions$Mutation$AddDownvote()); + return Mutation$AddDownvote$HookResult( + (variables, {optimisticResult, typedOptimisticResult}) => + result.runMutation( + variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result.result, + ); +} + +graphql.ObservableQuery useWatchMutation$AddDownvote( + WatchOptions$Mutation$AddDownvote options) => + graphql_flutter.useWatchMutation(options); + +class WidgetOptions$Mutation$AddDownvote + extends graphql.MutationOptions { + WidgetOptions$Mutation$AddDownvote({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$AddDownvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$AddDownvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$AddDownvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationAddDownvote, + parserFn: _parserFn$Mutation$AddDownvote, + ); + + final OnMutationCompleted$Mutation$AddDownvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +typedef RunMutation$Mutation$AddDownvote + = graphql.MultiSourceResult Function( + Variables$Mutation$AddDownvote, { + Object? optimisticResult, + Mutation$AddDownvote? typedOptimisticResult, +}); +typedef Builder$Mutation$AddDownvote = widgets.Widget Function( + RunMutation$Mutation$AddDownvote, + graphql.QueryResult?, +); + +class Mutation$AddDownvote$Widget + extends graphql_flutter.Mutation { + Mutation$AddDownvote$Widget({ + widgets.Key? key, + WidgetOptions$Mutation$AddDownvote? options, + required Builder$Mutation$AddDownvote builder, + }) : super( + key: key, + options: options ?? WidgetOptions$Mutation$AddDownvote(), + builder: ( + run, + result, + ) => + builder( + ( + variables, { + optimisticResult, + typedOptimisticResult, + }) => + run( + variables.toJson(), + optimisticResult: + optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result, + ), + ); +} + +class Variables$Mutation$AddUpvote { + factory Variables$Mutation$AddUpvote({required String imageId}) => + Variables$Mutation$AddUpvote._({ + r'imageId': imageId, + }); + + Variables$Mutation$AddUpvote._(this._$data); + + factory Variables$Mutation$AddUpvote.fromJson(Map data) { + final result$data = {}; + final l$imageId = data['imageId']; + result$data['imageId'] = (l$imageId as String); + return Variables$Mutation$AddUpvote._(result$data); + } + + Map _$data; + + String get imageId => (_$data['imageId'] as String); + Map toJson() { + final result$data = {}; + final l$imageId = imageId; + result$data['imageId'] = l$imageId; + return result$data; + } + + CopyWith$Variables$Mutation$AddUpvote + get copyWith => CopyWith$Variables$Mutation$AddUpvote( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$AddUpvote) || + runtimeType != other.runtimeType) { + return false; + } + final l$imageId = imageId; + final lOther$imageId = other.imageId; + if (l$imageId != lOther$imageId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$imageId = imageId; + return Object.hashAll([l$imageId]); + } +} + +abstract class CopyWith$Variables$Mutation$AddUpvote { + factory CopyWith$Variables$Mutation$AddUpvote( + Variables$Mutation$AddUpvote instance, + TRes Function(Variables$Mutation$AddUpvote) then, + ) = _CopyWithImpl$Variables$Mutation$AddUpvote; + + factory CopyWith$Variables$Mutation$AddUpvote.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$AddUpvote; + + TRes call({String? imageId}); +} + +class _CopyWithImpl$Variables$Mutation$AddUpvote + implements CopyWith$Variables$Mutation$AddUpvote { + _CopyWithImpl$Variables$Mutation$AddUpvote( + this._instance, + this._then, + ); + + final Variables$Mutation$AddUpvote _instance; + + final TRes Function(Variables$Mutation$AddUpvote) _then; + + static const _undefined = {}; + + TRes call({Object? imageId = _undefined}) => + _then(Variables$Mutation$AddUpvote._({ + ..._instance._$data, + if (imageId != _undefined && imageId != null) + 'imageId': (imageId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$AddUpvote + implements CopyWith$Variables$Mutation$AddUpvote { + _CopyWithStubImpl$Variables$Mutation$AddUpvote(this._res); + + TRes _res; + + call({String? imageId}) => _res; +} + +class Mutation$AddUpvote { + Mutation$AddUpvote({ + required this.addUpvote, + this.$__typename = 'MutationRoot', + }); + + factory Mutation$AddUpvote.fromJson(Map json) { + final l$addUpvote = json['addUpvote']; + final l$$__typename = json['__typename']; + return Mutation$AddUpvote( + addUpvote: (l$addUpvote as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool addUpvote; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$addUpvote = addUpvote; + _resultData['addUpvote'] = l$addUpvote; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$addUpvote = addUpvote; + final l$$__typename = $__typename; + return Object.hashAll([ + l$addUpvote, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$AddUpvote) || runtimeType != other.runtimeType) { + return false; + } + final l$addUpvote = addUpvote; + final lOther$addUpvote = other.addUpvote; + if (l$addUpvote != lOther$addUpvote) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$AddUpvote on Mutation$AddUpvote { + CopyWith$Mutation$AddUpvote get copyWith => + CopyWith$Mutation$AddUpvote( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$AddUpvote { + factory CopyWith$Mutation$AddUpvote( + Mutation$AddUpvote instance, + TRes Function(Mutation$AddUpvote) then, + ) = _CopyWithImpl$Mutation$AddUpvote; + + factory CopyWith$Mutation$AddUpvote.stub(TRes res) = + _CopyWithStubImpl$Mutation$AddUpvote; + + TRes call({ + bool? addUpvote, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$AddUpvote + implements CopyWith$Mutation$AddUpvote { + _CopyWithImpl$Mutation$AddUpvote( + this._instance, + this._then, + ); + + final Mutation$AddUpvote _instance; + + final TRes Function(Mutation$AddUpvote) _then; + + static const _undefined = {}; + + TRes call({ + Object? addUpvote = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$AddUpvote( + addUpvote: addUpvote == _undefined || addUpvote == null + ? _instance.addUpvote + : (addUpvote as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$AddUpvote + implements CopyWith$Mutation$AddUpvote { + _CopyWithStubImpl$Mutation$AddUpvote(this._res); + + TRes _res; + + call({ + bool? addUpvote, + String? $__typename, + }) => + _res; +} + +const documentNodeMutationAddUpvote = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'AddUpvote'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'imageId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'addUpvote'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'imageId'), + value: VariableNode(name: NameNode(value: 'imageId')), + ) + ], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Mutation$AddUpvote _parserFn$Mutation$AddUpvote(Map data) => + Mutation$AddUpvote.fromJson(data); +typedef OnMutationCompleted$Mutation$AddUpvote = FutureOr Function( + Map?, + Mutation$AddUpvote?, +); + +class Options$Mutation$AddUpvote + extends graphql.MutationOptions { + Options$Mutation$AddUpvote({ + String? operationName, + required Variables$Mutation$AddUpvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$AddUpvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$AddUpvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$AddUpvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationAddUpvote, + parserFn: _parserFn$Mutation$AddUpvote, + ); + + final OnMutationCompleted$Mutation$AddUpvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$AddUpvote + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$AddUpvote({ + String? operationName, + required Variables$Mutation$AddUpvote variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$AddUpvote? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationAddUpvote, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$AddUpvote, + ); +} + +extension ClientExtension$Mutation$AddUpvote on graphql.GraphQLClient { + Future> mutate$AddUpvote( + Options$Mutation$AddUpvote options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$AddUpvote( + WatchOptions$Mutation$AddUpvote options) => + this.watchMutation(options); +} + +class Mutation$AddUpvote$HookResult { + Mutation$AddUpvote$HookResult( + this.runMutation, + this.result, + ); + + final RunMutation$Mutation$AddUpvote runMutation; + + final graphql.QueryResult result; +} + +Mutation$AddUpvote$HookResult useMutation$AddUpvote( + [WidgetOptions$Mutation$AddUpvote? options]) { + final result = graphql_flutter + .useMutation(options ?? WidgetOptions$Mutation$AddUpvote()); + return Mutation$AddUpvote$HookResult( + (variables, {optimisticResult, typedOptimisticResult}) => + result.runMutation( + variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result.result, + ); +} + +graphql.ObservableQuery useWatchMutation$AddUpvote( + WatchOptions$Mutation$AddUpvote options) => + graphql_flutter.useWatchMutation(options); + +class WidgetOptions$Mutation$AddUpvote + extends graphql.MutationOptions { + WidgetOptions$Mutation$AddUpvote({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$AddUpvote? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$AddUpvote? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$AddUpvote(data), + ), + update: update, + onError: onError, + document: documentNodeMutationAddUpvote, + parserFn: _parserFn$Mutation$AddUpvote, + ); + + final OnMutationCompleted$Mutation$AddUpvote? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +typedef RunMutation$Mutation$AddUpvote + = graphql.MultiSourceResult Function( + Variables$Mutation$AddUpvote, { + Object? optimisticResult, + Mutation$AddUpvote? typedOptimisticResult, +}); +typedef Builder$Mutation$AddUpvote = widgets.Widget Function( + RunMutation$Mutation$AddUpvote, + graphql.QueryResult?, +); + +class Mutation$AddUpvote$Widget + extends graphql_flutter.Mutation { + Mutation$AddUpvote$Widget({ + widgets.Key? key, + WidgetOptions$Mutation$AddUpvote? options, + required Builder$Mutation$AddUpvote builder, + }) : super( + key: key, + options: options ?? WidgetOptions$Mutation$AddUpvote(), + builder: ( + run, + result, + ) => + builder( + ( + variables, { + optimisticResult, + typedOptimisticResult, + }) => + run( + variables.toJson(), + optimisticResult: + optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result, + ), + ); +} + +class Variables$Mutation$LinkImage { + factory Variables$Mutation$LinkImage({ + required String mealId, + required String imageUrl, + }) => + Variables$Mutation$LinkImage._({ + r'mealId': mealId, + r'imageUrl': imageUrl, + }); + + Variables$Mutation$LinkImage._(this._$data); + + factory Variables$Mutation$LinkImage.fromJson(Map data) { + final result$data = {}; + final l$mealId = data['mealId']; + result$data['mealId'] = (l$mealId as String); + final l$imageUrl = data['imageUrl']; + result$data['imageUrl'] = (l$imageUrl as String); + return Variables$Mutation$LinkImage._(result$data); + } + + Map _$data; + + String get mealId => (_$data['mealId'] as String); + String get imageUrl => (_$data['imageUrl'] as String); + Map toJson() { + final result$data = {}; + final l$mealId = mealId; + result$data['mealId'] = l$mealId; + final l$imageUrl = imageUrl; + result$data['imageUrl'] = l$imageUrl; + return result$data; + } + + CopyWith$Variables$Mutation$LinkImage + get copyWith => CopyWith$Variables$Mutation$LinkImage( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$LinkImage) || + runtimeType != other.runtimeType) { + return false; + } + final l$mealId = mealId; + final lOther$mealId = other.mealId; + if (l$mealId != lOther$mealId) { + return false; + } + final l$imageUrl = imageUrl; + final lOther$imageUrl = other.imageUrl; + if (l$imageUrl != lOther$imageUrl) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$mealId = mealId; + final l$imageUrl = imageUrl; + return Object.hashAll([ + l$mealId, + l$imageUrl, + ]); + } +} + +abstract class CopyWith$Variables$Mutation$LinkImage { + factory CopyWith$Variables$Mutation$LinkImage( + Variables$Mutation$LinkImage instance, + TRes Function(Variables$Mutation$LinkImage) then, + ) = _CopyWithImpl$Variables$Mutation$LinkImage; + + factory CopyWith$Variables$Mutation$LinkImage.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$LinkImage; + + TRes call({ + String? mealId, + String? imageUrl, + }); +} + +class _CopyWithImpl$Variables$Mutation$LinkImage + implements CopyWith$Variables$Mutation$LinkImage { + _CopyWithImpl$Variables$Mutation$LinkImage( + this._instance, + this._then, + ); + + final Variables$Mutation$LinkImage _instance; + + final TRes Function(Variables$Mutation$LinkImage) _then; + + static const _undefined = {}; + + TRes call({ + Object? mealId = _undefined, + Object? imageUrl = _undefined, + }) => + _then(Variables$Mutation$LinkImage._({ + ..._instance._$data, + if (mealId != _undefined && mealId != null) + 'mealId': (mealId as String), + if (imageUrl != _undefined && imageUrl != null) + 'imageUrl': (imageUrl as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$LinkImage + implements CopyWith$Variables$Mutation$LinkImage { + _CopyWithStubImpl$Variables$Mutation$LinkImage(this._res); + + TRes _res; + + call({ + String? mealId, + String? imageUrl, + }) => + _res; +} + +class Mutation$LinkImage { + Mutation$LinkImage({ + required this.addImage, + this.$__typename = 'MutationRoot', + }); + + factory Mutation$LinkImage.fromJson(Map json) { + final l$addImage = json['addImage']; + final l$$__typename = json['__typename']; + return Mutation$LinkImage( + addImage: (l$addImage as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool addImage; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$addImage = addImage; + _resultData['addImage'] = l$addImage; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$addImage = addImage; + final l$$__typename = $__typename; + return Object.hashAll([ + l$addImage, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$LinkImage) || runtimeType != other.runtimeType) { + return false; + } + final l$addImage = addImage; + final lOther$addImage = other.addImage; + if (l$addImage != lOther$addImage) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$LinkImage on Mutation$LinkImage { + CopyWith$Mutation$LinkImage get copyWith => + CopyWith$Mutation$LinkImage( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$LinkImage { + factory CopyWith$Mutation$LinkImage( + Mutation$LinkImage instance, + TRes Function(Mutation$LinkImage) then, + ) = _CopyWithImpl$Mutation$LinkImage; + + factory CopyWith$Mutation$LinkImage.stub(TRes res) = + _CopyWithStubImpl$Mutation$LinkImage; + + TRes call({ + bool? addImage, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$LinkImage + implements CopyWith$Mutation$LinkImage { + _CopyWithImpl$Mutation$LinkImage( + this._instance, + this._then, + ); + + final Mutation$LinkImage _instance; + + final TRes Function(Mutation$LinkImage) _then; + + static const _undefined = {}; + + TRes call({ + Object? addImage = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$LinkImage( + addImage: addImage == _undefined || addImage == null + ? _instance.addImage + : (addImage as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$LinkImage + implements CopyWith$Mutation$LinkImage { + _CopyWithStubImpl$Mutation$LinkImage(this._res); + + TRes _res; + + call({ + bool? addImage, + String? $__typename, + }) => + _res; +} + +const documentNodeMutationLinkImage = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'LinkImage'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'mealId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ), + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'imageUrl')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ), + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'addImage'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'mealId'), + value: VariableNode(name: NameNode(value: 'mealId')), + ), + ArgumentNode( + name: NameNode(value: 'imageUrl'), + value: VariableNode(name: NameNode(value: 'imageUrl')), + ), + ], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Mutation$LinkImage _parserFn$Mutation$LinkImage(Map data) => + Mutation$LinkImage.fromJson(data); +typedef OnMutationCompleted$Mutation$LinkImage = FutureOr Function( + Map?, + Mutation$LinkImage?, +); + +class Options$Mutation$LinkImage + extends graphql.MutationOptions { + Options$Mutation$LinkImage({ + String? operationName, + required Variables$Mutation$LinkImage variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$LinkImage? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$LinkImage? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$LinkImage(data), + ), + update: update, + onError: onError, + document: documentNodeMutationLinkImage, + parserFn: _parserFn$Mutation$LinkImage, + ); + + final OnMutationCompleted$Mutation$LinkImage? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$LinkImage + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$LinkImage({ + String? operationName, + required Variables$Mutation$LinkImage variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$LinkImage? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationLinkImage, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$LinkImage, + ); +} + +extension ClientExtension$Mutation$LinkImage on graphql.GraphQLClient { + Future> mutate$LinkImage( + Options$Mutation$LinkImage options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$LinkImage( + WatchOptions$Mutation$LinkImage options) => + this.watchMutation(options); +} + +class Mutation$LinkImage$HookResult { + Mutation$LinkImage$HookResult( + this.runMutation, + this.result, + ); + + final RunMutation$Mutation$LinkImage runMutation; + + final graphql.QueryResult result; +} + +Mutation$LinkImage$HookResult useMutation$LinkImage( + [WidgetOptions$Mutation$LinkImage? options]) { + final result = graphql_flutter + .useMutation(options ?? WidgetOptions$Mutation$LinkImage()); + return Mutation$LinkImage$HookResult( + (variables, {optimisticResult, typedOptimisticResult}) => + result.runMutation( + variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result.result, + ); +} + +graphql.ObservableQuery useWatchMutation$LinkImage( + WatchOptions$Mutation$LinkImage options) => + graphql_flutter.useWatchMutation(options); + +class WidgetOptions$Mutation$LinkImage + extends graphql.MutationOptions { + WidgetOptions$Mutation$LinkImage({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$LinkImage? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$LinkImage? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$LinkImage(data), + ), + update: update, + onError: onError, + document: documentNodeMutationLinkImage, + parserFn: _parserFn$Mutation$LinkImage, + ); + + final OnMutationCompleted$Mutation$LinkImage? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +typedef RunMutation$Mutation$LinkImage + = graphql.MultiSourceResult Function( + Variables$Mutation$LinkImage, { + Object? optimisticResult, + Mutation$LinkImage? typedOptimisticResult, +}); +typedef Builder$Mutation$LinkImage = widgets.Widget Function( + RunMutation$Mutation$LinkImage, + graphql.QueryResult?, +); + +class Mutation$LinkImage$Widget + extends graphql_flutter.Mutation { + Mutation$LinkImage$Widget({ + widgets.Key? key, + WidgetOptions$Mutation$LinkImage? options, + required Builder$Mutation$LinkImage builder, + }) : super( + key: key, + options: options ?? WidgetOptions$Mutation$LinkImage(), + builder: ( + run, + result, + ) => + builder( + ( + variables, { + optimisticResult, + typedOptimisticResult, + }) => + run( + variables.toJson(), + optimisticResult: + optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result, + ), + ); +} + +class Variables$Mutation$ReportImage { + factory Variables$Mutation$ReportImage({ + required String imageId, + required Enum$ReportReason reason, + }) => + Variables$Mutation$ReportImage._({ + r'imageId': imageId, + r'reason': reason, + }); + + Variables$Mutation$ReportImage._(this._$data); + + factory Variables$Mutation$ReportImage.fromJson(Map data) { + final result$data = {}; + final l$imageId = data['imageId']; + result$data['imageId'] = (l$imageId as String); + final l$reason = data['reason']; + result$data['reason'] = fromJson$Enum$ReportReason((l$reason as String)); + return Variables$Mutation$ReportImage._(result$data); + } + + Map _$data; + + String get imageId => (_$data['imageId'] as String); + Enum$ReportReason get reason => (_$data['reason'] as Enum$ReportReason); + Map toJson() { + final result$data = {}; + final l$imageId = imageId; + result$data['imageId'] = l$imageId; + final l$reason = reason; + result$data['reason'] = toJson$Enum$ReportReason(l$reason); + return result$data; + } + + CopyWith$Variables$Mutation$ReportImage + get copyWith => CopyWith$Variables$Mutation$ReportImage( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$ReportImage) || + runtimeType != other.runtimeType) { + return false; + } + final l$imageId = imageId; + final lOther$imageId = other.imageId; + if (l$imageId != lOther$imageId) { + return false; + } + final l$reason = reason; + final lOther$reason = other.reason; + if (l$reason != lOther$reason) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$imageId = imageId; + final l$reason = reason; + return Object.hashAll([ + l$imageId, + l$reason, + ]); + } +} + +abstract class CopyWith$Variables$Mutation$ReportImage { + factory CopyWith$Variables$Mutation$ReportImage( + Variables$Mutation$ReportImage instance, + TRes Function(Variables$Mutation$ReportImage) then, + ) = _CopyWithImpl$Variables$Mutation$ReportImage; + + factory CopyWith$Variables$Mutation$ReportImage.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$ReportImage; + + TRes call({ + String? imageId, + Enum$ReportReason? reason, + }); +} + +class _CopyWithImpl$Variables$Mutation$ReportImage + implements CopyWith$Variables$Mutation$ReportImage { + _CopyWithImpl$Variables$Mutation$ReportImage( + this._instance, + this._then, + ); + + final Variables$Mutation$ReportImage _instance; + + final TRes Function(Variables$Mutation$ReportImage) _then; + + static const _undefined = {}; + + TRes call({ + Object? imageId = _undefined, + Object? reason = _undefined, + }) => + _then(Variables$Mutation$ReportImage._({ + ..._instance._$data, + if (imageId != _undefined && imageId != null) + 'imageId': (imageId as String), + if (reason != _undefined && reason != null) + 'reason': (reason as Enum$ReportReason), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$ReportImage + implements CopyWith$Variables$Mutation$ReportImage { + _CopyWithStubImpl$Variables$Mutation$ReportImage(this._res); + + TRes _res; + + call({ + String? imageId, + Enum$ReportReason? reason, + }) => + _res; +} + +class Mutation$ReportImage { + Mutation$ReportImage({ + required this.reportImage, + this.$__typename = 'MutationRoot', + }); + + factory Mutation$ReportImage.fromJson(Map json) { + final l$reportImage = json['reportImage']; + final l$$__typename = json['__typename']; + return Mutation$ReportImage( + reportImage: (l$reportImage as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool reportImage; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$reportImage = reportImage; + _resultData['reportImage'] = l$reportImage; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$reportImage = reportImage; + final l$$__typename = $__typename; + return Object.hashAll([ + l$reportImage, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ReportImage) || runtimeType != other.runtimeType) { + return false; + } + final l$reportImage = reportImage; + final lOther$reportImage = other.reportImage; + if (l$reportImage != lOther$reportImage) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ReportImage on Mutation$ReportImage { + CopyWith$Mutation$ReportImage get copyWith => + CopyWith$Mutation$ReportImage( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ReportImage { + factory CopyWith$Mutation$ReportImage( + Mutation$ReportImage instance, + TRes Function(Mutation$ReportImage) then, + ) = _CopyWithImpl$Mutation$ReportImage; + + factory CopyWith$Mutation$ReportImage.stub(TRes res) = + _CopyWithStubImpl$Mutation$ReportImage; + + TRes call({ + bool? reportImage, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$ReportImage + implements CopyWith$Mutation$ReportImage { + _CopyWithImpl$Mutation$ReportImage( + this._instance, + this._then, + ); + + final Mutation$ReportImage _instance; + + final TRes Function(Mutation$ReportImage) _then; + + static const _undefined = {}; + + TRes call({ + Object? reportImage = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$ReportImage( + reportImage: reportImage == _undefined || reportImage == null + ? _instance.reportImage + : (reportImage as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$ReportImage + implements CopyWith$Mutation$ReportImage { + _CopyWithStubImpl$Mutation$ReportImage(this._res); + + TRes _res; + + call({ + bool? reportImage, + String? $__typename, + }) => + _res; +} + +const documentNodeMutationReportImage = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'ReportImage'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'imageId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ), + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'reason')), + type: NamedTypeNode( + name: NameNode(value: 'ReportReason'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ), + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'reportImage'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'imageId'), + value: VariableNode(name: NameNode(value: 'imageId')), + ), + ArgumentNode( + name: NameNode(value: 'reason'), + value: VariableNode(name: NameNode(value: 'reason')), + ), + ], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Mutation$ReportImage _parserFn$Mutation$ReportImage( + Map data) => + Mutation$ReportImage.fromJson(data); +typedef OnMutationCompleted$Mutation$ReportImage = FutureOr Function( + Map?, + Mutation$ReportImage?, +); + +class Options$Mutation$ReportImage + extends graphql.MutationOptions { + Options$Mutation$ReportImage({ + String? operationName, + required Variables$Mutation$ReportImage variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ReportImage? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$ReportImage? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$ReportImage(data), + ), + update: update, + onError: onError, + document: documentNodeMutationReportImage, + parserFn: _parserFn$Mutation$ReportImage, + ); + + final OnMutationCompleted$Mutation$ReportImage? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$ReportImage + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$ReportImage({ + String? operationName, + required Variables$Mutation$ReportImage variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ReportImage? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationReportImage, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$ReportImage, + ); +} + +extension ClientExtension$Mutation$ReportImage on graphql.GraphQLClient { + Future> mutate$ReportImage( + Options$Mutation$ReportImage options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$ReportImage( + WatchOptions$Mutation$ReportImage options) => + this.watchMutation(options); +} + +class Mutation$ReportImage$HookResult { + Mutation$ReportImage$HookResult( + this.runMutation, + this.result, + ); + + final RunMutation$Mutation$ReportImage runMutation; + + final graphql.QueryResult result; +} + +Mutation$ReportImage$HookResult useMutation$ReportImage( + [WidgetOptions$Mutation$ReportImage? options]) { + final result = graphql_flutter + .useMutation(options ?? WidgetOptions$Mutation$ReportImage()); + return Mutation$ReportImage$HookResult( + (variables, {optimisticResult, typedOptimisticResult}) => + result.runMutation( + variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result.result, + ); +} + +graphql.ObservableQuery useWatchMutation$ReportImage( + WatchOptions$Mutation$ReportImage options) => + graphql_flutter.useWatchMutation(options); + +class WidgetOptions$Mutation$ReportImage + extends graphql.MutationOptions { + WidgetOptions$Mutation$ReportImage({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ReportImage? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$ReportImage? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$ReportImage(data), + ), + update: update, + onError: onError, + document: documentNodeMutationReportImage, + parserFn: _parserFn$Mutation$ReportImage, + ); + + final OnMutationCompleted$Mutation$ReportImage? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +typedef RunMutation$Mutation$ReportImage + = graphql.MultiSourceResult Function( + Variables$Mutation$ReportImage, { + Object? optimisticResult, + Mutation$ReportImage? typedOptimisticResult, +}); +typedef Builder$Mutation$ReportImage = widgets.Widget Function( + RunMutation$Mutation$ReportImage, + graphql.QueryResult?, +); + +class Mutation$ReportImage$Widget + extends graphql_flutter.Mutation { + Mutation$ReportImage$Widget({ + widgets.Key? key, + WidgetOptions$Mutation$ReportImage? options, + required Builder$Mutation$ReportImage builder, + }) : super( + key: key, + options: options ?? WidgetOptions$Mutation$ReportImage(), + builder: ( + run, + result, + ) => + builder( + ( + variables, { + optimisticResult, + typedOptimisticResult, + }) => + run( + variables.toJson(), + optimisticResult: + optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result, + ), + ); +} + +class Variables$Mutation$UpdateRating { + factory Variables$Mutation$UpdateRating({ + required String mealId, + required int rating, + }) => + Variables$Mutation$UpdateRating._({ + r'mealId': mealId, + r'rating': rating, + }); + + Variables$Mutation$UpdateRating._(this._$data); + + factory Variables$Mutation$UpdateRating.fromJson(Map data) { + final result$data = {}; + final l$mealId = data['mealId']; + result$data['mealId'] = (l$mealId as String); + final l$rating = data['rating']; + result$data['rating'] = (l$rating as int); + return Variables$Mutation$UpdateRating._(result$data); + } + + Map _$data; + + String get mealId => (_$data['mealId'] as String); + int get rating => (_$data['rating'] as int); + Map toJson() { + final result$data = {}; + final l$mealId = mealId; + result$data['mealId'] = l$mealId; + final l$rating = rating; + result$data['rating'] = l$rating; + return result$data; + } + + CopyWith$Variables$Mutation$UpdateRating + get copyWith => CopyWith$Variables$Mutation$UpdateRating( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$UpdateRating) || + runtimeType != other.runtimeType) { + return false; + } + final l$mealId = mealId; + final lOther$mealId = other.mealId; + if (l$mealId != lOther$mealId) { + return false; + } + final l$rating = rating; + final lOther$rating = other.rating; + if (l$rating != lOther$rating) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$mealId = mealId; + final l$rating = rating; + return Object.hashAll([ + l$mealId, + l$rating, + ]); + } +} + +abstract class CopyWith$Variables$Mutation$UpdateRating { + factory CopyWith$Variables$Mutation$UpdateRating( + Variables$Mutation$UpdateRating instance, + TRes Function(Variables$Mutation$UpdateRating) then, + ) = _CopyWithImpl$Variables$Mutation$UpdateRating; + + factory CopyWith$Variables$Mutation$UpdateRating.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$UpdateRating; + + TRes call({ + String? mealId, + int? rating, + }); +} + +class _CopyWithImpl$Variables$Mutation$UpdateRating + implements CopyWith$Variables$Mutation$UpdateRating { + _CopyWithImpl$Variables$Mutation$UpdateRating( + this._instance, + this._then, + ); + + final Variables$Mutation$UpdateRating _instance; + + final TRes Function(Variables$Mutation$UpdateRating) _then; + + static const _undefined = {}; + + TRes call({ + Object? mealId = _undefined, + Object? rating = _undefined, + }) => + _then(Variables$Mutation$UpdateRating._({ + ..._instance._$data, + if (mealId != _undefined && mealId != null) + 'mealId': (mealId as String), + if (rating != _undefined && rating != null) 'rating': (rating as int), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$UpdateRating + implements CopyWith$Variables$Mutation$UpdateRating { + _CopyWithStubImpl$Variables$Mutation$UpdateRating(this._res); + + TRes _res; + + call({ + String? mealId, + int? rating, + }) => + _res; +} + +class Mutation$UpdateRating { + Mutation$UpdateRating({ + required this.setRating, + this.$__typename = 'MutationRoot', + }); + + factory Mutation$UpdateRating.fromJson(Map json) { + final l$setRating = json['setRating']; + final l$$__typename = json['__typename']; + return Mutation$UpdateRating( + setRating: (l$setRating as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool setRating; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$setRating = setRating; + _resultData['setRating'] = l$setRating; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$setRating = setRating; + final l$$__typename = $__typename; + return Object.hashAll([ + l$setRating, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$UpdateRating) || runtimeType != other.runtimeType) { + return false; + } + final l$setRating = setRating; + final lOther$setRating = other.setRating; + if (l$setRating != lOther$setRating) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$UpdateRating on Mutation$UpdateRating { + CopyWith$Mutation$UpdateRating get copyWith => + CopyWith$Mutation$UpdateRating( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$UpdateRating { + factory CopyWith$Mutation$UpdateRating( + Mutation$UpdateRating instance, + TRes Function(Mutation$UpdateRating) then, + ) = _CopyWithImpl$Mutation$UpdateRating; + + factory CopyWith$Mutation$UpdateRating.stub(TRes res) = + _CopyWithStubImpl$Mutation$UpdateRating; + + TRes call({ + bool? setRating, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$UpdateRating + implements CopyWith$Mutation$UpdateRating { + _CopyWithImpl$Mutation$UpdateRating( + this._instance, + this._then, + ); + + final Mutation$UpdateRating _instance; + + final TRes Function(Mutation$UpdateRating) _then; + + static const _undefined = {}; + + TRes call({ + Object? setRating = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$UpdateRating( + setRating: setRating == _undefined || setRating == null + ? _instance.setRating + : (setRating as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$UpdateRating + implements CopyWith$Mutation$UpdateRating { + _CopyWithStubImpl$Mutation$UpdateRating(this._res); + + TRes _res; + + call({ + bool? setRating, + String? $__typename, + }) => + _res; +} + +const documentNodeMutationUpdateRating = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'UpdateRating'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'mealId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ), + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'rating')), + type: NamedTypeNode( + name: NameNode(value: 'Int'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ), + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'setRating'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'mealId'), + value: VariableNode(name: NameNode(value: 'mealId')), + ), + ArgumentNode( + name: NameNode(value: 'rating'), + value: VariableNode(name: NameNode(value: 'rating')), + ), + ], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Mutation$UpdateRating _parserFn$Mutation$UpdateRating( + Map data) => + Mutation$UpdateRating.fromJson(data); +typedef OnMutationCompleted$Mutation$UpdateRating = FutureOr Function( + Map?, + Mutation$UpdateRating?, +); + +class Options$Mutation$UpdateRating + extends graphql.MutationOptions { + Options$Mutation$UpdateRating({ + String? operationName, + required Variables$Mutation$UpdateRating variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$UpdateRating? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$UpdateRating? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$UpdateRating(data), + ), + update: update, + onError: onError, + document: documentNodeMutationUpdateRating, + parserFn: _parserFn$Mutation$UpdateRating, + ); + + final OnMutationCompleted$Mutation$UpdateRating? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$UpdateRating + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$UpdateRating({ + String? operationName, + required Variables$Mutation$UpdateRating variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$UpdateRating? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationUpdateRating, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$UpdateRating, + ); +} + +extension ClientExtension$Mutation$UpdateRating on graphql.GraphQLClient { + Future> mutate$UpdateRating( + Options$Mutation$UpdateRating options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$UpdateRating( + WatchOptions$Mutation$UpdateRating options) => + this.watchMutation(options); +} + +class Mutation$UpdateRating$HookResult { + Mutation$UpdateRating$HookResult( + this.runMutation, + this.result, + ); + + final RunMutation$Mutation$UpdateRating runMutation; + + final graphql.QueryResult result; +} + +Mutation$UpdateRating$HookResult useMutation$UpdateRating( + [WidgetOptions$Mutation$UpdateRating? options]) { + final result = graphql_flutter + .useMutation(options ?? WidgetOptions$Mutation$UpdateRating()); + return Mutation$UpdateRating$HookResult( + (variables, {optimisticResult, typedOptimisticResult}) => + result.runMutation( + variables.toJson(), + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result.result, + ); +} + +graphql.ObservableQuery useWatchMutation$UpdateRating( + WatchOptions$Mutation$UpdateRating options) => + graphql_flutter.useWatchMutation(options); + +class WidgetOptions$Mutation$UpdateRating + extends graphql.MutationOptions { + WidgetOptions$Mutation$UpdateRating({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$UpdateRating? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$UpdateRating? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$UpdateRating(data), + ), + update: update, + onError: onError, + document: documentNodeMutationUpdateRating, + parserFn: _parserFn$Mutation$UpdateRating, + ); + + final OnMutationCompleted$Mutation$UpdateRating? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +typedef RunMutation$Mutation$UpdateRating + = graphql.MultiSourceResult Function( + Variables$Mutation$UpdateRating, { + Object? optimisticResult, + Mutation$UpdateRating? typedOptimisticResult, +}); +typedef Builder$Mutation$UpdateRating = widgets.Widget Function( + RunMutation$Mutation$UpdateRating, + graphql.QueryResult?, +); + +class Mutation$UpdateRating$Widget + extends graphql_flutter.Mutation { + Mutation$UpdateRating$Widget({ + widgets.Key? key, + WidgetOptions$Mutation$UpdateRating? options, + required Builder$Mutation$UpdateRating builder, + }) : super( + key: key, + options: options ?? WidgetOptions$Mutation$UpdateRating(), + builder: ( + run, + result, + ) => + builder( + ( + variables, { + optimisticResult, + typedOptimisticResult, + }) => + run( + variables.toJson(), + optimisticResult: + optimisticResult ?? typedOptimisticResult?.toJson(), + ), + result, + ), + ); +} diff --git a/app/lib/model/api_server/requests/querys.graphql b/app/lib/model/api_server/requests/querys.graphql new file mode 100644 index 00000000..e69de29b diff --git a/app/lib/model/api_server/requests/querys.graphql.dart b/app/lib/model/api_server/requests/querys.graphql.dart new file mode 100644 index 00000000..7402ba88 --- /dev/null +++ b/app/lib/model/api_server/requests/querys.graphql.dart @@ -0,0 +1,244 @@ +import 'dart:async'; +import 'package:flutter/widgets.dart' as widgets; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; + +class Query$GetMeals { + Query$GetMeals({this.$__typename = 'QueryRoot'}); + + factory Query$GetMeals.fromJson(Map json) { + final l$$__typename = json['__typename']; + return Query$GetMeals($__typename: (l$$__typename as String)); + } + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$$__typename = $__typename; + return Object.hashAll([l$$__typename]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetMeals) || runtimeType != other.runtimeType) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetMeals on Query$GetMeals { + CopyWith$Query$GetMeals get copyWith => + CopyWith$Query$GetMeals( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetMeals { + factory CopyWith$Query$GetMeals( + Query$GetMeals instance, + TRes Function(Query$GetMeals) then, + ) = _CopyWithImpl$Query$GetMeals; + + factory CopyWith$Query$GetMeals.stub(TRes res) = + _CopyWithStubImpl$Query$GetMeals; + + TRes call({String? $__typename}); +} + +class _CopyWithImpl$Query$GetMeals + implements CopyWith$Query$GetMeals { + _CopyWithImpl$Query$GetMeals( + this._instance, + this._then, + ); + + final Query$GetMeals _instance; + + final TRes Function(Query$GetMeals) _then; + + static const _undefined = {}; + + TRes call({Object? $__typename = _undefined}) => _then(Query$GetMeals( + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String))); +} + +class _CopyWithStubImpl$Query$GetMeals + implements CopyWith$Query$GetMeals { + _CopyWithStubImpl$Query$GetMeals(this._res); + + TRes _res; + + call({String? $__typename}) => _res; +} + +const documentNodeQueryGetMeals = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'GetMeals'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ) + ]), + ), +]); +Query$GetMeals _parserFn$Query$GetMeals(Map data) => + Query$GetMeals.fromJson(data); +typedef OnQueryComplete$Query$GetMeals = FutureOr Function( + Map?, + Query$GetMeals?, +); + +class Options$Query$GetMeals extends graphql.QueryOptions { + Options$Query$GetMeals({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetMeals? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$GetMeals? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$GetMeals(data), + ), + onError: onError, + document: documentNodeQueryGetMeals, + parserFn: _parserFn$Query$GetMeals, + ); + + final OnQueryComplete$Query$GetMeals? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$GetMeals + extends graphql.WatchQueryOptions { + WatchOptions$Query$GetMeals({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetMeals? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryGetMeals, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$GetMeals, + ); +} + +class FetchMoreOptions$Query$GetMeals extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$GetMeals({required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQueryGetMeals, + ); +} + +extension ClientExtension$Query$GetMeals on graphql.GraphQLClient { + Future> query$GetMeals( + [Options$Query$GetMeals? options]) async => + await this.query(options ?? Options$Query$GetMeals()); + graphql.ObservableQuery watchQuery$GetMeals( + [WatchOptions$Query$GetMeals? options]) => + this.watchQuery(options ?? WatchOptions$Query$GetMeals()); + void writeQuery$GetMeals({ + required Query$GetMeals data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryGetMeals)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$GetMeals? readQuery$GetMeals({bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryGetMeals)), + optimistic: optimistic, + ); + return result == null ? null : Query$GetMeals.fromJson(result); + } +} + +graphql_flutter.QueryHookResult useQuery$GetMeals( + [Options$Query$GetMeals? options]) => + graphql_flutter.useQuery(options ?? Options$Query$GetMeals()); +graphql.ObservableQuery useWatchQuery$GetMeals( + [WatchOptions$Query$GetMeals? options]) => + graphql_flutter.useWatchQuery(options ?? WatchOptions$Query$GetMeals()); + +class Query$GetMeals$Widget extends graphql_flutter.Query { + Query$GetMeals$Widget({ + widgets.Key? key, + Options$Query$GetMeals? options, + required graphql_flutter.QueryBuilder builder, + }) : super( + key: key, + options: options ?? Options$Query$GetMeals(), + builder: builder, + ); +} diff --git a/app/secret.example.json b/app/secret.example.json index 5d61e618..3d3d6709 100644 --- a/app/secret.example.json +++ b/app/secret.example.json @@ -1,4 +1,4 @@ { - "API_URL": "mensa.yourdomain.com", + "API_URL": "https://mensa.yourdomain.com", "API_KEY": "yourapikey" } \ No newline at end of file diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index 262ebfc3..ccdc4d87 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -1,15 +1,24 @@ - import 'package:app/model/api_server/GraphQlServerAccess.dart'; import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:flutter_test/flutter_test.dart'; void main() async { - final GraphQlServerAccess serverAccess = GraphQlServerAccess("1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); + final GraphQlServerAccess serverAccess = + GraphQlServerAccess("1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); + + test('environment endpoint defined', () { + expect(const String.fromEnvironment('API_URL').isNotEmpty, true, + reason: + "define secret file with `--dart-define-from-file=`, see README"); + }); test('graphql', () async { - var deleted = await serverAccess.deleteDownvote(ImageData(id: "1234", url: "url", imageRank: 0, positiveRating: 0, negativeRating: 0)); + var deleted = await serverAccess.deleteDownvote(ImageData( + id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", + url: "url", + imageRank: 0, + positiveRating: 0, + negativeRating: 0)); expect(deleted, true); }); - - -} \ No newline at end of file +} From 1f9c22075877b817c50d1ff36c34c505a97f0223 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Thu, 6 Jul 2023 20:30:30 +0200 Subject: [PATCH 018/184] wrote graphql query for updateAll --- .../model/api_server/GraphQlServerAccess.dart | 11 +- .../model/api_server/requests/querys.graphql | 48 + .../api_server/requests/querys.graphql.dart | 2366 ++++++++++++++++- 3 files changed, 2341 insertions(+), 84 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index fc9ebf0e..1d4f79bd 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -104,15 +104,16 @@ class GraphQlServerAccess implements IServerAccess { } // ---------------------- queries ---------------------- + @override - Future> getMealFromId(String id) async { - // TODO: implement getMealFromId + Future>> updateAll() async { + // TODO: implement updateAll throw UnimplementedError(); } @override - Future>> updateAll() async { - // TODO: implement updateAll + Future> getMealFromId(String id) async { + // TODO: implement getMealFromId throw UnimplementedError(); } @@ -124,6 +125,8 @@ class GraphQlServerAccess implements IServerAccess { } } +/// ---------------------- utility functions ---------------------- + Enum$ReportReason _convertToReportReason(ReportCategory reportReason) { switch (reportReason) { case ReportCategory.offensive: diff --git a/app/lib/model/api_server/requests/querys.graphql b/app/lib/model/api_server/requests/querys.graphql index e69de29b..ca437b9f 100644 --- a/app/lib/model/api_server/requests/querys.graphql +++ b/app/lib/model/api_server/requests/querys.graphql @@ -0,0 +1,48 @@ +query GetMealPlanForDay($date0: NaiveDate!) { + getCanteens { + lines { + id + name + canteen { + id + name + } + meals(date: $date0) { + ...mealInfo + } + } + } +} + +fragment mealInfo on Meal { + id + name + mealType + price { + employee + guest + pupil + student + } + allergens + additives + statistics { + lastServed + nextServed + relativeFrequency + } + ratings { + averageRating + personalRating + ratingsCount + } + images { + id + url + rank + personalDownvote + personalUpvote + downvotes + upvotes + } +} \ No newline at end of file diff --git a/app/lib/model/api_server/requests/querys.graphql.dart b/app/lib/model/api_server/requests/querys.graphql.dart index 7402ba88..82b01e15 100644 --- a/app/lib/model/api_server/requests/querys.graphql.dart +++ b/app/lib/model/api_server/requests/querys.graphql.dart @@ -3,19 +3,1516 @@ import 'package:flutter/widgets.dart' as widgets; import 'package:gql/ast.dart'; import 'package:graphql/client.dart' as graphql; import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; +import 'schema.graphql.dart'; -class Query$GetMeals { - Query$GetMeals({this.$__typename = 'QueryRoot'}); +class Fragment$mealInfo { + Fragment$mealInfo({ + required this.id, + required this.name, + required this.mealType, + required this.price, + required this.allergens, + required this.additives, + required this.statistics, + required this.ratings, + required this.images, + this.$__typename = 'Meal', + }); - factory Query$GetMeals.fromJson(Map json) { + factory Fragment$mealInfo.fromJson(Map json) { + final l$id = json['id']; + final l$name = json['name']; + final l$mealType = json['mealType']; + final l$price = json['price']; + final l$allergens = json['allergens']; + final l$additives = json['additives']; + final l$statistics = json['statistics']; + final l$ratings = json['ratings']; + final l$images = json['images']; final l$$__typename = json['__typename']; - return Query$GetMeals($__typename: (l$$__typename as String)); + return Fragment$mealInfo( + id: (l$id as String), + name: (l$name as String), + mealType: fromJson$Enum$MealType((l$mealType as String)), + price: + Fragment$mealInfo$price.fromJson((l$price as Map)), + allergens: (l$allergens as List) + .map((e) => fromJson$Enum$Allergen((e as String))) + .toList(), + additives: (l$additives as List) + .map((e) => fromJson$Enum$Additive((e as String))) + .toList(), + statistics: Fragment$mealInfo$statistics.fromJson( + (l$statistics as Map)), + ratings: Fragment$mealInfo$ratings.fromJson( + (l$ratings as Map)), + images: (l$images as List) + .map((e) => + Fragment$mealInfo$images.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final String id; + + final String name; + + final Enum$MealType mealType; + + final Fragment$mealInfo$price price; + + final List allergens; + + final List additives; + + final Fragment$mealInfo$statistics statistics; + + final Fragment$mealInfo$ratings ratings; + + final List images; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$id = id; + _resultData['id'] = l$id; + final l$name = name; + _resultData['name'] = l$name; + final l$mealType = mealType; + _resultData['mealType'] = toJson$Enum$MealType(l$mealType); + final l$price = price; + _resultData['price'] = l$price.toJson(); + final l$allergens = allergens; + _resultData['allergens'] = + l$allergens.map((e) => toJson$Enum$Allergen(e)).toList(); + final l$additives = additives; + _resultData['additives'] = + l$additives.map((e) => toJson$Enum$Additive(e)).toList(); + final l$statistics = statistics; + _resultData['statistics'] = l$statistics.toJson(); + final l$ratings = ratings; + _resultData['ratings'] = l$ratings.toJson(); + final l$images = images; + _resultData['images'] = l$images.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$id = id; + final l$name = name; + final l$mealType = mealType; + final l$price = price; + final l$allergens = allergens; + final l$additives = additives; + final l$statistics = statistics; + final l$ratings = ratings; + final l$images = images; + final l$$__typename = $__typename; + return Object.hashAll([ + l$id, + l$name, + l$mealType, + l$price, + Object.hashAll(l$allergens.map((v) => v)), + Object.hashAll(l$additives.map((v) => v)), + l$statistics, + l$ratings, + Object.hashAll(l$images.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$mealInfo) || runtimeType != other.runtimeType) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$mealType = mealType; + final lOther$mealType = other.mealType; + if (l$mealType != lOther$mealType) { + return false; + } + final l$price = price; + final lOther$price = other.price; + if (l$price != lOther$price) { + return false; + } + final l$allergens = allergens; + final lOther$allergens = other.allergens; + if (l$allergens.length != lOther$allergens.length) { + return false; + } + for (int i = 0; i < l$allergens.length; i++) { + final l$allergens$entry = l$allergens[i]; + final lOther$allergens$entry = lOther$allergens[i]; + if (l$allergens$entry != lOther$allergens$entry) { + return false; + } + } + final l$additives = additives; + final lOther$additives = other.additives; + if (l$additives.length != lOther$additives.length) { + return false; + } + for (int i = 0; i < l$additives.length; i++) { + final l$additives$entry = l$additives[i]; + final lOther$additives$entry = lOther$additives[i]; + if (l$additives$entry != lOther$additives$entry) { + return false; + } + } + final l$statistics = statistics; + final lOther$statistics = other.statistics; + if (l$statistics != lOther$statistics) { + return false; + } + final l$ratings = ratings; + final lOther$ratings = other.ratings; + if (l$ratings != lOther$ratings) { + return false; + } + final l$images = images; + final lOther$images = other.images; + if (l$images.length != lOther$images.length) { + return false; + } + for (int i = 0; i < l$images.length; i++) { + final l$images$entry = l$images[i]; + final lOther$images$entry = lOther$images[i]; + if (l$images$entry != lOther$images$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$mealInfo on Fragment$mealInfo { + CopyWith$Fragment$mealInfo get copyWith => + CopyWith$Fragment$mealInfo( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$mealInfo { + factory CopyWith$Fragment$mealInfo( + Fragment$mealInfo instance, + TRes Function(Fragment$mealInfo) then, + ) = _CopyWithImpl$Fragment$mealInfo; + + factory CopyWith$Fragment$mealInfo.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo; + + TRes call({ + String? id, + String? name, + Enum$MealType? mealType, + Fragment$mealInfo$price? price, + List? allergens, + List? additives, + Fragment$mealInfo$statistics? statistics, + Fragment$mealInfo$ratings? ratings, + List? images, + String? $__typename, + }); + CopyWith$Fragment$mealInfo$price get price; + CopyWith$Fragment$mealInfo$statistics get statistics; + CopyWith$Fragment$mealInfo$ratings get ratings; + TRes images( + Iterable Function( + Iterable< + CopyWith$Fragment$mealInfo$images>) + _fn); +} + +class _CopyWithImpl$Fragment$mealInfo + implements CopyWith$Fragment$mealInfo { + _CopyWithImpl$Fragment$mealInfo( + this._instance, + this._then, + ); + + final Fragment$mealInfo _instance; + + final TRes Function(Fragment$mealInfo) _then; + + static const _undefined = {}; + + TRes call({ + Object? id = _undefined, + Object? name = _undefined, + Object? mealType = _undefined, + Object? price = _undefined, + Object? allergens = _undefined, + Object? additives = _undefined, + Object? statistics = _undefined, + Object? ratings = _undefined, + Object? images = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$mealInfo( + id: id == _undefined || id == null ? _instance.id : (id as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + mealType: mealType == _undefined || mealType == null + ? _instance.mealType + : (mealType as Enum$MealType), + price: price == _undefined || price == null + ? _instance.price + : (price as Fragment$mealInfo$price), + allergens: allergens == _undefined || allergens == null + ? _instance.allergens + : (allergens as List), + additives: additives == _undefined || additives == null + ? _instance.additives + : (additives as List), + statistics: statistics == _undefined || statistics == null + ? _instance.statistics + : (statistics as Fragment$mealInfo$statistics), + ratings: ratings == _undefined || ratings == null + ? _instance.ratings + : (ratings as Fragment$mealInfo$ratings), + images: images == _undefined || images == null + ? _instance.images + : (images as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Fragment$mealInfo$price get price { + final local$price = _instance.price; + return CopyWith$Fragment$mealInfo$price(local$price, (e) => call(price: e)); + } + + CopyWith$Fragment$mealInfo$statistics get statistics { + final local$statistics = _instance.statistics; + return CopyWith$Fragment$mealInfo$statistics( + local$statistics, (e) => call(statistics: e)); + } + + CopyWith$Fragment$mealInfo$ratings get ratings { + final local$ratings = _instance.ratings; + return CopyWith$Fragment$mealInfo$ratings( + local$ratings, (e) => call(ratings: e)); + } + + TRes images( + Iterable Function( + Iterable< + CopyWith$Fragment$mealInfo$images< + Fragment$mealInfo$images>>) + _fn) => + call( + images: + _fn(_instance.images.map((e) => CopyWith$Fragment$mealInfo$images( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Fragment$mealInfo + implements CopyWith$Fragment$mealInfo { + _CopyWithStubImpl$Fragment$mealInfo(this._res); + + TRes _res; + + call({ + String? id, + String? name, + Enum$MealType? mealType, + Fragment$mealInfo$price? price, + List? allergens, + List? additives, + Fragment$mealInfo$statistics? statistics, + Fragment$mealInfo$ratings? ratings, + List? images, + String? $__typename, + }) => + _res; + CopyWith$Fragment$mealInfo$price get price => + CopyWith$Fragment$mealInfo$price.stub(_res); + CopyWith$Fragment$mealInfo$statistics get statistics => + CopyWith$Fragment$mealInfo$statistics.stub(_res); + CopyWith$Fragment$mealInfo$ratings get ratings => + CopyWith$Fragment$mealInfo$ratings.stub(_res); + images(_fn) => _res; +} + +const fragmentDefinitionmealInfo = FragmentDefinitionNode( + name: NameNode(value: 'mealInfo'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'Meal'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'mealType'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'price'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'employee'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'guest'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'pupil'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'student'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'allergens'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'additives'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'statistics'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'lastServed'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'nextServed'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'relativeFrequency'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'ratings'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'averageRating'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'personalRating'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'ratingsCount'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'images'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'url'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'rank'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'personalDownvote'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'personalUpvote'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'downvotes'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'upvotes'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentmealInfo = DocumentNode(definitions: [ + fragmentDefinitionmealInfo, +]); + +extension ClientExtension$Fragment$mealInfo on graphql.GraphQLClient { + void writeFragment$mealInfo({ + required Fragment$mealInfo data, + required Map idFields, + bool broadcast = true, + }) => + this.writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'mealInfo', + document: documentNodeFragmentmealInfo, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$mealInfo? readFragment$mealInfo({ + required Map idFields, + bool optimistic = true, + }) { + final result = this.readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'mealInfo', + document: documentNodeFragmentmealInfo, + ), + ), + optimistic: optimistic, + ); + return result == null ? null : Fragment$mealInfo.fromJson(result); + } +} + +class Fragment$mealInfo$price { + Fragment$mealInfo$price({ + required this.employee, + required this.guest, + required this.pupil, + required this.student, + this.$__typename = 'Price', + }); + + factory Fragment$mealInfo$price.fromJson(Map json) { + final l$employee = json['employee']; + final l$guest = json['guest']; + final l$pupil = json['pupil']; + final l$student = json['student']; + final l$$__typename = json['__typename']; + return Fragment$mealInfo$price( + employee: (l$employee as int), + guest: (l$guest as int), + pupil: (l$pupil as int), + student: (l$student as int), + $__typename: (l$$__typename as String), + ); + } + + final int employee; + + final int guest; + + final int pupil; + + final int student; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$employee = employee; + _resultData['employee'] = l$employee; + final l$guest = guest; + _resultData['guest'] = l$guest; + final l$pupil = pupil; + _resultData['pupil'] = l$pupil; + final l$student = student; + _resultData['student'] = l$student; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$employee = employee; + final l$guest = guest; + final l$pupil = pupil; + final l$student = student; + final l$$__typename = $__typename; + return Object.hashAll([ + l$employee, + l$guest, + l$pupil, + l$student, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$mealInfo$price) || + runtimeType != other.runtimeType) { + return false; + } + final l$employee = employee; + final lOther$employee = other.employee; + if (l$employee != lOther$employee) { + return false; + } + final l$guest = guest; + final lOther$guest = other.guest; + if (l$guest != lOther$guest) { + return false; + } + final l$pupil = pupil; + final lOther$pupil = other.pupil; + if (l$pupil != lOther$pupil) { + return false; + } + final l$student = student; + final lOther$student = other.student; + if (l$student != lOther$student) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$mealInfo$price on Fragment$mealInfo$price { + CopyWith$Fragment$mealInfo$price get copyWith => + CopyWith$Fragment$mealInfo$price( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$mealInfo$price { + factory CopyWith$Fragment$mealInfo$price( + Fragment$mealInfo$price instance, + TRes Function(Fragment$mealInfo$price) then, + ) = _CopyWithImpl$Fragment$mealInfo$price; + + factory CopyWith$Fragment$mealInfo$price.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo$price; + + TRes call({ + int? employee, + int? guest, + int? pupil, + int? student, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$mealInfo$price + implements CopyWith$Fragment$mealInfo$price { + _CopyWithImpl$Fragment$mealInfo$price( + this._instance, + this._then, + ); + + final Fragment$mealInfo$price _instance; + + final TRes Function(Fragment$mealInfo$price) _then; + + static const _undefined = {}; + + TRes call({ + Object? employee = _undefined, + Object? guest = _undefined, + Object? pupil = _undefined, + Object? student = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$mealInfo$price( + employee: employee == _undefined || employee == null + ? _instance.employee + : (employee as int), + guest: guest == _undefined || guest == null + ? _instance.guest + : (guest as int), + pupil: pupil == _undefined || pupil == null + ? _instance.pupil + : (pupil as int), + student: student == _undefined || student == null + ? _instance.student + : (student as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$mealInfo$price + implements CopyWith$Fragment$mealInfo$price { + _CopyWithStubImpl$Fragment$mealInfo$price(this._res); + + TRes _res; + + call({ + int? employee, + int? guest, + int? pupil, + int? student, + String? $__typename, + }) => + _res; +} + +class Fragment$mealInfo$statistics { + Fragment$mealInfo$statistics({ + this.lastServed, + this.nextServed, + required this.relativeFrequency, + this.$__typename = 'MealStatistics', + }); + + factory Fragment$mealInfo$statistics.fromJson(Map json) { + final l$lastServed = json['lastServed']; + final l$nextServed = json['nextServed']; + final l$relativeFrequency = json['relativeFrequency']; + final l$$__typename = json['__typename']; + return Fragment$mealInfo$statistics( + lastServed: (l$lastServed as String?), + nextServed: (l$nextServed as String?), + relativeFrequency: (l$relativeFrequency as num).toDouble(), + $__typename: (l$$__typename as String), + ); + } + + final String? lastServed; + + final String? nextServed; + + final double relativeFrequency; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$lastServed = lastServed; + _resultData['lastServed'] = l$lastServed; + final l$nextServed = nextServed; + _resultData['nextServed'] = l$nextServed; + final l$relativeFrequency = relativeFrequency; + _resultData['relativeFrequency'] = l$relativeFrequency; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$lastServed = lastServed; + final l$nextServed = nextServed; + final l$relativeFrequency = relativeFrequency; + final l$$__typename = $__typename; + return Object.hashAll([ + l$lastServed, + l$nextServed, + l$relativeFrequency, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$mealInfo$statistics) || + runtimeType != other.runtimeType) { + return false; + } + final l$lastServed = lastServed; + final lOther$lastServed = other.lastServed; + if (l$lastServed != lOther$lastServed) { + return false; + } + final l$nextServed = nextServed; + final lOther$nextServed = other.nextServed; + if (l$nextServed != lOther$nextServed) { + return false; + } + final l$relativeFrequency = relativeFrequency; + final lOther$relativeFrequency = other.relativeFrequency; + if (l$relativeFrequency != lOther$relativeFrequency) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$mealInfo$statistics + on Fragment$mealInfo$statistics { + CopyWith$Fragment$mealInfo$statistics + get copyWith => CopyWith$Fragment$mealInfo$statistics( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$mealInfo$statistics { + factory CopyWith$Fragment$mealInfo$statistics( + Fragment$mealInfo$statistics instance, + TRes Function(Fragment$mealInfo$statistics) then, + ) = _CopyWithImpl$Fragment$mealInfo$statistics; + + factory CopyWith$Fragment$mealInfo$statistics.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo$statistics; + + TRes call({ + String? lastServed, + String? nextServed, + double? relativeFrequency, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$mealInfo$statistics + implements CopyWith$Fragment$mealInfo$statistics { + _CopyWithImpl$Fragment$mealInfo$statistics( + this._instance, + this._then, + ); + + final Fragment$mealInfo$statistics _instance; + + final TRes Function(Fragment$mealInfo$statistics) _then; + + static const _undefined = {}; + + TRes call({ + Object? lastServed = _undefined, + Object? nextServed = _undefined, + Object? relativeFrequency = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$mealInfo$statistics( + lastServed: lastServed == _undefined + ? _instance.lastServed + : (lastServed as String?), + nextServed: nextServed == _undefined + ? _instance.nextServed + : (nextServed as String?), + relativeFrequency: + relativeFrequency == _undefined || relativeFrequency == null + ? _instance.relativeFrequency + : (relativeFrequency as double), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$mealInfo$statistics + implements CopyWith$Fragment$mealInfo$statistics { + _CopyWithStubImpl$Fragment$mealInfo$statistics(this._res); + + TRes _res; + + call({ + String? lastServed, + String? nextServed, + double? relativeFrequency, + String? $__typename, + }) => + _res; +} + +class Fragment$mealInfo$ratings { + Fragment$mealInfo$ratings({ + required this.averageRating, + this.personalRating, + required this.ratingsCount, + this.$__typename = 'Ratings', + }); + + factory Fragment$mealInfo$ratings.fromJson(Map json) { + final l$averageRating = json['averageRating']; + final l$personalRating = json['personalRating']; + final l$ratingsCount = json['ratingsCount']; + final l$$__typename = json['__typename']; + return Fragment$mealInfo$ratings( + averageRating: (l$averageRating as num).toDouble(), + personalRating: (l$personalRating as int?), + ratingsCount: (l$ratingsCount as int), + $__typename: (l$$__typename as String), + ); + } + + final double averageRating; + + final int? personalRating; + + final int ratingsCount; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$averageRating = averageRating; + _resultData['averageRating'] = l$averageRating; + final l$personalRating = personalRating; + _resultData['personalRating'] = l$personalRating; + final l$ratingsCount = ratingsCount; + _resultData['ratingsCount'] = l$ratingsCount; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$averageRating = averageRating; + final l$personalRating = personalRating; + final l$ratingsCount = ratingsCount; + final l$$__typename = $__typename; + return Object.hashAll([ + l$averageRating, + l$personalRating, + l$ratingsCount, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$mealInfo$ratings) || + runtimeType != other.runtimeType) { + return false; + } + final l$averageRating = averageRating; + final lOther$averageRating = other.averageRating; + if (l$averageRating != lOther$averageRating) { + return false; + } + final l$personalRating = personalRating; + final lOther$personalRating = other.personalRating; + if (l$personalRating != lOther$personalRating) { + return false; + } + final l$ratingsCount = ratingsCount; + final lOther$ratingsCount = other.ratingsCount; + if (l$ratingsCount != lOther$ratingsCount) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$mealInfo$ratings + on Fragment$mealInfo$ratings { + CopyWith$Fragment$mealInfo$ratings get copyWith => + CopyWith$Fragment$mealInfo$ratings( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$mealInfo$ratings { + factory CopyWith$Fragment$mealInfo$ratings( + Fragment$mealInfo$ratings instance, + TRes Function(Fragment$mealInfo$ratings) then, + ) = _CopyWithImpl$Fragment$mealInfo$ratings; + + factory CopyWith$Fragment$mealInfo$ratings.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo$ratings; + + TRes call({ + double? averageRating, + int? personalRating, + int? ratingsCount, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$mealInfo$ratings + implements CopyWith$Fragment$mealInfo$ratings { + _CopyWithImpl$Fragment$mealInfo$ratings( + this._instance, + this._then, + ); + + final Fragment$mealInfo$ratings _instance; + + final TRes Function(Fragment$mealInfo$ratings) _then; + + static const _undefined = {}; + + TRes call({ + Object? averageRating = _undefined, + Object? personalRating = _undefined, + Object? ratingsCount = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$mealInfo$ratings( + averageRating: averageRating == _undefined || averageRating == null + ? _instance.averageRating + : (averageRating as double), + personalRating: personalRating == _undefined + ? _instance.personalRating + : (personalRating as int?), + ratingsCount: ratingsCount == _undefined || ratingsCount == null + ? _instance.ratingsCount + : (ratingsCount as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$mealInfo$ratings + implements CopyWith$Fragment$mealInfo$ratings { + _CopyWithStubImpl$Fragment$mealInfo$ratings(this._res); + + TRes _res; + + call({ + double? averageRating, + int? personalRating, + int? ratingsCount, + String? $__typename, + }) => + _res; +} + +class Fragment$mealInfo$images { + Fragment$mealInfo$images({ + required this.id, + required this.url, + required this.rank, + required this.personalDownvote, + required this.personalUpvote, + required this.downvotes, + required this.upvotes, + this.$__typename = 'Image', + }); + + factory Fragment$mealInfo$images.fromJson(Map json) { + final l$id = json['id']; + final l$url = json['url']; + final l$rank = json['rank']; + final l$personalDownvote = json['personalDownvote']; + final l$personalUpvote = json['personalUpvote']; + final l$downvotes = json['downvotes']; + final l$upvotes = json['upvotes']; + final l$$__typename = json['__typename']; + return Fragment$mealInfo$images( + id: (l$id as String), + url: (l$url as String), + rank: (l$rank as num).toDouble(), + personalDownvote: (l$personalDownvote as bool), + personalUpvote: (l$personalUpvote as bool), + downvotes: (l$downvotes as int), + upvotes: (l$upvotes as int), + $__typename: (l$$__typename as String), + ); + } + + final String id; + + final String url; + + final double rank; + + final bool personalDownvote; + + final bool personalUpvote; + + final int downvotes; + + final int upvotes; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$id = id; + _resultData['id'] = l$id; + final l$url = url; + _resultData['url'] = l$url; + final l$rank = rank; + _resultData['rank'] = l$rank; + final l$personalDownvote = personalDownvote; + _resultData['personalDownvote'] = l$personalDownvote; + final l$personalUpvote = personalUpvote; + _resultData['personalUpvote'] = l$personalUpvote; + final l$downvotes = downvotes; + _resultData['downvotes'] = l$downvotes; + final l$upvotes = upvotes; + _resultData['upvotes'] = l$upvotes; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$id = id; + final l$url = url; + final l$rank = rank; + final l$personalDownvote = personalDownvote; + final l$personalUpvote = personalUpvote; + final l$downvotes = downvotes; + final l$upvotes = upvotes; + final l$$__typename = $__typename; + return Object.hashAll([ + l$id, + l$url, + l$rank, + l$personalDownvote, + l$personalUpvote, + l$downvotes, + l$upvotes, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$mealInfo$images) || + runtimeType != other.runtimeType) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$url = url; + final lOther$url = other.url; + if (l$url != lOther$url) { + return false; + } + final l$rank = rank; + final lOther$rank = other.rank; + if (l$rank != lOther$rank) { + return false; + } + final l$personalDownvote = personalDownvote; + final lOther$personalDownvote = other.personalDownvote; + if (l$personalDownvote != lOther$personalDownvote) { + return false; + } + final l$personalUpvote = personalUpvote; + final lOther$personalUpvote = other.personalUpvote; + if (l$personalUpvote != lOther$personalUpvote) { + return false; + } + final l$downvotes = downvotes; + final lOther$downvotes = other.downvotes; + if (l$downvotes != lOther$downvotes) { + return false; + } + final l$upvotes = upvotes; + final lOther$upvotes = other.upvotes; + if (l$upvotes != lOther$upvotes) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$mealInfo$images + on Fragment$mealInfo$images { + CopyWith$Fragment$mealInfo$images get copyWith => + CopyWith$Fragment$mealInfo$images( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$mealInfo$images { + factory CopyWith$Fragment$mealInfo$images( + Fragment$mealInfo$images instance, + TRes Function(Fragment$mealInfo$images) then, + ) = _CopyWithImpl$Fragment$mealInfo$images; + + factory CopyWith$Fragment$mealInfo$images.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo$images; + + TRes call({ + String? id, + String? url, + double? rank, + bool? personalDownvote, + bool? personalUpvote, + int? downvotes, + int? upvotes, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$mealInfo$images + implements CopyWith$Fragment$mealInfo$images { + _CopyWithImpl$Fragment$mealInfo$images( + this._instance, + this._then, + ); + + final Fragment$mealInfo$images _instance; + + final TRes Function(Fragment$mealInfo$images) _then; + + static const _undefined = {}; + + TRes call({ + Object? id = _undefined, + Object? url = _undefined, + Object? rank = _undefined, + Object? personalDownvote = _undefined, + Object? personalUpvote = _undefined, + Object? downvotes = _undefined, + Object? upvotes = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$mealInfo$images( + id: id == _undefined || id == null ? _instance.id : (id as String), + url: url == _undefined || url == null ? _instance.url : (url as String), + rank: rank == _undefined || rank == null + ? _instance.rank + : (rank as double), + personalDownvote: + personalDownvote == _undefined || personalDownvote == null + ? _instance.personalDownvote + : (personalDownvote as bool), + personalUpvote: personalUpvote == _undefined || personalUpvote == null + ? _instance.personalUpvote + : (personalUpvote as bool), + downvotes: downvotes == _undefined || downvotes == null + ? _instance.downvotes + : (downvotes as int), + upvotes: upvotes == _undefined || upvotes == null + ? _instance.upvotes + : (upvotes as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$mealInfo$images + implements CopyWith$Fragment$mealInfo$images { + _CopyWithStubImpl$Fragment$mealInfo$images(this._res); + + TRes _res; + + call({ + String? id, + String? url, + double? rank, + bool? personalDownvote, + bool? personalUpvote, + int? downvotes, + int? upvotes, + String? $__typename, + }) => + _res; +} + +class Variables$Query$GetMealPlanForDay { + factory Variables$Query$GetMealPlanForDay({required String date0}) => + Variables$Query$GetMealPlanForDay._({ + r'date0': date0, + }); + + Variables$Query$GetMealPlanForDay._(this._$data); + + factory Variables$Query$GetMealPlanForDay.fromJson( + Map data) { + final result$data = {}; + final l$date0 = data['date0']; + result$data['date0'] = (l$date0 as String); + return Variables$Query$GetMealPlanForDay._(result$data); + } + + Map _$data; + + String get date0 => (_$data['date0'] as String); + Map toJson() { + final result$data = {}; + final l$date0 = date0; + result$data['date0'] = l$date0; + return result$data; + } + + CopyWith$Variables$Query$GetMealPlanForDay + get copyWith => CopyWith$Variables$Query$GetMealPlanForDay( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Query$GetMealPlanForDay) || + runtimeType != other.runtimeType) { + return false; + } + final l$date0 = date0; + final lOther$date0 = other.date0; + if (l$date0 != lOther$date0) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$date0 = date0; + return Object.hashAll([l$date0]); + } +} + +abstract class CopyWith$Variables$Query$GetMealPlanForDay { + factory CopyWith$Variables$Query$GetMealPlanForDay( + Variables$Query$GetMealPlanForDay instance, + TRes Function(Variables$Query$GetMealPlanForDay) then, + ) = _CopyWithImpl$Variables$Query$GetMealPlanForDay; + + factory CopyWith$Variables$Query$GetMealPlanForDay.stub(TRes res) = + _CopyWithStubImpl$Variables$Query$GetMealPlanForDay; + + TRes call({String? date0}); +} + +class _CopyWithImpl$Variables$Query$GetMealPlanForDay + implements CopyWith$Variables$Query$GetMealPlanForDay { + _CopyWithImpl$Variables$Query$GetMealPlanForDay( + this._instance, + this._then, + ); + + final Variables$Query$GetMealPlanForDay _instance; + + final TRes Function(Variables$Query$GetMealPlanForDay) _then; + + static const _undefined = {}; + + TRes call({Object? date0 = _undefined}) => + _then(Variables$Query$GetMealPlanForDay._({ + ..._instance._$data, + if (date0 != _undefined && date0 != null) 'date0': (date0 as String), + })); +} + +class _CopyWithStubImpl$Variables$Query$GetMealPlanForDay + implements CopyWith$Variables$Query$GetMealPlanForDay { + _CopyWithStubImpl$Variables$Query$GetMealPlanForDay(this._res); + + TRes _res; + + call({String? date0}) => _res; +} + +class Query$GetMealPlanForDay { + Query$GetMealPlanForDay({ + required this.getCanteens, + this.$__typename = 'QueryRoot', + }); + + factory Query$GetMealPlanForDay.fromJson(Map json) { + final l$getCanteens = json['getCanteens']; + final l$$__typename = json['__typename']; + return Query$GetMealPlanForDay( + getCanteens: (l$getCanteens as List) + .map((e) => Query$GetMealPlanForDay$getCanteens.fromJson( + (e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); } + final List getCanteens; + final String $__typename; Map toJson() { final _resultData = {}; + final l$getCanteens = getCanteens; + _resultData['getCanteens'] = l$getCanteens.map((e) => e.toJson()).toList(); final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -23,8 +1520,12 @@ class Query$GetMeals { @override int get hashCode { + final l$getCanteens = getCanteens; final l$$__typename = $__typename; - return Object.hashAll([l$$__typename]); + return Object.hashAll([ + Object.hashAll(l$getCanteens.map((v) => v)), + l$$__typename, + ]); } @override @@ -32,9 +1533,22 @@ class Query$GetMeals { if (identical(this, other)) { return true; } - if (!(other is Query$GetMeals) || runtimeType != other.runtimeType) { + if (!(other is Query$GetMealPlanForDay) || + runtimeType != other.runtimeType) { + return false; + } + final l$getCanteens = getCanteens; + final lOther$getCanteens = other.getCanteens; + if (l$getCanteens.length != lOther$getCanteens.length) { return false; } + for (int i = 0; i < l$getCanteens.length; i++) { + final l$getCanteens$entry = l$getCanteens[i]; + final lOther$getCanteens$entry = lOther$getCanteens[i]; + if (l$getCanteens$entry != lOther$getCanteens$entry) { + return false; + } + } final l$$__typename = $__typename; final lOther$$__typename = other.$__typename; if (l$$__typename != lOther$$__typename) { @@ -44,92 +1558,238 @@ class Query$GetMeals { } } -extension UtilityExtension$Query$GetMeals on Query$GetMeals { - CopyWith$Query$GetMeals get copyWith => - CopyWith$Query$GetMeals( +extension UtilityExtension$Query$GetMealPlanForDay on Query$GetMealPlanForDay { + CopyWith$Query$GetMealPlanForDay get copyWith => + CopyWith$Query$GetMealPlanForDay( this, (i) => i, ); } -abstract class CopyWith$Query$GetMeals { - factory CopyWith$Query$GetMeals( - Query$GetMeals instance, - TRes Function(Query$GetMeals) then, - ) = _CopyWithImpl$Query$GetMeals; +abstract class CopyWith$Query$GetMealPlanForDay { + factory CopyWith$Query$GetMealPlanForDay( + Query$GetMealPlanForDay instance, + TRes Function(Query$GetMealPlanForDay) then, + ) = _CopyWithImpl$Query$GetMealPlanForDay; - factory CopyWith$Query$GetMeals.stub(TRes res) = - _CopyWithStubImpl$Query$GetMeals; + factory CopyWith$Query$GetMealPlanForDay.stub(TRes res) = + _CopyWithStubImpl$Query$GetMealPlanForDay; - TRes call({String? $__typename}); + TRes call({ + List? getCanteens, + String? $__typename, + }); + TRes getCanteens( + Iterable Function( + Iterable< + CopyWith$Query$GetMealPlanForDay$getCanteens< + Query$GetMealPlanForDay$getCanteens>>) + _fn); } -class _CopyWithImpl$Query$GetMeals - implements CopyWith$Query$GetMeals { - _CopyWithImpl$Query$GetMeals( +class _CopyWithImpl$Query$GetMealPlanForDay + implements CopyWith$Query$GetMealPlanForDay { + _CopyWithImpl$Query$GetMealPlanForDay( this._instance, this._then, ); - final Query$GetMeals _instance; + final Query$GetMealPlanForDay _instance; - final TRes Function(Query$GetMeals) _then; + final TRes Function(Query$GetMealPlanForDay) _then; static const _undefined = {}; - TRes call({Object? $__typename = _undefined}) => _then(Query$GetMeals( - $__typename: $__typename == _undefined || $__typename == null - ? _instance.$__typename - : ($__typename as String))); + TRes call({ + Object? getCanteens = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetMealPlanForDay( + getCanteens: getCanteens == _undefined || getCanteens == null + ? _instance.getCanteens + : (getCanteens as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes getCanteens( + Iterable Function( + Iterable< + CopyWith$Query$GetMealPlanForDay$getCanteens< + Query$GetMealPlanForDay$getCanteens>>) + _fn) => + call( + getCanteens: _fn(_instance.getCanteens + .map((e) => CopyWith$Query$GetMealPlanForDay$getCanteens( + e, + (i) => i, + ))).toList()); } -class _CopyWithStubImpl$Query$GetMeals - implements CopyWith$Query$GetMeals { - _CopyWithStubImpl$Query$GetMeals(this._res); +class _CopyWithStubImpl$Query$GetMealPlanForDay + implements CopyWith$Query$GetMealPlanForDay { + _CopyWithStubImpl$Query$GetMealPlanForDay(this._res); TRes _res; - call({String? $__typename}) => _res; + call({ + List? getCanteens, + String? $__typename, + }) => + _res; + getCanteens(_fn) => _res; } -const documentNodeQueryGetMeals = DocumentNode(definitions: [ +const documentNodeQueryGetMealPlanForDay = DocumentNode(definitions: [ OperationDefinitionNode( type: OperationType.query, - name: NameNode(value: 'GetMeals'), - variableDefinitions: [], + name: NameNode(value: 'GetMealPlanForDay'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'date0')), + type: NamedTypeNode( + name: NameNode(value: 'NaiveDate'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], directives: [], selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'getCanteens'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'lines'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'canteen'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'meals'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'date'), + value: VariableNode(name: NameNode(value: 'date0')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'mealInfo'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), FieldNode( name: NameNode(value: '__typename'), alias: null, arguments: [], directives: [], selectionSet: null, - ) + ), ]), ), + fragmentDefinitionmealInfo, ]); -Query$GetMeals _parserFn$Query$GetMeals(Map data) => - Query$GetMeals.fromJson(data); -typedef OnQueryComplete$Query$GetMeals = FutureOr Function( +Query$GetMealPlanForDay _parserFn$Query$GetMealPlanForDay( + Map data) => + Query$GetMealPlanForDay.fromJson(data); +typedef OnQueryComplete$Query$GetMealPlanForDay = FutureOr Function( Map?, - Query$GetMeals?, + Query$GetMealPlanForDay?, ); -class Options$Query$GetMeals extends graphql.QueryOptions { - Options$Query$GetMeals({ +class Options$Query$GetMealPlanForDay + extends graphql.QueryOptions { + Options$Query$GetMealPlanForDay({ String? operationName, + required Variables$Query$GetMealPlanForDay variables, graphql.FetchPolicy? fetchPolicy, graphql.ErrorPolicy? errorPolicy, graphql.CacheRereadPolicy? cacheRereadPolicy, Object? optimisticResult, - Query$GetMeals? typedOptimisticResult, + Query$GetMealPlanForDay? typedOptimisticResult, Duration? pollInterval, graphql.Context? context, - OnQueryComplete$Query$GetMeals? onComplete, + OnQueryComplete$Query$GetMealPlanForDay? onComplete, graphql.OnQueryError? onError, }) : onCompleteWithParsed = onComplete, super( + variables: variables.toJson(), operationName: operationName, fetchPolicy: fetchPolicy, errorPolicy: errorPolicy, @@ -141,14 +1801,16 @@ class Options$Query$GetMeals extends graphql.QueryOptions { ? null : (data) => onComplete( data, - data == null ? null : _parserFn$Query$GetMeals(data), + data == null + ? null + : _parserFn$Query$GetMealPlanForDay(data), ), onError: onError, - document: documentNodeQueryGetMeals, - parserFn: _parserFn$Query$GetMeals, + document: documentNodeQueryGetMealPlanForDay, + parserFn: _parserFn$Query$GetMealPlanForDay, ); - final OnQueryComplete$Query$GetMeals? onCompleteWithParsed; + final OnQueryComplete$Query$GetMealPlanForDay? onCompleteWithParsed; @override List get properties => [ @@ -159,86 +1821,630 @@ class Options$Query$GetMeals extends graphql.QueryOptions { ]; } -class WatchOptions$Query$GetMeals - extends graphql.WatchQueryOptions { - WatchOptions$Query$GetMeals({ +class WatchOptions$Query$GetMealPlanForDay + extends graphql.WatchQueryOptions { + WatchOptions$Query$GetMealPlanForDay({ String? operationName, + required Variables$Query$GetMealPlanForDay variables, graphql.FetchPolicy? fetchPolicy, graphql.ErrorPolicy? errorPolicy, graphql.CacheRereadPolicy? cacheRereadPolicy, Object? optimisticResult, - Query$GetMeals? typedOptimisticResult, + Query$GetMealPlanForDay? typedOptimisticResult, graphql.Context? context, Duration? pollInterval, bool? eagerlyFetchResults, bool carryForwardDataOnException = true, bool fetchResults = false, }) : super( + variables: variables.toJson(), operationName: operationName, fetchPolicy: fetchPolicy, errorPolicy: errorPolicy, cacheRereadPolicy: cacheRereadPolicy, optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), context: context, - document: documentNodeQueryGetMeals, + document: documentNodeQueryGetMealPlanForDay, pollInterval: pollInterval, eagerlyFetchResults: eagerlyFetchResults, carryForwardDataOnException: carryForwardDataOnException, fetchResults: fetchResults, - parserFn: _parserFn$Query$GetMeals, + parserFn: _parserFn$Query$GetMealPlanForDay, ); } -class FetchMoreOptions$Query$GetMeals extends graphql.FetchMoreOptions { - FetchMoreOptions$Query$GetMeals({required graphql.UpdateQuery updateQuery}) - : super( +class FetchMoreOptions$Query$GetMealPlanForDay + extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$GetMealPlanForDay({ + required graphql.UpdateQuery updateQuery, + required Variables$Query$GetMealPlanForDay variables, + }) : super( updateQuery: updateQuery, - document: documentNodeQueryGetMeals, + variables: variables.toJson(), + document: documentNodeQueryGetMealPlanForDay, ); } -extension ClientExtension$Query$GetMeals on graphql.GraphQLClient { - Future> query$GetMeals( - [Options$Query$GetMeals? options]) async => - await this.query(options ?? Options$Query$GetMeals()); - graphql.ObservableQuery watchQuery$GetMeals( - [WatchOptions$Query$GetMeals? options]) => - this.watchQuery(options ?? WatchOptions$Query$GetMeals()); - void writeQuery$GetMeals({ - required Query$GetMeals data, +extension ClientExtension$Query$GetMealPlanForDay on graphql.GraphQLClient { + Future> query$GetMealPlanForDay( + Options$Query$GetMealPlanForDay options) async => + await this.query(options); + graphql.ObservableQuery watchQuery$GetMealPlanForDay( + WatchOptions$Query$GetMealPlanForDay options) => + this.watchQuery(options); + void writeQuery$GetMealPlanForDay({ + required Query$GetMealPlanForDay data, + required Variables$Query$GetMealPlanForDay variables, bool broadcast = true, }) => this.writeQuery( graphql.Request( - operation: graphql.Operation(document: documentNodeQueryGetMeals)), + operation: + graphql.Operation(document: documentNodeQueryGetMealPlanForDay), + variables: variables.toJson(), + ), data: data.toJson(), broadcast: broadcast, ); - Query$GetMeals? readQuery$GetMeals({bool optimistic = true}) { + Query$GetMealPlanForDay? readQuery$GetMealPlanForDay({ + required Variables$Query$GetMealPlanForDay variables, + bool optimistic = true, + }) { final result = this.readQuery( graphql.Request( - operation: graphql.Operation(document: documentNodeQueryGetMeals)), + operation: + graphql.Operation(document: documentNodeQueryGetMealPlanForDay), + variables: variables.toJson(), + ), optimistic: optimistic, ); - return result == null ? null : Query$GetMeals.fromJson(result); + return result == null ? null : Query$GetMealPlanForDay.fromJson(result); } } -graphql_flutter.QueryHookResult useQuery$GetMeals( - [Options$Query$GetMeals? options]) => - graphql_flutter.useQuery(options ?? Options$Query$GetMeals()); -graphql.ObservableQuery useWatchQuery$GetMeals( - [WatchOptions$Query$GetMeals? options]) => - graphql_flutter.useWatchQuery(options ?? WatchOptions$Query$GetMeals()); +graphql_flutter.QueryHookResult + useQuery$GetMealPlanForDay(Options$Query$GetMealPlanForDay options) => + graphql_flutter.useQuery(options); +graphql.ObservableQuery + useWatchQuery$GetMealPlanForDay( + WatchOptions$Query$GetMealPlanForDay options) => + graphql_flutter.useWatchQuery(options); -class Query$GetMeals$Widget extends graphql_flutter.Query { - Query$GetMeals$Widget({ +class Query$GetMealPlanForDay$Widget + extends graphql_flutter.Query { + Query$GetMealPlanForDay$Widget({ widgets.Key? key, - Options$Query$GetMeals? options, - required graphql_flutter.QueryBuilder builder, + required Options$Query$GetMealPlanForDay options, + required graphql_flutter.QueryBuilder builder, }) : super( key: key, - options: options ?? Options$Query$GetMeals(), + options: options, builder: builder, ); } + +class Query$GetMealPlanForDay$getCanteens { + Query$GetMealPlanForDay$getCanteens({ + required this.lines, + this.$__typename = 'Canteen', + }); + + factory Query$GetMealPlanForDay$getCanteens.fromJson( + Map json) { + final l$lines = json['lines']; + final l$$__typename = json['__typename']; + return Query$GetMealPlanForDay$getCanteens( + lines: (l$lines as List) + .map((e) => Query$GetMealPlanForDay$getCanteens$lines.fromJson( + (e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final List lines; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$lines = lines; + _resultData['lines'] = l$lines.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$lines = lines; + final l$$__typename = $__typename; + return Object.hashAll([ + Object.hashAll(l$lines.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetMealPlanForDay$getCanteens) || + runtimeType != other.runtimeType) { + return false; + } + final l$lines = lines; + final lOther$lines = other.lines; + if (l$lines.length != lOther$lines.length) { + return false; + } + for (int i = 0; i < l$lines.length; i++) { + final l$lines$entry = l$lines[i]; + final lOther$lines$entry = lOther$lines[i]; + if (l$lines$entry != lOther$lines$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetMealPlanForDay$getCanteens + on Query$GetMealPlanForDay$getCanteens { + CopyWith$Query$GetMealPlanForDay$getCanteens< + Query$GetMealPlanForDay$getCanteens> + get copyWith => CopyWith$Query$GetMealPlanForDay$getCanteens( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetMealPlanForDay$getCanteens { + factory CopyWith$Query$GetMealPlanForDay$getCanteens( + Query$GetMealPlanForDay$getCanteens instance, + TRes Function(Query$GetMealPlanForDay$getCanteens) then, + ) = _CopyWithImpl$Query$GetMealPlanForDay$getCanteens; + + factory CopyWith$Query$GetMealPlanForDay$getCanteens.stub(TRes res) = + _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens; + + TRes call({ + List? lines, + String? $__typename, + }); + TRes lines( + Iterable Function( + Iterable< + CopyWith$Query$GetMealPlanForDay$getCanteens$lines< + Query$GetMealPlanForDay$getCanteens$lines>>) + _fn); +} + +class _CopyWithImpl$Query$GetMealPlanForDay$getCanteens + implements CopyWith$Query$GetMealPlanForDay$getCanteens { + _CopyWithImpl$Query$GetMealPlanForDay$getCanteens( + this._instance, + this._then, + ); + + final Query$GetMealPlanForDay$getCanteens _instance; + + final TRes Function(Query$GetMealPlanForDay$getCanteens) _then; + + static const _undefined = {}; + + TRes call({ + Object? lines = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetMealPlanForDay$getCanteens( + lines: lines == _undefined || lines == null + ? _instance.lines + : (lines as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes lines( + Iterable Function( + Iterable< + CopyWith$Query$GetMealPlanForDay$getCanteens$lines< + Query$GetMealPlanForDay$getCanteens$lines>>) + _fn) => + call( + lines: _fn(_instance.lines + .map((e) => CopyWith$Query$GetMealPlanForDay$getCanteens$lines( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens + implements CopyWith$Query$GetMealPlanForDay$getCanteens { + _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens(this._res); + + TRes _res; + + call({ + List? lines, + String? $__typename, + }) => + _res; + lines(_fn) => _res; +} + +class Query$GetMealPlanForDay$getCanteens$lines { + Query$GetMealPlanForDay$getCanteens$lines({ + required this.id, + required this.name, + required this.canteen, + required this.meals, + this.$__typename = 'Line', + }); + + factory Query$GetMealPlanForDay$getCanteens$lines.fromJson( + Map json) { + final l$id = json['id']; + final l$name = json['name']; + final l$canteen = json['canteen']; + final l$meals = json['meals']; + final l$$__typename = json['__typename']; + return Query$GetMealPlanForDay$getCanteens$lines( + id: (l$id as String), + name: (l$name as String), + canteen: Query$GetMealPlanForDay$getCanteens$lines$canteen.fromJson( + (l$canteen as Map)), + meals: (l$meals as List) + .map((e) => Fragment$mealInfo.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final String id; + + final String name; + + final Query$GetMealPlanForDay$getCanteens$lines$canteen canteen; + + final List meals; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$id = id; + _resultData['id'] = l$id; + final l$name = name; + _resultData['name'] = l$name; + final l$canteen = canteen; + _resultData['canteen'] = l$canteen.toJson(); + final l$meals = meals; + _resultData['meals'] = l$meals.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$id = id; + final l$name = name; + final l$canteen = canteen; + final l$meals = meals; + final l$$__typename = $__typename; + return Object.hashAll([ + l$id, + l$name, + l$canteen, + Object.hashAll(l$meals.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetMealPlanForDay$getCanteens$lines) || + runtimeType != other.runtimeType) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$canteen = canteen; + final lOther$canteen = other.canteen; + if (l$canteen != lOther$canteen) { + return false; + } + final l$meals = meals; + final lOther$meals = other.meals; + if (l$meals.length != lOther$meals.length) { + return false; + } + for (int i = 0; i < l$meals.length; i++) { + final l$meals$entry = l$meals[i]; + final lOther$meals$entry = lOther$meals[i]; + if (l$meals$entry != lOther$meals$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetMealPlanForDay$getCanteens$lines + on Query$GetMealPlanForDay$getCanteens$lines { + CopyWith$Query$GetMealPlanForDay$getCanteens$lines< + Query$GetMealPlanForDay$getCanteens$lines> + get copyWith => CopyWith$Query$GetMealPlanForDay$getCanteens$lines( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetMealPlanForDay$getCanteens$lines { + factory CopyWith$Query$GetMealPlanForDay$getCanteens$lines( + Query$GetMealPlanForDay$getCanteens$lines instance, + TRes Function(Query$GetMealPlanForDay$getCanteens$lines) then, + ) = _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines; + + factory CopyWith$Query$GetMealPlanForDay$getCanteens$lines.stub(TRes res) = + _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines; + + TRes call({ + String? id, + String? name, + Query$GetMealPlanForDay$getCanteens$lines$canteen? canteen, + List? meals, + String? $__typename, + }); + CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen get canteen; + TRes meals( + Iterable Function( + Iterable>) + _fn); +} + +class _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines + implements CopyWith$Query$GetMealPlanForDay$getCanteens$lines { + _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines( + this._instance, + this._then, + ); + + final Query$GetMealPlanForDay$getCanteens$lines _instance; + + final TRes Function(Query$GetMealPlanForDay$getCanteens$lines) _then; + + static const _undefined = {}; + + TRes call({ + Object? id = _undefined, + Object? name = _undefined, + Object? canteen = _undefined, + Object? meals = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetMealPlanForDay$getCanteens$lines( + id: id == _undefined || id == null ? _instance.id : (id as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + canteen: canteen == _undefined || canteen == null + ? _instance.canteen + : (canteen as Query$GetMealPlanForDay$getCanteens$lines$canteen), + meals: meals == _undefined || meals == null + ? _instance.meals + : (meals as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen get canteen { + final local$canteen = _instance.canteen; + return CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen( + local$canteen, (e) => call(canteen: e)); + } + + TRes meals( + Iterable Function( + Iterable>) + _fn) => + call( + meals: _fn(_instance.meals.map((e) => CopyWith$Fragment$mealInfo( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines + implements CopyWith$Query$GetMealPlanForDay$getCanteens$lines { + _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines(this._res); + + TRes _res; + + call({ + String? id, + String? name, + Query$GetMealPlanForDay$getCanteens$lines$canteen? canteen, + List? meals, + String? $__typename, + }) => + _res; + CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen + get canteen => + CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen.stub(_res); + meals(_fn) => _res; +} + +class Query$GetMealPlanForDay$getCanteens$lines$canteen { + Query$GetMealPlanForDay$getCanteens$lines$canteen({ + required this.id, + required this.name, + this.$__typename = 'Canteen', + }); + + factory Query$GetMealPlanForDay$getCanteens$lines$canteen.fromJson( + Map json) { + final l$id = json['id']; + final l$name = json['name']; + final l$$__typename = json['__typename']; + return Query$GetMealPlanForDay$getCanteens$lines$canteen( + id: (l$id as String), + name: (l$name as String), + $__typename: (l$$__typename as String), + ); + } + + final String id; + + final String name; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$id = id; + _resultData['id'] = l$id; + final l$name = name; + _resultData['name'] = l$name; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$id = id; + final l$name = name; + final l$$__typename = $__typename; + return Object.hashAll([ + l$id, + l$name, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetMealPlanForDay$getCanteens$lines$canteen) || + runtimeType != other.runtimeType) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetMealPlanForDay$getCanteens$lines$canteen + on Query$GetMealPlanForDay$getCanteens$lines$canteen { + CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen< + Query$GetMealPlanForDay$getCanteens$lines$canteen> + get copyWith => + CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen< + TRes> { + factory CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen( + Query$GetMealPlanForDay$getCanteens$lines$canteen instance, + TRes Function(Query$GetMealPlanForDay$getCanteens$lines$canteen) then, + ) = _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen; + + factory CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen.stub( + TRes res) = + _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen; + + TRes call({ + String? id, + String? name, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen + implements + CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen { + _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen( + this._instance, + this._then, + ); + + final Query$GetMealPlanForDay$getCanteens$lines$canteen _instance; + + final TRes Function(Query$GetMealPlanForDay$getCanteens$lines$canteen) _then; + + static const _undefined = {}; + + TRes call({ + Object? id = _undefined, + Object? name = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetMealPlanForDay$getCanteens$lines$canteen( + id: id == _undefined || id == null ? _instance.id : (id as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen + implements + CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen { + _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen( + this._res); + + TRes _res; + + call({ + String? id, + String? name, + String? $__typename, + }) => + _res; +} From 9e0f6fd092a10b980795da9493813021081978e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sat, 8 Jul 2023 21:33:00 +0200 Subject: [PATCH 019/184] make returntype nullable --- .../model/local_storage/SharedPreferenceAccess.dart | 13 ++++++------- .../repository/interface/ILocalStorage.dart | 12 ++++++------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index 654f68eb..7e0dc284 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -22,19 +22,19 @@ class SharedPreferenceAccess implements ILocalStorage { SharedPreferenceAccess(this._pref); @override - Future getClientIdentifier() async { + Future getClientIdentifier() async { final clientIdentifier = _pref.getString('clientIdentifier') ?? ""; return Future.value(clientIdentifier); } @override - Future getColorScheme() async { + Future getColorScheme() async { final colorScheme = _pref.getString('colorScheme'); return Future.value(ColorScheme.values.firstWhere((e) => e.toString() == colorScheme)); } @override - Future getFilterPreferences() async { + Future getFilterPreferences() async { // get data from shared preferences final categories = _pref.getStringList('filterCategories'); final allergens = _pref.getStringList('filterAllergens'); @@ -80,16 +80,15 @@ class SharedPreferenceAccess implements ILocalStorage { } @override - Future getMealPlanFormat() async { + Future getMealPlanFormat() async { final mealPlanFormat = _pref.getString('mealPlanFormat'); return Future.value(MealPlanFormat.values.firstWhere((e) => e.toString() == mealPlanFormat)); } @override - Future getPriceCategory() async { + Future getPriceCategory() async { final priceCategory = _pref.getString('priceCategory'); return Future.value(PriceCategory.values.firstWhere((e) => e.toString() == priceCategory)); - } @override @@ -125,7 +124,7 @@ class SharedPreferenceAccess implements ILocalStorage { } @override - Future getCanteen() async { + Future getCanteen() async { final canteen = _pref.getString('canteen') ?? ""; return Future.value(canteen); } diff --git a/app/lib/view_model/repository/interface/ILocalStorage.dart b/app/lib/view_model/repository/interface/ILocalStorage.dart index 8a3e19e4..b7d09d6e 100644 --- a/app/lib/view_model/repository/interface/ILocalStorage.dart +++ b/app/lib/view_model/repository/interface/ILocalStorage.dart @@ -8,7 +8,7 @@ import '../data_classes/settings/PriceCategory.dart'; abstract class ILocalStorage { /// The device identifier is returned. /// @return The device identifier. - Future getClientIdentifier(); + Future getClientIdentifier(); /// The device identifier is set. /// @param identifier The new device identifier. @@ -17,7 +17,7 @@ abstract class ILocalStorage { /// The saved FilterPreferences is returned. /// @return The saved FilterPreferences. - Future getFilterPreferences(); + Future getFilterPreferences(); /// The committed FilterPreferences is set. /// @param filter The new FilterPreferences. @@ -26,7 +26,7 @@ abstract class ILocalStorage { /// The saved canteen id is returned. /// @return The saved canteen id. - Future getCanteen(); + Future getCanteen(); /// The committed id of the canteen is set. /// @param canteen The id of the new canteen. @@ -35,7 +35,7 @@ abstract class ILocalStorage { /// The saved ColorScheme is returned. /// @return The saved ColorScheme. - Future getColorScheme(); + Future getColorScheme(); /// The committed ColorScheme is set. /// @param scheme The new ColorScheme. @@ -45,7 +45,7 @@ abstract class ILocalStorage { /// The saved PriceCategory is returned. /// @return The saved PriceCategory. /// @return The result of the update. - Future getPriceCategory(); + Future getPriceCategory(); /// The committed PriceCategory is set. /// @param category The new PriceCategory. @@ -54,7 +54,7 @@ abstract class ILocalStorage { /// The saved MealPlanFormat is returned. /// @return The saved MealPlanFormat. - Future getMealPlanFormat(); + Future getMealPlanFormat(); /// The committed MealPlanFormat is set. /// @param format The new MealPlanFormat. From 1b9e4f4355eb0127ab170e3eef4ec24654e66b97 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Sat, 8 Jul 2023 22:07:33 +0200 Subject: [PATCH 020/184] first data query with conversion --- .../model/api_server/GraphQlServerAccess.dart | 95 +++++++++++++++++-- .../model/api_server/requests/querys.graphql | 4 +- .../api_server/requests/querys.graphql.dart | 82 ++++++++-------- .../model/api_server/requests/schema.graphql | 2 +- 4 files changed, 134 insertions(+), 49 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index 1d4f79bd..76529b6c 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -1,9 +1,13 @@ +import 'package:app/model/api_server/requests/querys.graphql.dart'; import 'package:app/model/api_server/requests/schema.graphql.dart'; +import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/meal/Price.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; @@ -11,6 +15,7 @@ import 'package:app/view_model/repository/data_classes/settings/ReportCategory.d import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; +import 'package:intl/intl.dart'; import '../../view_model/repository/interface/IServerAccess.dart'; import 'requests/mutations.graphql.dart'; @@ -25,6 +30,8 @@ class GraphQlServerAccess implements IServerAccess { GraphQlServerAccess._(this._clientId); + + factory GraphQlServerAccess(String clientId) { return GraphQlServerAccess._(clientId); } @@ -75,7 +82,7 @@ class GraphQlServerAccess implements IServerAccess { // TODO: auth final result = await _client.mutate$LinkImage(Options$Mutation$LinkImage( variables: - Variables$Mutation$LinkImage(imageUrl: url, mealId: meal.id))); + Variables$Mutation$LinkImage(imageUrl: url, mealId: meal.id))); final parsedData = result.parsedData; return parsedData?.addImage ?? false; } @@ -104,11 +111,41 @@ class GraphQlServerAccess implements IServerAccess { } // ---------------------- queries ---------------------- - + static const DAYS_TO_PARSE = 7; @override Future>> updateAll() async { - // TODO: implement updateAll - throw UnimplementedError(); + final dateFormat = DateFormat("Y-m-d"); // TODO correct? + final date = DateTime.now(); // TODO for next 7 days + + + var completeList = []; + + for (int offset = 0; offset < 7; offset++) { + final offsetDate = date.add(Duration(days: offset)); + final result = await _client.query$GetMealPlanForDay( + Options$Query$GetMealPlanForDay( + variables: Variables$Query$GetMealPlanForDay( + date: dateFormat.format(offsetDate)))); + final parsedData = result.parsedData; + + + final mealPlan = parsedData?.getCanteens.expand((e) => + e.lines.asMap().map((idx, e) => + MapEntry(idx, + Mealplan(date: offsetDate, + line: Line(id: e.id, + name: e.name, + canteen: _convertCanteen(e.canteen), + position: idx), + isClosed: false, // TODO what to do when no data available + meals: _convertMeals(e.meals ?? [])), + ), + ).values.toList(), + ).toList() ?? []; + + completeList.addAll(mealPlan); + } + return Success(completeList); } @override @@ -118,14 +155,58 @@ class GraphQlServerAccess implements IServerAccess { } @override - Future>> updateCanteen( - Canteen canteen, DateTime date) async { + Future>> updateCanteen(Canteen canteen, + DateTime date) async { // TODO: implement updateCanteen throw UnimplementedError(); } } -/// ---------------------- utility functions ---------------------- + +List _convertMeals(List meals) { + return meals.map((e) => + Meal(id: e.id, + name: e.name, + foodType: _convertMealType(e.mealType), + price: _convertPrice(e.price))).toList(); +} + +FoodType _convertMealType(Enum$MealType mealType) { + switch (mealType) { + case Enum$MealType.BEEF: + return FoodType.beef; + case Enum$MealType.BEEF_AW: + return FoodType.beefAw; + case Enum$MealType.FISH: + return FoodType.fish; + case Enum$MealType.PORK: + return FoodType.pork; + case Enum$MealType.PORK_AW: + return FoodType.porkAw; + case Enum$MealType.VEGAN: + return FoodType.vegan; + case Enum$MealType.VEGETARIAN: + return FoodType.vegetarian; + case Enum$MealType.UNKNOWN: + return FoodType.unknown; + case Enum$MealType.$unknown: + return FoodType.unknown; + } +} + +Price _convertPrice(Fragment$mealInfo$price price) { + return Price(student: price.student, + employee: price.employee, + pupil: price.pupil, + guest: price.guest); +} + +Canteen _convertCanteen( + Query$GetMealPlanForDay$getCanteens$lines$canteen canteen) { + return Canteen(id: canteen.id, name: canteen.name); +} + +// ---------------------- utility functions ---------------------- Enum$ReportReason _convertToReportReason(ReportCategory reportReason) { switch (reportReason) { diff --git a/app/lib/model/api_server/requests/querys.graphql b/app/lib/model/api_server/requests/querys.graphql index ca437b9f..2066e00a 100644 --- a/app/lib/model/api_server/requests/querys.graphql +++ b/app/lib/model/api_server/requests/querys.graphql @@ -1,4 +1,4 @@ -query GetMealPlanForDay($date0: NaiveDate!) { +query GetMealPlanForDay($date: NaiveDate!) { getCanteens { lines { id @@ -7,7 +7,7 @@ query GetMealPlanForDay($date0: NaiveDate!) { id name } - meals(date: $date0) { + meals(date: $date) { ...mealInfo } } diff --git a/app/lib/model/api_server/requests/querys.graphql.dart b/app/lib/model/api_server/requests/querys.graphql.dart index 82b01e15..9ff54cdc 100644 --- a/app/lib/model/api_server/requests/querys.graphql.dart +++ b/app/lib/model/api_server/requests/querys.graphql.dart @@ -1392,9 +1392,9 @@ class _CopyWithStubImpl$Fragment$mealInfo$images } class Variables$Query$GetMealPlanForDay { - factory Variables$Query$GetMealPlanForDay({required String date0}) => + factory Variables$Query$GetMealPlanForDay({required String date}) => Variables$Query$GetMealPlanForDay._({ - r'date0': date0, + r'date': date, }); Variables$Query$GetMealPlanForDay._(this._$data); @@ -1402,18 +1402,18 @@ class Variables$Query$GetMealPlanForDay { factory Variables$Query$GetMealPlanForDay.fromJson( Map data) { final result$data = {}; - final l$date0 = data['date0']; - result$data['date0'] = (l$date0 as String); + final l$date = data['date']; + result$data['date'] = (l$date as String); return Variables$Query$GetMealPlanForDay._(result$data); } Map _$data; - String get date0 => (_$data['date0'] as String); + String get date => (_$data['date'] as String); Map toJson() { final result$data = {}; - final l$date0 = date0; - result$data['date0'] = l$date0; + final l$date = date; + result$data['date'] = l$date; return result$data; } @@ -1431,9 +1431,9 @@ class Variables$Query$GetMealPlanForDay { runtimeType != other.runtimeType) { return false; } - final l$date0 = date0; - final lOther$date0 = other.date0; - if (l$date0 != lOther$date0) { + final l$date = date; + final lOther$date = other.date; + if (l$date != lOther$date) { return false; } return true; @@ -1441,8 +1441,8 @@ class Variables$Query$GetMealPlanForDay { @override int get hashCode { - final l$date0 = date0; - return Object.hashAll([l$date0]); + final l$date = date; + return Object.hashAll([l$date]); } } @@ -1455,7 +1455,7 @@ abstract class CopyWith$Variables$Query$GetMealPlanForDay { factory CopyWith$Variables$Query$GetMealPlanForDay.stub(TRes res) = _CopyWithStubImpl$Variables$Query$GetMealPlanForDay; - TRes call({String? date0}); + TRes call({String? date}); } class _CopyWithImpl$Variables$Query$GetMealPlanForDay @@ -1471,10 +1471,10 @@ class _CopyWithImpl$Variables$Query$GetMealPlanForDay static const _undefined = {}; - TRes call({Object? date0 = _undefined}) => + TRes call({Object? date = _undefined}) => _then(Variables$Query$GetMealPlanForDay._({ ..._instance._$data, - if (date0 != _undefined && date0 != null) 'date0': (date0 as String), + if (date != _undefined && date != null) 'date': (date as String), })); } @@ -1484,7 +1484,7 @@ class _CopyWithStubImpl$Variables$Query$GetMealPlanForDay TRes _res; - call({String? date0}) => _res; + call({String? date}) => _res; } class Query$GetMealPlanForDay { @@ -1646,7 +1646,7 @@ const documentNodeQueryGetMealPlanForDay = DocumentNode(definitions: [ name: NameNode(value: 'GetMealPlanForDay'), variableDefinitions: [ VariableDefinitionNode( - variable: VariableNode(name: NameNode(value: 'date0')), + variable: VariableNode(name: NameNode(value: 'date')), type: NamedTypeNode( name: NameNode(value: 'NaiveDate'), isNonNull: true, @@ -1718,7 +1718,7 @@ const documentNodeQueryGetMealPlanForDay = DocumentNode(definitions: [ arguments: [ ArgumentNode( name: NameNode(value: 'date'), - value: VariableNode(name: NameNode(value: 'date0')), + value: VariableNode(name: NameNode(value: 'date')), ) ], directives: [], @@ -2084,7 +2084,7 @@ class Query$GetMealPlanForDay$getCanteens$lines { required this.id, required this.name, required this.canteen, - required this.meals, + this.meals, this.$__typename = 'Line', }); @@ -2100,8 +2100,8 @@ class Query$GetMealPlanForDay$getCanteens$lines { name: (l$name as String), canteen: Query$GetMealPlanForDay$getCanteens$lines$canteen.fromJson( (l$canteen as Map)), - meals: (l$meals as List) - .map((e) => Fragment$mealInfo.fromJson((e as Map))) + meals: (l$meals as List?) + ?.map((e) => Fragment$mealInfo.fromJson((e as Map))) .toList(), $__typename: (l$$__typename as String), ); @@ -2113,7 +2113,7 @@ class Query$GetMealPlanForDay$getCanteens$lines { final Query$GetMealPlanForDay$getCanteens$lines$canteen canteen; - final List meals; + final List? meals; final String $__typename; @@ -2126,7 +2126,7 @@ class Query$GetMealPlanForDay$getCanteens$lines { final l$canteen = canteen; _resultData['canteen'] = l$canteen.toJson(); final l$meals = meals; - _resultData['meals'] = l$meals.map((e) => e.toJson()).toList(); + _resultData['meals'] = l$meals?.map((e) => e.toJson()).toList(); final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -2143,7 +2143,7 @@ class Query$GetMealPlanForDay$getCanteens$lines { l$id, l$name, l$canteen, - Object.hashAll(l$meals.map((v) => v)), + l$meals == null ? null : Object.hashAll(l$meals.map((v) => v)), l$$__typename, ]); } @@ -2174,15 +2174,19 @@ class Query$GetMealPlanForDay$getCanteens$lines { } final l$meals = meals; final lOther$meals = other.meals; - if (l$meals.length != lOther$meals.length) { - return false; - } - for (int i = 0; i < l$meals.length; i++) { - final l$meals$entry = l$meals[i]; - final lOther$meals$entry = lOther$meals[i]; - if (l$meals$entry != lOther$meals$entry) { + if (l$meals != null && lOther$meals != null) { + if (l$meals.length != lOther$meals.length) { return false; } + for (int i = 0; i < l$meals.length; i++) { + final l$meals$entry = l$meals[i]; + final lOther$meals$entry = lOther$meals[i]; + if (l$meals$entry != lOther$meals$entry) { + return false; + } + } + } else if (l$meals != lOther$meals) { + return false; } final l$$__typename = $__typename; final lOther$$__typename = other.$__typename; @@ -2221,8 +2225,8 @@ abstract class CopyWith$Query$GetMealPlanForDay$getCanteens$lines { }); CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen get canteen; TRes meals( - Iterable Function( - Iterable>) + Iterable? Function( + Iterable>?) _fn); } @@ -2254,9 +2258,9 @@ class _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines canteen: canteen == _undefined || canteen == null ? _instance.canteen : (canteen as Query$GetMealPlanForDay$getCanteens$lines$canteen), - meals: meals == _undefined || meals == null + meals: meals == _undefined ? _instance.meals - : (meals as List), + : (meals as List?), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String), @@ -2268,14 +2272,14 @@ class _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines } TRes meals( - Iterable Function( - Iterable>) + Iterable? Function( + Iterable>?) _fn) => call( - meals: _fn(_instance.meals.map((e) => CopyWith$Fragment$mealInfo( + meals: _fn(_instance.meals?.map((e) => CopyWith$Fragment$mealInfo( e, (i) => i, - ))).toList()); + )))?.toList()); } class _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines diff --git a/app/lib/model/api_server/requests/schema.graphql b/app/lib/model/api_server/requests/schema.graphql index 1f085f44..0a284803 100644 --- a/app/lib/model/api_server/requests/schema.graphql +++ b/app/lib/model/api_server/requests/schema.graphql @@ -193,7 +193,7 @@ type Line { canteen: Canteen! # Provides the meals offered at this line on a given day. Requires a date. - meals(date: NaiveDate!): [Meal!]! + meals(date: NaiveDate!): [Meal!] } type Meal { From 9c13b13bd54e10b255265f30c8c80573d225c4f7 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Sun, 9 Jul 2023 18:57:14 +0200 Subject: [PATCH 021/184] Implemented MensaButtonGroup --- app/lib/view/core/MensaNaviagtionBar.dart | 0 app/lib/view/filter/MensaButtonGroup.dart | 42 +++++++++++++++++++ .../view/filter/MensaButtonGroupEntry.dart | 33 +++++++++++++++ 3 files changed, 75 insertions(+) delete mode 100644 app/lib/view/core/MensaNaviagtionBar.dart create mode 100644 app/lib/view/filter/MensaButtonGroup.dart create mode 100644 app/lib/view/filter/MensaButtonGroupEntry.dart diff --git a/app/lib/view/core/MensaNaviagtionBar.dart b/app/lib/view/core/MensaNaviagtionBar.dart deleted file mode 100644 index e69de29b..00000000 diff --git a/app/lib/view/filter/MensaButtonGroup.dart b/app/lib/view/filter/MensaButtonGroup.dart new file mode 100644 index 00000000..bf9b97d1 --- /dev/null +++ b/app/lib/view/filter/MensaButtonGroup.dart @@ -0,0 +1,42 @@ +import 'package:app/view/filter/MensaButtonGroupEntry.dart'; +import 'package:flutter/material.dart'; + +/// A button group that is used in the Mensa app. +class MensaButtonGroup extends StatelessWidget { + final T _value; + final Function(T) _onChanged; + final List> _entries; + + /// Creates a new MensaButtonGroup. + /// @param key The key to identify this widget. + /// @param value The value that is currently selected. + /// @param onChanged The function that is called when the value changes. + /// @param entries The entries that can be selected. + /// @returns A new MensaButtonGroup. + const MensaButtonGroup( + {super.key, + required T value, + required Function(T) onChanged, + required List> entries}) + : _value = value, + _onChanged = onChanged, + _entries = entries; + + @override + Widget build(BuildContext context) { + return Container( + height: 44, + padding: const EdgeInsets.symmetric(horizontal: 1, vertical: 2), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: Theme.of(context).colorScheme.surface, width: 1)), + child: IntrinsicHeight(child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: _entries + .map((e) => e.build(context, e.value == _value, _onChanged)) + .toList(), + )), + ); + } +} diff --git a/app/lib/view/filter/MensaButtonGroupEntry.dart b/app/lib/view/filter/MensaButtonGroupEntry.dart new file mode 100644 index 00000000..805ba761 --- /dev/null +++ b/app/lib/view/filter/MensaButtonGroupEntry.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +/// This class represents a single entry in a MensaButtonGroup. +class MensaButtonGroupEntry { + final String _title; + final T value; + + /// Creates a new MensaButtonGroupEntry. + /// @param title The title of the entry. + /// @param value The value of the entry. + /// @returns A new MensaButtonGroupEntry. + const MensaButtonGroupEntry({required String title, required this.value}) + : _title = title; + + /// Builds the widget. + /// @param context The context in which the widget is built. + /// @param selected Whether the entry is selected. + /// @param onChanged The function that is called when the entry is pressed. + /// @returns The widget. + Widget build( + BuildContext context, bool selected, void Function(T) onChanged) { + return Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 1), + child: MaterialButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(2)), + color: selected ? Theme.of(context).colorScheme.primary : null, + elevation: 0, + onPressed: () => onChanged(value), + child: Text(_title)))); + } +} From fb68e5c5ef3e4229254825137544874f4f989405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 9 Jul 2023 19:55:50 +0200 Subject: [PATCH 022/184] initialise model mocks for testing --- app/test/model/mocks/ApiMock.dart | 4 ++++ app/test/model/mocks/DatabaseMock.dart | 4 ++++ app/test/model/mocks/LocalStorageMock.dart | 4 ++++ 3 files changed, 12 insertions(+) create mode 100644 app/test/model/mocks/ApiMock.dart create mode 100644 app/test/model/mocks/DatabaseMock.dart create mode 100644 app/test/model/mocks/LocalStorageMock.dart diff --git a/app/test/model/mocks/ApiMock.dart b/app/test/model/mocks/ApiMock.dart new file mode 100644 index 00000000..950f8487 --- /dev/null +++ b/app/test/model/mocks/ApiMock.dart @@ -0,0 +1,4 @@ +import 'package:app/view_model/repository/interface/IServerAccess.dart'; +import 'package:mocktail/mocktail.dart'; + +class ApiMock extends Mock implements IServerAccess {} \ No newline at end of file diff --git a/app/test/model/mocks/DatabaseMock.dart b/app/test/model/mocks/DatabaseMock.dart new file mode 100644 index 00000000..d7cefe25 --- /dev/null +++ b/app/test/model/mocks/DatabaseMock.dart @@ -0,0 +1,4 @@ +import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; +import 'package:mocktail/mocktail.dart'; + +class DatabaseMock extends Mock implements IDatabaseAccess {} \ No newline at end of file diff --git a/app/test/model/mocks/LocalStorageMock.dart b/app/test/model/mocks/LocalStorageMock.dart new file mode 100644 index 00000000..0c51e0d3 --- /dev/null +++ b/app/test/model/mocks/LocalStorageMock.dart @@ -0,0 +1,4 @@ +import 'package:app/view_model/repository/interface/ILocalStorage.dart'; +import 'package:mocktail/mocktail.dart'; + +class LocalStorageMock extends Mock implements ILocalStorage {} \ No newline at end of file From dd267b95fdcf453952a5a27017e5f45dae29a2f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 9 Jul 2023 19:56:12 +0200 Subject: [PATCH 023/184] initialise FavoriteMealAccess.dart --- .../logic/favorite/FavoriteMealAccess.dart | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 app/lib/view_model/logic/favorite/FavoriteMealAccess.dart diff --git a/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart new file mode 100644 index 00000000..6146a5f4 --- /dev/null +++ b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart @@ -0,0 +1,52 @@ + +import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; +import 'package:flutter/material.dart'; + +class FavoriteMealAccess extends ChangeNotifier implements IFavoriteMealAccess { + final IDatabaseAccess _database; + + late List _favorites; + + FavoriteMealAccess(this._database) { + _init(); + } + + Future _init() async { + _favorites = await _database.getFavorites(); + } + + @override + Future addFavoriteMeal(Meal meal) async { + if (await isFavoriteMeal(meal)) { + return; + } + + await _database.addFavorite(meal); + _favorites.add(meal); + notifyListeners(); + } + + @override + Future> getFavoriteMeals() async { + return Future.value(_favorites); + } + + @override + Future isFavoriteMeal(Meal meal) async { + return Future.value(_favorites.map((favorite) => favorite.id).contains(meal.id)); + } + + @override + Future removeFavoriteMeal(Meal meal) async { + if (await isFavoriteMeal(meal) == false) { + return; + } + + await _database.deleteFavorite(meal); + _favorites.removeWhere((element) => element.id == meal.id); + notifyListeners(); + } + +} \ No newline at end of file From 18f51b7d46b3b28d98ee8a2afc5e01cd207ddd1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 9 Jul 2023 19:56:28 +0200 Subject: [PATCH 024/184] write test for FavoriteMealAccess.dart --- .../view-model/FavoriteMealAccessTest.dart | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 app/test/view-model/FavoriteMealAccessTest.dart diff --git a/app/test/view-model/FavoriteMealAccessTest.dart b/app/test/view-model/FavoriteMealAccessTest.dart new file mode 100644 index 00000000..e60dcd0f --- /dev/null +++ b/app/test/view-model/FavoriteMealAccessTest.dart @@ -0,0 +1,77 @@ +import 'package:app/view_model/logic/favorite/FavoriteMealAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/meal/Price.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../model/mocks/DatabaseMock.dart'; + +void main () { + final database = DatabaseMock(); + + late FavoriteMealAccess favorites; + + final meal1 = Meal(id: "1", + name: "vegan Meal", + foodType: FoodType.vegan, + price: Price(student: 200, employee: 300, pupil: 400, guest: 500)); + final meal2 = Meal(id: "42", + name: "vegetarian Meal", + foodType: FoodType.vegetarian, + price: Price(student: 200, employee: 300, pupil: 400, guest: 500)); + final meal3 = Meal(id: "12", + name: "fishi Meal", + foodType: FoodType.fish, + price: Price(student: 200, employee: 300, pupil: 400, guest: 500)); + var meals = [meal1, meal2]; + + setUp(() { + when(() => database.getFavorites()).thenAnswer((_) async => meals); + favorites = FavoriteMealAccess(database); + }); + + test("Test initialisation", () async { + expect(await favorites.getFavoriteMeals(), meals); + }); + + group("Favorite check", () { + test("is Favorite", () async { + expect(await favorites.isFavoriteMeal(meal1), true); + }); + + test("is no favorite", () async { + expect(await favorites.isFavoriteMeal(meal3), false); + }); + }); + + group("Test adding and removing meals", () { + test("add meal already in meals", () async { + await favorites.addFavoriteMeal(meal1); + verifyNever(() => database.addFavorite(meal1)); + expect(await favorites.getFavoriteMeals(), meals); + }); + + test("remove meal not in meals", () async { + await favorites.removeFavoriteMeal(meal3); + verifyNever(() => database.deleteFavorite(meal3)); + expect(await favorites.getFavoriteMeals(), meals); + }); + + test("add meal to meals (not in meals)", () async { + when(() => database.addFavorite(meal3)).thenAnswer((_) async {}); + await favorites.addFavoriteMeal(meal3); + verify(() => database.addFavorite(meal3)).called(1); + meals.add(meal3); + expect(await favorites.getFavoriteMeals(), meals); + }); + + test("remove meal from meals (that is in meals)", () async { + when(() => database.deleteFavorite(meal1)).thenAnswer((_) async {}); + await favorites.removeFavoriteMeal(meal1); + verify(() => database.deleteFavorite(meal1)).called(1); + meals.remove(meal1); + expect(await favorites.getFavoriteMeals(), meals); + }); + }); +} \ No newline at end of file From c9a642b0ea79f5d4ae4fcf7b634b18026f8490e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 10 Jul 2023 10:15:15 +0200 Subject: [PATCH 025/184] error handling changes --- .../view_model/logic/meal/IMealAccess.dart | 5 +- .../ClosedCanteenException.dart | 9 ---- .../error_handling/FilteredMealException.dart | 9 ---- .../error_handling/MealPlanException.dart | 48 +++++++++++++++++++ .../error_handling/NoConnectionException.dart | 9 ---- .../error_handling/NoDataExeption.dart | 9 ---- .../repository/error_handling/Result.dart | 10 ++-- .../repository/interface/IDatabaseAccess.dart | 6 ++- .../repository/interface/IServerAccess.dart | 10 ++-- 9 files changed, 67 insertions(+), 48 deletions(-) delete mode 100644 app/lib/view_model/repository/error_handling/ClosedCanteenException.dart delete mode 100644 app/lib/view_model/repository/error_handling/FilteredMealException.dart create mode 100644 app/lib/view_model/repository/error_handling/MealPlanException.dart delete mode 100644 app/lib/view_model/repository/error_handling/NoConnectionException.dart delete mode 100644 app/lib/view_model/repository/error_handling/NoDataExeption.dart diff --git a/app/lib/view_model/logic/meal/IMealAccess.dart b/app/lib/view_model/logic/meal/IMealAccess.dart index ed14d063..4b4cdcf5 100644 --- a/app/lib/view_model/logic/meal/IMealAccess.dart +++ b/app/lib/view_model/logic/meal/IMealAccess.dart @@ -1,5 +1,6 @@ import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; +import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; import 'package:flutter/cupertino.dart'; import '../../repository/data_classes/meal/Meal.dart'; @@ -13,13 +14,13 @@ abstract class IMealAccess { /// @param date The date of the mealplan /// @param canteen The canteen of the mealplan /// @return The mealplan of the committed date of the committed canteen or an error - Future>> getMealPlan(DateTime date, Canteen canteen); + Future, MealPlanException>> getMealPlan(DateTime date, Canteen canteen); /// This method returns the meal with the committed id form the database. /// If the requested data is not stored there, the data is requested from the server. /// @param id The id of the meal /// @return The meal with the committed id or an error - Future> getMealFromId(String id); + Future> getMealFromId(String id); /// This method updates all meal plans of the committed date of the committed canteen. /// If the connection to the server fails, an temporal error message is displayed. diff --git a/app/lib/view_model/repository/error_handling/ClosedCanteenException.dart b/app/lib/view_model/repository/error_handling/ClosedCanteenException.dart deleted file mode 100644 index b5c0a2d4..00000000 --- a/app/lib/view_model/repository/error_handling/ClosedCanteenException.dart +++ /dev/null @@ -1,9 +0,0 @@ -/// This exception is thrown if the selected canteen is closed on the selected day. -class ClosedCanteenException implements Exception { - /// The message of the exception. - String message; - - /// This constructor creates a new ClosedCanteenException with the given message. - /// @param message The message of the exception. - ClosedCanteenException(this.message); -} \ No newline at end of file diff --git a/app/lib/view_model/repository/error_handling/FilteredMealException.dart b/app/lib/view_model/repository/error_handling/FilteredMealException.dart deleted file mode 100644 index d6ee8ba6..00000000 --- a/app/lib/view_model/repository/error_handling/FilteredMealException.dart +++ /dev/null @@ -1,9 +0,0 @@ -/// This exception is thrown if no meal of the mealplan matches the filter preferences. -class FilteredMealException implements Exception { - /// The message of the exception. - String message; - - /// This constructor creates a new FilteredMealException with the given message. - /// @param message The message of the exception. - FilteredMealException(this.message); -} \ No newline at end of file diff --git a/app/lib/view_model/repository/error_handling/MealPlanException.dart b/app/lib/view_model/repository/error_handling/MealPlanException.dart new file mode 100644 index 00000000..4aec92a2 --- /dev/null +++ b/app/lib/view_model/repository/error_handling/MealPlanException.dart @@ -0,0 +1,48 @@ +/// This classes are exceptions that can occur by accessing or loading the meal plan. +/// This happens with functional programming. + +/// This class is the superclass of al meal plan exceptions +sealed class MealPlanException implements Exception { + /// This constructor creates a new object + const MealPlanException(); +} + +/// This exception is thrown if the requested data are not stored in the local database and the connection to the server can not be established. +class NoConnectionException extends MealPlanException { + /// The message of the exception. + String message; + + /// This constructor creates a new NoConnectionException with the given message. + /// @param message The message of the exception. + NoConnectionException(this.message); +} + +/// This exception is thrown if the selected canteen is closed on the selected day. +class ClosedCanteenException extends MealPlanException { + /// The message of the exception. + String message; + + /// This constructor creates a new ClosedCanteenException with the given message. + /// @param message The message of the exception. + ClosedCanteenException(this.message); +} + +/// This exception is thrown if no meal of the mealplan matches the filter preferences. +class FilteredMealException extends MealPlanException { + /// The message of the exception. + String message; + + /// This constructor creates a new FilteredMealException with the given message. + /// @param message The message of the exception. + FilteredMealException(this.message); +} + +/// This exception is thrown if the server does not have the requested data stored because the selected date is to far in the future or before the server started storing mealplans. +class NoDataException extends MealPlanException { + /// The message of the exception. + String message; + + /// This constructor creates a new NoDataException with the given message. + /// @param message The message of the exception. + NoDataException(this.message); +} \ No newline at end of file diff --git a/app/lib/view_model/repository/error_handling/NoConnectionException.dart b/app/lib/view_model/repository/error_handling/NoConnectionException.dart deleted file mode 100644 index 98a2e19f..00000000 --- a/app/lib/view_model/repository/error_handling/NoConnectionException.dart +++ /dev/null @@ -1,9 +0,0 @@ -/// This exception is thrown if the requested data are not stored in the local database and the connection to the server can not be established. -class NoConnectionException implements Exception { - /// The message of the exception. - String message; - - /// This constructor creates a new NoConnectionException with the given message. - /// @param message The message of the exception. - NoConnectionException(this.message); -} \ No newline at end of file diff --git a/app/lib/view_model/repository/error_handling/NoDataExeption.dart b/app/lib/view_model/repository/error_handling/NoDataExeption.dart deleted file mode 100644 index 15da670a..00000000 --- a/app/lib/view_model/repository/error_handling/NoDataExeption.dart +++ /dev/null @@ -1,9 +0,0 @@ -/// This exception is thrown if the server does not have the requested data stored because the selected date is to far in the future or before the server started storing mealplans. -class NoDataException implements Exception { - /// The message of the exception. - String message; - - /// This constructor creates a new NoDataException with the given message. - /// @param message The message of the exception. - NoDataException(this.message); -} \ No newline at end of file diff --git a/app/lib/view_model/repository/error_handling/Result.dart b/app/lib/view_model/repository/error_handling/Result.dart index 7e0ed16c..8e7577ac 100644 --- a/app/lib/view_model/repository/error_handling/Result.dart +++ b/app/lib/view_model/repository/error_handling/Result.dart @@ -2,13 +2,13 @@ /// This happens with functional programming. /// This class is the superclass of all results. -sealed class Result { +sealed class Result { /// This method maps the result to a new result. const Result(); } /// This class represents a success. -final class Success extends Result { +final class Success extends Result { /// The value of the success. /// @param value The value of the success. const Success(this.value); @@ -18,12 +18,12 @@ final class Success extends Result { } /// This class represents a failure. -final class Failure extends Result { +final class Failure extends Result { /// The value of the failure. /// @param value The value of the failure. - const Failure(this.value); + const Failure(this.exception); /// The value of the failure. - final Exception value; + final E exception; } diff --git a/app/lib/view_model/repository/interface/IDatabaseAccess.dart b/app/lib/view_model/repository/interface/IDatabaseAccess.dart index 049476c2..541a98b5 100644 --- a/app/lib/view_model/repository/interface/IDatabaseAccess.dart +++ b/app/lib/view_model/repository/interface/IDatabaseAccess.dart @@ -1,3 +1,5 @@ +import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; + import '../data_classes/meal/Meal.dart'; import '../data_classes/mealplan/Canteen.dart'; import '../data_classes/mealplan/MealPlan.dart'; @@ -14,12 +16,12 @@ abstract class IDatabaseAccess { /// @param date The date of the mealplan /// @param canteen The canteen of the mealplan /// @return The mealplan of the committed date of the committed canteen or an error - Future>> getMealPlan(DateTime date, Canteen canteen); + Future, MealPlanException>> getMealPlan(DateTime date, Canteen canteen); /// This method returns a favorite meal. /// @param id The id of the meal /// @return The favorite meal with the committed id or an error - Future> getMealFavorite(String id); + Future> getMealFavorite(String id); /// This method adds a favorite. If the favorite does already exists, it does nothing. /// @param meal The meal that should be added as favorite diff --git a/app/lib/view_model/repository/interface/IServerAccess.dart b/app/lib/view_model/repository/interface/IServerAccess.dart index 0c8bd6df..fcc60894 100644 --- a/app/lib/view_model/repository/interface/IServerAccess.dart +++ b/app/lib/view_model/repository/interface/IServerAccess.dart @@ -1,4 +1,5 @@ import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; import '../data_classes/meal/ImageData.dart'; import '../data_classes/meal/Meal.dart'; @@ -10,18 +11,18 @@ import '../error_handling/Result.dart'; abstract class IServerAccess { /// This method requests all mealplans of all canteens for the next seven days from the server. /// @return The result of the update or an error - Future>> updateAll(); + Future, MealPlanException>> updateAll(); /// This method requests the mealplan for the committed date of the committed canteen from the server. /// @param date The date of the mealplan /// @param canteen The canteen of the mealplan /// @return The mealplan of the committed date of the committed canteen or an error - Future>> updateCanteen(Canteen canteen, DateTime date); + Future, MealPlanException>> updateCanteen(Canteen canteen, DateTime date); /// This method returns the meal with the committed id. /// @param id The id of the meal /// @return The meal with the committed id or an error - Future> getMealFromId(String id); + Future> getMealFromId(String id); /// This method updates the rating of the committed meal on the server. /// @param rating The new rating of the meal @@ -60,4 +61,7 @@ abstract class IServerAccess { /// @param reportReason The reason why the image should be reported. /// @return The result of the update. It returns false, if the report failed. Future reportImage(ImageData image, ReportCategory reportReason); + + + } \ No newline at end of file From aeca607687f7f930675e7650bb6462d83b374952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 10 Jul 2023 11:20:51 +0200 Subject: [PATCH 026/184] changes in Interfaces --- app/lib/view_model/repository/interface/IDatabaseAccess.dart | 3 +++ app/lib/view_model/repository/interface/ILocalStorage.dart | 5 ++--- app/lib/view_model/repository/interface/IServerAccess.dart | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/lib/view_model/repository/interface/IDatabaseAccess.dart b/app/lib/view_model/repository/interface/IDatabaseAccess.dart index 541a98b5..6bd07275 100644 --- a/app/lib/view_model/repository/interface/IDatabaseAccess.dart +++ b/app/lib/view_model/repository/interface/IDatabaseAccess.dart @@ -36,4 +36,7 @@ abstract class IDatabaseAccess { /// This method returns all Favorites. /// @return all Favorites Future> getFavorites(); + + /// This method returns the canteen with the committed id + Future getCanteenById(String id); } \ No newline at end of file diff --git a/app/lib/view_model/repository/interface/ILocalStorage.dart b/app/lib/view_model/repository/interface/ILocalStorage.dart index 9071c27c..c7cf3bea 100644 --- a/app/lib/view_model/repository/interface/ILocalStorage.dart +++ b/app/lib/view_model/repository/interface/ILocalStorage.dart @@ -1,6 +1,5 @@ import '../data_classes/filter/FilterPreferences.dart'; -import '../data_classes/mealplan/Canteen.dart'; import '../data_classes/settings/MensaColorScheme.dart'; import '../data_classes/settings/MealPlanFormat.dart'; import '../data_classes/settings/PriceCategory.dart'; @@ -27,12 +26,12 @@ abstract class ILocalStorage { /// The saved Canteen is returned. /// @return The saved Canteen. - Future getCanteen(); + Future getCanteen(); /// The committed Canteen is set. /// @param canteen The new Canteen. /// @return The result of the update. - Future setCanteen(Canteen canteen); + Future setCanteen(String canteen); /// The saved ColorScheme is returned. /// @return The saved ColorScheme. diff --git a/app/lib/view_model/repository/interface/IServerAccess.dart b/app/lib/view_model/repository/interface/IServerAccess.dart index fcc60894..940580ea 100644 --- a/app/lib/view_model/repository/interface/IServerAccess.dart +++ b/app/lib/view_model/repository/interface/IServerAccess.dart @@ -62,6 +62,7 @@ abstract class IServerAccess { /// @return The result of the update. It returns false, if the report failed. Future reportImage(ImageData image, ReportCategory reportReason); - - + /// This method requests the default canteen from the server. + /// @return The default canteen or null if no connection could be established. + Future getDefaultCanteen(); } \ No newline at end of file From 4dca881d3db39351460b86ffc337bbdc77fb6b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 10 Jul 2023 11:32:33 +0200 Subject: [PATCH 027/184] spelling --- app/lib/view_model/repository/interface/IDatabaseAccess.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/view_model/repository/interface/IDatabaseAccess.dart b/app/lib/view_model/repository/interface/IDatabaseAccess.dart index 6bd07275..36cc04c7 100644 --- a/app/lib/view_model/repository/interface/IDatabaseAccess.dart +++ b/app/lib/view_model/repository/interface/IDatabaseAccess.dart @@ -37,6 +37,6 @@ abstract class IDatabaseAccess { /// @return all Favorites Future> getFavorites(); - /// This method returns the canteen with the committed id + /// This method returns the canteen with the committed id. Future getCanteenById(String id); } \ No newline at end of file From e363a93fcbd037217d45442a1dcd04b5e5abb42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 10 Jul 2023 16:06:19 +0200 Subject: [PATCH 028/184] change ColorScheme to MensaColorScheme --- .../local_storage/SharedPreferenceAccess.dart | 8 ++++---- .../logic/preference/IPreferenceAccess.dart | 4 ++-- .../data_classes/settings/MensaColorScheme.dart | 2 +- .../repository/interface/ILocalStorage.dart | 6 +++--- app/pubspec.lock | 16 ++++++++-------- app/test/model/SharedPreferencesTest.dart | 6 +++--- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index 7e0dc284..ac81c7ab 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -2,7 +2,7 @@ import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; -import 'package:app/view_model/repository/data_classes/settings/ColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:app/view_model/repository/interface/ILocalStorage.dart'; @@ -28,9 +28,9 @@ class SharedPreferenceAccess implements ILocalStorage { } @override - Future getColorScheme() async { + Future getColorScheme() async { final colorScheme = _pref.getString('colorScheme'); - return Future.value(ColorScheme.values.firstWhere((e) => e.toString() == colorScheme)); + return Future.value(MensaColorScheme.values.firstWhere((e) => e.toString() == colorScheme)); } @override @@ -97,7 +97,7 @@ class SharedPreferenceAccess implements ILocalStorage { } @override - Future setColorScheme(ColorScheme scheme) async { + Future setColorScheme(MensaColorScheme scheme) async { await _pref.setString('colorScheme', scheme.toString()); } diff --git a/app/lib/view_model/logic/preference/IPreferenceAccess.dart b/app/lib/view_model/logic/preference/IPreferenceAccess.dart index e6e1d324..5dc96ab6 100644 --- a/app/lib/view_model/logic/preference/IPreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/IPreferenceAccess.dart @@ -40,12 +40,12 @@ abstract class IPreferenceAccess { /// The saved ColorScheme is returned. /// @return The saved ColorScheme. - Future getColorScheme(); + Future getColorScheme(); /// The committed ColorScheme is set. /// @param scheme The new ColorScheme. /// @return The result of the update. - Future setColorScheme(ColorScheme scheme); + Future setColorScheme(MensaColorScheme scheme); /// The saved PriceCategory is returned. /// @return The saved PriceCategory. diff --git a/app/lib/view_model/repository/data_classes/settings/MensaColorScheme.dart b/app/lib/view_model/repository/data_classes/settings/MensaColorScheme.dart index b86f7b62..e0c98921 100644 --- a/app/lib/view_model/repository/data_classes/settings/MensaColorScheme.dart +++ b/app/lib/view_model/repository/data_classes/settings/MensaColorScheme.dart @@ -1,6 +1,6 @@ /// This enums contains all different color schemes. -enum ColorScheme { +enum MensaColorScheme { /// The light color scheme. light, /// The dark color scheme. diff --git a/app/lib/view_model/repository/interface/ILocalStorage.dart b/app/lib/view_model/repository/interface/ILocalStorage.dart index b7d09d6e..b63358ea 100644 --- a/app/lib/view_model/repository/interface/ILocalStorage.dart +++ b/app/lib/view_model/repository/interface/ILocalStorage.dart @@ -1,6 +1,6 @@ import '../data_classes/filter/FilterPreferences.dart'; -import '../data_classes/settings/ColorScheme.dart'; +import '../data_classes/settings/MensaColorScheme.dart'; import '../data_classes/settings/MealPlanFormat.dart'; import '../data_classes/settings/PriceCategory.dart'; @@ -35,12 +35,12 @@ abstract class ILocalStorage { /// The saved ColorScheme is returned. /// @return The saved ColorScheme. - Future getColorScheme(); + Future getColorScheme(); /// The committed ColorScheme is set. /// @param scheme The new ColorScheme. /// @return The result of the update. - Future setColorScheme(ColorScheme scheme); + Future setColorScheme(MensaColorScheme scheme); /// The saved PriceCategory is returned. /// @return The saved PriceCategory. diff --git a/app/pubspec.lock b/app/pubspec.lock index 58363d1b..4df52b69 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -98,10 +98,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_localizations: dependency: "direct main" description: flutter @@ -161,10 +161,10 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.1" logging: dependency: transitive description: @@ -329,10 +329,10 @@ packages: dependency: transitive description: name: shared_preferences_foundation - sha256: "0dc5c49ad8a05ed358b991b60c7b0ba1a14e16dae58af9b420d6b9e82dc024ab" + sha256: b046999bf0ff58f04c364491bb803dcfa8f42e47b19c75478f53d323684a8cc1 url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" shared_preferences_linux: dependency: transitive description: @@ -470,10 +470,10 @@ packages: dependency: transitive description: name: win32 - sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee url: "https://pub.dev" source: hosted - version: "4.1.4" + version: "5.0.5" xdg_directories: dependency: transitive description: diff --git a/app/test/model/SharedPreferencesTest.dart b/app/test/model/SharedPreferencesTest.dart index 9a0d4b44..580cf07f 100644 --- a/app/test/model/SharedPreferencesTest.dart +++ b/app/test/model/SharedPreferencesTest.dart @@ -6,7 +6,7 @@ import 'package:app/view_model/repository/data_classes/filter/Sorting.dart'; import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; -import 'package:app/view_model/repository/data_classes/settings/ColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -47,7 +47,7 @@ Future main() async { /// This method tests the access to the color scheme. test('Color Scheme Test', () async { - ColorScheme scheme = ColorScheme.light; + MensaColorScheme scheme = MensaColorScheme.light; pref.setColorScheme(scheme); expect(await pref.getColorScheme(), scheme); }); @@ -69,7 +69,7 @@ Future main() async { /// This method prepares the access to the filter preferences. setUp(() async { pref.setFilterPreferences(filter); - filterResult = await pref.getFilterPreferences(); + filterResult = await pref.getFilterPreferences() ?? FilterPreferences(); }); /// This group tests the access to the filter preferences From b985c3241b9bce6e2998c8141fcac009d516ef11 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Tue, 11 Jul 2023 12:07:05 +0200 Subject: [PATCH 029/184] Implemented MealAccordion --- app/lib/view/detail_view/MealAccordion.dart | 56 +++++++++++++++++++ .../view/detail_view/MealAccordionInfo.dart | 44 +++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 app/lib/view/detail_view/MealAccordion.dart create mode 100644 app/lib/view/detail_view/MealAccordionInfo.dart diff --git a/app/lib/view/detail_view/MealAccordion.dart b/app/lib/view/detail_view/MealAccordion.dart new file mode 100644 index 00000000..2b48e1ac --- /dev/null +++ b/app/lib/view/detail_view/MealAccordion.dart @@ -0,0 +1,56 @@ +import 'package:app/view/core/information_display/MealMainEntry.dart'; +import 'package:app/view/core/information_display/MealSideEntry.dart'; +import 'package:app/view/detail_view/MealAccordionInfo.dart'; +import 'package:flutter/material.dart'; + +class MealAccordion extends StatelessWidget { + final bool _isExpanded; + final MealMainEntry? _mainEntry; + final MealSideEntry? _sideEntry; + final MealAccordionInfo _info; + final Function()? _onTap; + + const MealAccordion( + {super.key, + required bool isExpanded, + MealMainEntry? mainEntry, + MealSideEntry? sideEntry, + required MealAccordionInfo info, + Function()? onTap}) + : _isExpanded = isExpanded, + _mainEntry = mainEntry, + _sideEntry = sideEntry, + _info = info, + _onTap = onTap; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.0), + color: _isExpanded + ? Theme.of(context).colorScheme.surface + : Theme.of(context).colorScheme.background, + ), + child: Material( + color: Colors.transparent, + borderRadius: BorderRadius.circular(4.0), + child: InkWell( + borderRadius: BorderRadius.circular(4.0), + onTap: _onTap, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 0, vertical: 8), + child: Column( + children: [ + _mainEntry ?? _sideEntry ?? Container(), + _isExpanded + ? Padding( + padding: const EdgeInsets.only(left: 40), + child: _info) + : Container(), + ], + ))), + )); + } +} diff --git a/app/lib/view/detail_view/MealAccordionInfo.dart b/app/lib/view/detail_view/MealAccordionInfo.dart new file mode 100644 index 00000000..64278470 --- /dev/null +++ b/app/lib/view/detail_view/MealAccordionInfo.dart @@ -0,0 +1,44 @@ +import 'package:app/view_model/repository/data_classes/meal/Additive.dart'; +import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; +import 'package:flutter/cupertino.dart'; + +class MealAccordionInfo extends StatelessWidget { + final List _allergens; + final List _additives; + + const MealAccordionInfo( + {super.key, + required List allergens, + required List additives}) + : _allergens = allergens, + _additives = additives; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 8), + const Text( + "Allergene:", + ), + ..._allergens.map((e) => Row( + children: [ + const Text("• "), + Expanded(child: Text(e.name)), + ], + )), + SizedBox(height: 8), + const Text( + "Zusatzstoffe:", + ), + ..._additives.map((e) => Row( + children: [ + const Text("• "), + Expanded(child: Text(e.name)), + ], + )), + ], + ); + } +} From 585232ba797535f2010d4a6d6a03dc991ee7051f Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Tue, 11 Jul 2023 12:10:15 +0200 Subject: [PATCH 030/184] Implemented MealAccordion --- app/lib/view/detail_view/MealAccordion.dart | 9 +++++++++ app/lib/view/detail_view/MealAccordionInfo.dart | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/app/lib/view/detail_view/MealAccordion.dart b/app/lib/view/detail_view/MealAccordion.dart index 2b48e1ac..c0c5672e 100644 --- a/app/lib/view/detail_view/MealAccordion.dart +++ b/app/lib/view/detail_view/MealAccordion.dart @@ -3,6 +3,7 @@ import 'package:app/view/core/information_display/MealSideEntry.dart'; import 'package:app/view/detail_view/MealAccordionInfo.dart'; import 'package:flutter/material.dart'; +/// This class is used to display a meal in an accordion. class MealAccordion extends StatelessWidget { final bool _isExpanded; final MealMainEntry? _mainEntry; @@ -10,6 +11,14 @@ class MealAccordion extends StatelessWidget { final MealAccordionInfo _info; final Function()? _onTap; + /// Creates a new MealAccordion. + /// @param key The key to identify this widget. + /// @param isExpanded Whether the accordion is expanded. + /// @param mainEntry The main entry to display. + /// @param sideEntry The side entry to display. + /// @param info The MealAccordionInfo to display in the expanded MealAccordion. + /// @param onTap The function that is called when the MealAccordion is tapped. + /// @returns A new MealAccordion. const MealAccordion( {super.key, required bool isExpanded, diff --git a/app/lib/view/detail_view/MealAccordionInfo.dart b/app/lib/view/detail_view/MealAccordionInfo.dart index 64278470..6856add4 100644 --- a/app/lib/view/detail_view/MealAccordionInfo.dart +++ b/app/lib/view/detail_view/MealAccordionInfo.dart @@ -2,10 +2,16 @@ import 'package:app/view_model/repository/data_classes/meal/Additive.dart'; import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; import 'package:flutter/cupertino.dart'; +/// This class is used to display the allergens and additives of a meal. class MealAccordionInfo extends StatelessWidget { final List _allergens; final List _additives; + /// Creates a MealAccordionInfo widget. + /// @param key The key to identify this widget. + /// @param allergens The allergens of the meal. + /// @param additives The additives of the meal. + /// @returns A new MealAccordionInfo widget. const MealAccordionInfo( {super.key, required List allergens, From 392a48a92e62cf76e19ad96dd9a8aeeac1c929aa Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Tue, 11 Jul 2023 16:56:51 +0200 Subject: [PATCH 031/184] more mutation tests --- .../api_server/GraphQlServerAccess_test.dart | 70 ++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index ccdc4d87..bb147c7f 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -1,5 +1,9 @@ import 'package:app/model/api_server/GraphQlServerAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/meal/Price.dart'; +import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; import 'package:flutter_test/flutter_test.dart'; void main() async { @@ -12,7 +16,7 @@ void main() async { "define secret file with `--dart-define-from-file=`, see README"); }); - test('graphql', () async { + test('remove downvote', () async { var deleted = await serverAccess.deleteDownvote(ImageData( id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", url: "url", @@ -21,4 +25,68 @@ void main() async { negativeRating: 0)); expect(deleted, true); }); + + test('remove upvote', () async { + var deleted = await serverAccess.deleteUpvote(ImageData( + id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", + url: "url", + imageRank: 0, + positiveRating: 0, + negativeRating: 0)); + expect(deleted, true); + }); + + test('add downvote', () async { + var deleted = await serverAccess.downvoteImage(ImageData( + id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", + url: "url", + imageRank: 0, + positiveRating: 0, + negativeRating: 0)); + expect(deleted, true); + }); + + test('add upvote', () async { + var deleted = await serverAccess.upvoteImage(ImageData( + id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", + url: "url", + imageRank: 0, + positiveRating: 0, + negativeRating: 0)); + expect(deleted, true); + }); + + test('link image', () async { + var deleted = await serverAccess.linkImage( + "https://image_url.de", + Meal( + id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", + name: "Best meal", + foodType: FoodType.vegetarian, + price: Price(student: 1, employee: 23, pupil: 5, guest: 15))); + expect(deleted, true); + }); + + test('report image', () async { + var deleted = await serverAccess.reportImage( + ImageData( + id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", + url: "http://image.de", + imageRank: 0.89, + positiveRating: 1, + negativeRating: 22), + ReportCategory.wrongMeal); + expect(deleted, true); + }); + + test('rate meal', () async { + var deleted = await serverAccess.updateMealRating( + 3, + Meal( + id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", + name: "Best meal", + foodType: FoodType.fish, + price: Price(student: 22, employee: 33, pupil: 11, guest: 123))); + expect(deleted, true); + }); } From 41e02a3927172fd518835c54ffbdd4696e7279b7 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Tue, 11 Jul 2023 18:13:12 +0200 Subject: [PATCH 032/184] rest of querys --- .../model/api_server/GraphQlServerAccess.dart | 111 +- .../model/api_server/requests/querys.graphql | 32 +- .../api_server/requests/querys.graphql.dart | 4191 +++++++++++------ .../repository/interface/IServerAccess.dart | 4 +- .../api_server/GraphQlServerAccess_test.dart | 22 +- 5 files changed, 2772 insertions(+), 1588 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index 76529b6c..4bd2567c 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -27,11 +27,10 @@ class GraphQlServerAccess implements IServerAccess { link: HttpLink(const String.fromEnvironment('API_URL')), cache: GraphQLCache()); final String _clientId; + final _dateFormat = DateFormat(dateFormatPattern); GraphQlServerAccess._(this._clientId); - - factory GraphQlServerAccess(String clientId) { return GraphQlServerAccess._(clientId); } @@ -82,7 +81,7 @@ class GraphQlServerAccess implements IServerAccess { // TODO: auth final result = await _client.mutate$LinkImage(Options$Mutation$LinkImage( variables: - Variables$Mutation$LinkImage(imageUrl: url, mealId: meal.id))); + Variables$Mutation$LinkImage(imageUrl: url, mealId: meal.id))); final parsedData = result.parsedData; return parsedData?.addImage ?? false; } @@ -111,64 +110,94 @@ class GraphQlServerAccess implements IServerAccess { } // ---------------------- queries ---------------------- - static const DAYS_TO_PARSE = 7; + static const daysToParse = 7; + static const dateFormatPattern = "yyyy-MM-dd"; + @override Future>> updateAll() async { - final dateFormat = DateFormat("Y-m-d"); // TODO correct? - final date = DateTime.now(); // TODO for next 7 days - + final today = DateTime.now(); var completeList = []; - for (int offset = 0; offset < 7; offset++) { - final offsetDate = date.add(Duration(days: offset)); + // TODO parallel? + for (int offset = 0; offset < daysToParse; offset++) { + final date = today.add(Duration(days: offset)); final result = await _client.query$GetMealPlanForDay( Options$Query$GetMealPlanForDay( variables: Variables$Query$GetMealPlanForDay( - date: dateFormat.format(offsetDate)))); + date: _dateFormat.format(date)))); final parsedData = result.parsedData; - - final mealPlan = parsedData?.getCanteens.expand((e) => - e.lines.asMap().map((idx, e) => - MapEntry(idx, - Mealplan(date: offsetDate, - line: Line(id: e.id, - name: e.name, - canteen: _convertCanteen(e.canteen), - position: idx), - isClosed: false, // TODO what to do when no data available - meals: _convertMeals(e.meals ?? [])), - ), - ).values.toList(), - ).toList() ?? []; + final mealPlan = _convertMealPlan(parsedData?.getCanteens ?? [], date); completeList.addAll(mealPlan); } - return Success(completeList); + return Success(completeList); // TODO when return error? } @override - Future> getMealFromId(String id) async { - // TODO: implement getMealFromId - throw UnimplementedError(); + Future> getMealFromId( + Meal meal, Line line, DateTime date) async { + final result = await _client.query$GetMeal(Options$Query$GetMeal( + variables: Variables$Query$GetMeal( + date: _dateFormat.format(date), mealId: meal.id, lineId: line.id))); + final meal_data = result.parsedData?.getMeal; + + if (meal_data == null) { + return Failure(Exception("")); + } + return Success(_convertMeal(meal_data)); } @override - Future>> updateCanteen(Canteen canteen, - DateTime date) async { - // TODO: implement updateCanteen - throw UnimplementedError(); + Future>> updateCanteen( + Canteen canteen, DateTime date) async { + final result = await _client.query$GetCanteenDate( + Options$Query$GetCanteenDate( + variables: Variables$Query$GetCanteenDate( + canteenId: canteen.id, date: _dateFormat.format(date)))); + final parsedData = result.parsedData; + + final mealPlan = + _convertMealPlan([parsedData?.getCanteen].nonNulls.toList(), date); + return Success(mealPlan); // TODO when error? } } +// --------------- utility helper methods --------------- + +List _convertMealPlan( + List mealPlan, DateTime date) { + return mealPlan + .expand( + (e) => e.lines + .asMap() + .map((idx, e) => MapEntry( + idx, + Mealplan( + date: date, + line: Line( + id: e.id, + name: e.name, + canteen: _convertCanteen(e.canteen), + position: idx), + // mensa closed when data available but no meals in list + isClosed: e.meals?.isEmpty ?? false, + meals: e.meals?.map((e) => _convertMeal(e)).toList() ?? [], + ), + )) + .values + .toList(), + ) + .toList(); +} -List _convertMeals(List meals) { - return meals.map((e) => - Meal(id: e.id, - name: e.name, - foodType: _convertMealType(e.mealType), - price: _convertPrice(e.price))).toList(); +Meal _convertMeal(Fragment$mealInfo meal) { + return Meal( + id: meal.id, + name: meal.name, + foodType: _convertMealType(meal.mealType), + price: _convertPrice(meal.price)); } FoodType _convertMealType(Enum$MealType mealType) { @@ -195,14 +224,14 @@ FoodType _convertMealType(Enum$MealType mealType) { } Price _convertPrice(Fragment$mealInfo$price price) { - return Price(student: price.student, + return Price( + student: price.student, employee: price.employee, pupil: price.pupil, guest: price.guest); } -Canteen _convertCanteen( - Query$GetMealPlanForDay$getCanteens$lines$canteen canteen) { +Canteen _convertCanteen(Fragment$mealPlan$lines$canteen canteen) { return Canteen(id: canteen.id, name: canteen.name); } diff --git a/app/lib/model/api_server/requests/querys.graphql b/app/lib/model/api_server/requests/querys.graphql index 2066e00a..54ebd97a 100644 --- a/app/lib/model/api_server/requests/querys.graphql +++ b/app/lib/model/api_server/requests/querys.graphql @@ -1,15 +1,31 @@ query GetMealPlanForDay($date: NaiveDate!) { getCanteens { - lines { + ...mealPlan + } +} + +query GetCanteenDate($canteenId: UUID!, $date: NaiveDate!) { + getCanteen(canteenId: $canteenId) { + ...mealPlan + } +} + +query GetMeal($date: NaiveDate!, $mealId: UUID!, $lineId: UUID!) { + getMeal(date: $date, mealId: $mealId, lineId: $lineId) { + ...mealInfo + } +} + +fragment mealPlan on Canteen { + lines { + id + name + canteen { id name - canteen { - id - name - } - meals(date: $date) { - ...mealInfo - } + } + meals(date: $date) { + ...mealInfo } } } diff --git a/app/lib/model/api_server/requests/querys.graphql.dart b/app/lib/model/api_server/requests/querys.graphql.dart index 9ff54cdc..4d2abde3 100644 --- a/app/lib/model/api_server/requests/querys.graphql.dart +++ b/app/lib/model/api_server/requests/querys.graphql.dart @@ -5,97 +5,127 @@ import 'package:graphql/client.dart' as graphql; import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; import 'schema.graphql.dart'; -class Fragment$mealInfo { - Fragment$mealInfo({ - required this.id, - required this.name, - required this.mealType, - required this.price, - required this.allergens, - required this.additives, - required this.statistics, - required this.ratings, - required this.images, - this.$__typename = 'Meal', - }); +class Variables$Fragment$mealPlan { + factory Variables$Fragment$mealPlan({required String date}) => + Variables$Fragment$mealPlan._({ + r'date': date, + }); - factory Fragment$mealInfo.fromJson(Map json) { - final l$id = json['id']; - final l$name = json['name']; - final l$mealType = json['mealType']; - final l$price = json['price']; - final l$allergens = json['allergens']; - final l$additives = json['additives']; - final l$statistics = json['statistics']; - final l$ratings = json['ratings']; - final l$images = json['images']; - final l$$__typename = json['__typename']; - return Fragment$mealInfo( - id: (l$id as String), - name: (l$name as String), - mealType: fromJson$Enum$MealType((l$mealType as String)), - price: - Fragment$mealInfo$price.fromJson((l$price as Map)), - allergens: (l$allergens as List) - .map((e) => fromJson$Enum$Allergen((e as String))) - .toList(), - additives: (l$additives as List) - .map((e) => fromJson$Enum$Additive((e as String))) - .toList(), - statistics: Fragment$mealInfo$statistics.fromJson( - (l$statistics as Map)), - ratings: Fragment$mealInfo$ratings.fromJson( - (l$ratings as Map)), - images: (l$images as List) - .map((e) => - Fragment$mealInfo$images.fromJson((e as Map))) - .toList(), - $__typename: (l$$__typename as String), - ); + Variables$Fragment$mealPlan._(this._$data); + + factory Variables$Fragment$mealPlan.fromJson(Map data) { + final result$data = {}; + final l$date = data['date']; + result$data['date'] = (l$date as String); + return Variables$Fragment$mealPlan._(result$data); } - final String id; + Map _$data; - final String name; + String get date => (_$data['date'] as String); + Map toJson() { + final result$data = {}; + final l$date = date; + result$data['date'] = l$date; + return result$data; + } - final Enum$MealType mealType; + CopyWith$Variables$Fragment$mealPlan + get copyWith => CopyWith$Variables$Fragment$mealPlan( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Fragment$mealPlan) || + runtimeType != other.runtimeType) { + return false; + } + final l$date = date; + final lOther$date = other.date; + if (l$date != lOther$date) { + return false; + } + return true; + } - final Fragment$mealInfo$price price; + @override + int get hashCode { + final l$date = date; + return Object.hashAll([l$date]); + } +} - final List allergens; +abstract class CopyWith$Variables$Fragment$mealPlan { + factory CopyWith$Variables$Fragment$mealPlan( + Variables$Fragment$mealPlan instance, + TRes Function(Variables$Fragment$mealPlan) then, + ) = _CopyWithImpl$Variables$Fragment$mealPlan; - final List additives; + factory CopyWith$Variables$Fragment$mealPlan.stub(TRes res) = + _CopyWithStubImpl$Variables$Fragment$mealPlan; - final Fragment$mealInfo$statistics statistics; + TRes call({String? date}); +} - final Fragment$mealInfo$ratings ratings; +class _CopyWithImpl$Variables$Fragment$mealPlan + implements CopyWith$Variables$Fragment$mealPlan { + _CopyWithImpl$Variables$Fragment$mealPlan( + this._instance, + this._then, + ); - final List images; + final Variables$Fragment$mealPlan _instance; + + final TRes Function(Variables$Fragment$mealPlan) _then; + + static const _undefined = {}; + + TRes call({Object? date = _undefined}) => + _then(Variables$Fragment$mealPlan._({ + ..._instance._$data, + if (date != _undefined && date != null) 'date': (date as String), + })); +} + +class _CopyWithStubImpl$Variables$Fragment$mealPlan + implements CopyWith$Variables$Fragment$mealPlan { + _CopyWithStubImpl$Variables$Fragment$mealPlan(this._res); + + TRes _res; + + call({String? date}) => _res; +} + +class Fragment$mealPlan { + Fragment$mealPlan({ + required this.lines, + this.$__typename = 'Canteen', + }); + + factory Fragment$mealPlan.fromJson(Map json) { + final l$lines = json['lines']; + final l$$__typename = json['__typename']; + return Fragment$mealPlan( + lines: (l$lines as List) + .map((e) => + Fragment$mealPlan$lines.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final List lines; final String $__typename; Map toJson() { final _resultData = {}; - final l$id = id; - _resultData['id'] = l$id; - final l$name = name; - _resultData['name'] = l$name; - final l$mealType = mealType; - _resultData['mealType'] = toJson$Enum$MealType(l$mealType); - final l$price = price; - _resultData['price'] = l$price.toJson(); - final l$allergens = allergens; - _resultData['allergens'] = - l$allergens.map((e) => toJson$Enum$Allergen(e)).toList(); - final l$additives = additives; - _resultData['additives'] = - l$additives.map((e) => toJson$Enum$Additive(e)).toList(); - final l$statistics = statistics; - _resultData['statistics'] = l$statistics.toJson(); - final l$ratings = ratings; - _resultData['ratings'] = l$ratings.toJson(); - final l$images = images; - _resultData['images'] = l$images.map((e) => e.toJson()).toList(); + final l$lines = lines; + _resultData['lines'] = l$lines.map((e) => e.toJson()).toList(); final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -103,26 +133,10 @@ class Fragment$mealInfo { @override int get hashCode { - final l$id = id; - final l$name = name; - final l$mealType = mealType; - final l$price = price; - final l$allergens = allergens; - final l$additives = additives; - final l$statistics = statistics; - final l$ratings = ratings; - final l$images = images; + final l$lines = lines; final l$$__typename = $__typename; return Object.hashAll([ - l$id, - l$name, - l$mealType, - l$price, - Object.hashAll(l$allergens.map((v) => v)), - Object.hashAll(l$additives.map((v) => v)), - l$statistics, - l$ratings, - Object.hashAll(l$images.map((v) => v)), + Object.hashAll(l$lines.map((v) => v)), l$$__typename, ]); } @@ -132,72 +146,18 @@ class Fragment$mealInfo { if (identical(this, other)) { return true; } - if (!(other is Fragment$mealInfo) || runtimeType != other.runtimeType) { - return false; - } - final l$id = id; - final lOther$id = other.id; - if (l$id != lOther$id) { - return false; - } - final l$name = name; - final lOther$name = other.name; - if (l$name != lOther$name) { - return false; - } - final l$mealType = mealType; - final lOther$mealType = other.mealType; - if (l$mealType != lOther$mealType) { - return false; - } - final l$price = price; - final lOther$price = other.price; - if (l$price != lOther$price) { - return false; - } - final l$allergens = allergens; - final lOther$allergens = other.allergens; - if (l$allergens.length != lOther$allergens.length) { - return false; - } - for (int i = 0; i < l$allergens.length; i++) { - final l$allergens$entry = l$allergens[i]; - final lOther$allergens$entry = lOther$allergens[i]; - if (l$allergens$entry != lOther$allergens$entry) { - return false; - } - } - final l$additives = additives; - final lOther$additives = other.additives; - if (l$additives.length != lOther$additives.length) { - return false; - } - for (int i = 0; i < l$additives.length; i++) { - final l$additives$entry = l$additives[i]; - final lOther$additives$entry = lOther$additives[i]; - if (l$additives$entry != lOther$additives$entry) { - return false; - } - } - final l$statistics = statistics; - final lOther$statistics = other.statistics; - if (l$statistics != lOther$statistics) { - return false; - } - final l$ratings = ratings; - final lOther$ratings = other.ratings; - if (l$ratings != lOther$ratings) { + if (!(other is Fragment$mealPlan) || runtimeType != other.runtimeType) { return false; } - final l$images = images; - final lOther$images = other.images; - if (l$images.length != lOther$images.length) { + final l$lines = lines; + final lOther$lines = other.lines; + if (l$lines.length != lOther$lines.length) { return false; } - for (int i = 0; i < l$images.length; i++) { - final l$images$entry = l$images[i]; - final lOther$images$entry = lOther$images[i]; - if (l$images$entry != lOther$images$entry) { + for (int i = 0; i < l$lines.length; i++) { + final l$lines$entry = l$lines[i]; + final lOther$lines$entry = lOther$lines[i]; + if (l$lines$entry != lOther$lines$entry) { return false; } } @@ -210,222 +170,168 @@ class Fragment$mealInfo { } } -extension UtilityExtension$Fragment$mealInfo on Fragment$mealInfo { - CopyWith$Fragment$mealInfo get copyWith => - CopyWith$Fragment$mealInfo( +extension UtilityExtension$Fragment$mealPlan on Fragment$mealPlan { + CopyWith$Fragment$mealPlan get copyWith => + CopyWith$Fragment$mealPlan( this, (i) => i, ); } -abstract class CopyWith$Fragment$mealInfo { - factory CopyWith$Fragment$mealInfo( - Fragment$mealInfo instance, - TRes Function(Fragment$mealInfo) then, - ) = _CopyWithImpl$Fragment$mealInfo; +abstract class CopyWith$Fragment$mealPlan { + factory CopyWith$Fragment$mealPlan( + Fragment$mealPlan instance, + TRes Function(Fragment$mealPlan) then, + ) = _CopyWithImpl$Fragment$mealPlan; - factory CopyWith$Fragment$mealInfo.stub(TRes res) = - _CopyWithStubImpl$Fragment$mealInfo; + factory CopyWith$Fragment$mealPlan.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealPlan; TRes call({ - String? id, - String? name, - Enum$MealType? mealType, - Fragment$mealInfo$price? price, - List? allergens, - List? additives, - Fragment$mealInfo$statistics? statistics, - Fragment$mealInfo$ratings? ratings, - List? images, + List? lines, String? $__typename, }); - CopyWith$Fragment$mealInfo$price get price; - CopyWith$Fragment$mealInfo$statistics get statistics; - CopyWith$Fragment$mealInfo$ratings get ratings; - TRes images( - Iterable Function( + TRes lines( + Iterable Function( Iterable< - CopyWith$Fragment$mealInfo$images>) + CopyWith$Fragment$mealPlan$lines>) _fn); } -class _CopyWithImpl$Fragment$mealInfo - implements CopyWith$Fragment$mealInfo { - _CopyWithImpl$Fragment$mealInfo( +class _CopyWithImpl$Fragment$mealPlan + implements CopyWith$Fragment$mealPlan { + _CopyWithImpl$Fragment$mealPlan( this._instance, this._then, ); - final Fragment$mealInfo _instance; + final Fragment$mealPlan _instance; - final TRes Function(Fragment$mealInfo) _then; + final TRes Function(Fragment$mealPlan) _then; static const _undefined = {}; TRes call({ - Object? id = _undefined, - Object? name = _undefined, - Object? mealType = _undefined, - Object? price = _undefined, - Object? allergens = _undefined, - Object? additives = _undefined, - Object? statistics = _undefined, - Object? ratings = _undefined, - Object? images = _undefined, + Object? lines = _undefined, Object? $__typename = _undefined, }) => - _then(Fragment$mealInfo( - id: id == _undefined || id == null ? _instance.id : (id as String), - name: name == _undefined || name == null - ? _instance.name - : (name as String), - mealType: mealType == _undefined || mealType == null - ? _instance.mealType - : (mealType as Enum$MealType), - price: price == _undefined || price == null - ? _instance.price - : (price as Fragment$mealInfo$price), - allergens: allergens == _undefined || allergens == null - ? _instance.allergens - : (allergens as List), - additives: additives == _undefined || additives == null - ? _instance.additives - : (additives as List), - statistics: statistics == _undefined || statistics == null - ? _instance.statistics - : (statistics as Fragment$mealInfo$statistics), - ratings: ratings == _undefined || ratings == null - ? _instance.ratings - : (ratings as Fragment$mealInfo$ratings), - images: images == _undefined || images == null - ? _instance.images - : (images as List), + _then(Fragment$mealPlan( + lines: lines == _undefined || lines == null + ? _instance.lines + : (lines as List), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String), )); - CopyWith$Fragment$mealInfo$price get price { - final local$price = _instance.price; - return CopyWith$Fragment$mealInfo$price(local$price, (e) => call(price: e)); - } - - CopyWith$Fragment$mealInfo$statistics get statistics { - final local$statistics = _instance.statistics; - return CopyWith$Fragment$mealInfo$statistics( - local$statistics, (e) => call(statistics: e)); - } - - CopyWith$Fragment$mealInfo$ratings get ratings { - final local$ratings = _instance.ratings; - return CopyWith$Fragment$mealInfo$ratings( - local$ratings, (e) => call(ratings: e)); - } - - TRes images( - Iterable Function( + TRes lines( + Iterable Function( Iterable< - CopyWith$Fragment$mealInfo$images< - Fragment$mealInfo$images>>) + CopyWith$Fragment$mealPlan$lines< + Fragment$mealPlan$lines>>) _fn) => call( - images: - _fn(_instance.images.map((e) => CopyWith$Fragment$mealInfo$images( + lines: + _fn(_instance.lines.map((e) => CopyWith$Fragment$mealPlan$lines( e, (i) => i, ))).toList()); } -class _CopyWithStubImpl$Fragment$mealInfo - implements CopyWith$Fragment$mealInfo { - _CopyWithStubImpl$Fragment$mealInfo(this._res); +class _CopyWithStubImpl$Fragment$mealPlan + implements CopyWith$Fragment$mealPlan { + _CopyWithStubImpl$Fragment$mealPlan(this._res); TRes _res; call({ - String? id, - String? name, - Enum$MealType? mealType, - Fragment$mealInfo$price? price, - List? allergens, - List? additives, - Fragment$mealInfo$statistics? statistics, - Fragment$mealInfo$ratings? ratings, - List? images, + List? lines, String? $__typename, }) => _res; - CopyWith$Fragment$mealInfo$price get price => - CopyWith$Fragment$mealInfo$price.stub(_res); - CopyWith$Fragment$mealInfo$statistics get statistics => - CopyWith$Fragment$mealInfo$statistics.stub(_res); - CopyWith$Fragment$mealInfo$ratings get ratings => - CopyWith$Fragment$mealInfo$ratings.stub(_res); - images(_fn) => _res; + lines(_fn) => _res; } -const fragmentDefinitionmealInfo = FragmentDefinitionNode( - name: NameNode(value: 'mealInfo'), +const fragmentDefinitionmealPlan = FragmentDefinitionNode( + name: NameNode(value: 'mealPlan'), typeCondition: TypeConditionNode( on: NamedTypeNode( - name: NameNode(value: 'Meal'), + name: NameNode(value: 'Canteen'), isNonNull: false, )), directives: [], selectionSet: SelectionSetNode(selections: [ FieldNode( - name: NameNode(value: 'id'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'name'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'mealType'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'price'), + name: NameNode(value: 'lines'), alias: null, arguments: [], directives: [], selectionSet: SelectionSetNode(selections: [ FieldNode( - name: NameNode(value: 'employee'), + name: NameNode(value: 'id'), alias: null, arguments: [], directives: [], selectionSet: null, ), FieldNode( - name: NameNode(value: 'guest'), + name: NameNode(value: 'name'), alias: null, arguments: [], directives: [], selectionSet: null, ), FieldNode( - name: NameNode(value: 'pupil'), + name: NameNode(value: 'canteen'), alias: null, arguments: [], directives: [], - selectionSet: null, + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), ), FieldNode( - name: NameNode(value: 'student'), + name: NameNode(value: 'meals'), alias: null, - arguments: [], + arguments: [ + ArgumentNode( + name: NameNode(value: 'date'), + value: VariableNode(name: NameNode(value: 'date')), + ) + ], directives: [], - selectionSet: null, + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'mealInfo'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), ), FieldNode( name: NameNode(value: '__typename'), @@ -437,247 +343,105 @@ const fragmentDefinitionmealInfo = FragmentDefinitionNode( ]), ), FieldNode( - name: NameNode(value: 'allergens'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'additives'), + name: NameNode(value: '__typename'), alias: null, arguments: [], directives: [], selectionSet: null, ), - FieldNode( - name: NameNode(value: 'statistics'), - alias: null, - arguments: [], - directives: [], - selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'lastServed'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'nextServed'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'relativeFrequency'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: '__typename'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - ]), - ), - FieldNode( - name: NameNode(value: 'ratings'), - alias: null, - arguments: [], - directives: [], - selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'averageRating'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'personalRating'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'ratingsCount'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: '__typename'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - ]), - ), - FieldNode( - name: NameNode(value: 'images'), - alias: null, - arguments: [], - directives: [], - selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'id'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, + ]), +); +const documentNodeFragmentmealPlan = DocumentNode(definitions: [ + fragmentDefinitionmealPlan, + fragmentDefinitionmealInfo, +]); + +extension ClientExtension$Fragment$mealPlan on graphql.GraphQLClient { + void writeFragment$mealPlan({ + required Fragment$mealPlan data, + required Map idFields, + required Variables$Fragment$mealPlan variables, + bool broadcast = true, + }) => + this.writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'mealPlan', + document: documentNodeFragmentmealPlan, + ), + variables: variables.toJson(), ), - FieldNode( - name: NameNode(value: 'url'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'rank'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'personalDownvote'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'personalUpvote'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'downvotes'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'upvotes'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: '__typename'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - ]), - ), - FieldNode( - name: NameNode(value: '__typename'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - ]), -); -const documentNodeFragmentmealInfo = DocumentNode(definitions: [ - fragmentDefinitionmealInfo, -]); - -extension ClientExtension$Fragment$mealInfo on graphql.GraphQLClient { - void writeFragment$mealInfo({ - required Fragment$mealInfo data, - required Map idFields, - bool broadcast = true, - }) => - this.writeFragment( - graphql.FragmentRequest( - idFields: idFields, - fragment: const graphql.Fragment( - fragmentName: 'mealInfo', - document: documentNodeFragmentmealInfo, - ), - ), - data: data.toJson(), - broadcast: broadcast, - ); - Fragment$mealInfo? readFragment$mealInfo({ - required Map idFields, - bool optimistic = true, - }) { - final result = this.readFragment( - graphql.FragmentRequest( - idFields: idFields, - fragment: const graphql.Fragment( - fragmentName: 'mealInfo', - document: documentNodeFragmentmealInfo, + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$mealPlan? readFragment$mealPlan({ + required Map idFields, + required Variables$Fragment$mealPlan variables, + bool optimistic = true, + }) { + final result = this.readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'mealPlan', + document: documentNodeFragmentmealPlan, ), + variables: variables.toJson(), ), optimistic: optimistic, ); - return result == null ? null : Fragment$mealInfo.fromJson(result); + return result == null ? null : Fragment$mealPlan.fromJson(result); } } -class Fragment$mealInfo$price { - Fragment$mealInfo$price({ - required this.employee, - required this.guest, - required this.pupil, - required this.student, - this.$__typename = 'Price', +class Fragment$mealPlan$lines { + Fragment$mealPlan$lines({ + required this.id, + required this.name, + required this.canteen, + this.meals, + this.$__typename = 'Line', }); - factory Fragment$mealInfo$price.fromJson(Map json) { - final l$employee = json['employee']; - final l$guest = json['guest']; - final l$pupil = json['pupil']; - final l$student = json['student']; + factory Fragment$mealPlan$lines.fromJson(Map json) { + final l$id = json['id']; + final l$name = json['name']; + final l$canteen = json['canteen']; + final l$meals = json['meals']; final l$$__typename = json['__typename']; - return Fragment$mealInfo$price( - employee: (l$employee as int), - guest: (l$guest as int), - pupil: (l$pupil as int), - student: (l$student as int), + return Fragment$mealPlan$lines( + id: (l$id as String), + name: (l$name as String), + canteen: Fragment$mealPlan$lines$canteen.fromJson( + (l$canteen as Map)), + meals: (l$meals as List?) + ?.map((e) => Fragment$mealInfo.fromJson((e as Map))) + .toList(), $__typename: (l$$__typename as String), ); } - final int employee; + final String id; - final int guest; + final String name; - final int pupil; + final Fragment$mealPlan$lines$canteen canteen; - final int student; + final List? meals; final String $__typename; Map toJson() { final _resultData = {}; - final l$employee = employee; - _resultData['employee'] = l$employee; - final l$guest = guest; - _resultData['guest'] = l$guest; - final l$pupil = pupil; - _resultData['pupil'] = l$pupil; - final l$student = student; - _resultData['student'] = l$student; + final l$id = id; + _resultData['id'] = l$id; + final l$name = name; + _resultData['name'] = l$name; + final l$canteen = canteen; + _resultData['canteen'] = l$canteen.toJson(); + final l$meals = meals; + _resultData['meals'] = l$meals?.map((e) => e.toJson()).toList(); final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -685,16 +449,16 @@ class Fragment$mealInfo$price { @override int get hashCode { - final l$employee = employee; - final l$guest = guest; - final l$pupil = pupil; - final l$student = student; + final l$id = id; + final l$name = name; + final l$canteen = canteen; + final l$meals = meals; final l$$__typename = $__typename; return Object.hashAll([ - l$employee, - l$guest, - l$pupil, - l$student, + l$id, + l$name, + l$canteen, + l$meals == null ? null : Object.hashAll(l$meals.map((v) => v)), l$$__typename, ]); } @@ -704,28 +468,39 @@ class Fragment$mealInfo$price { if (identical(this, other)) { return true; } - if (!(other is Fragment$mealInfo$price) || + if (!(other is Fragment$mealPlan$lines) || runtimeType != other.runtimeType) { return false; } - final l$employee = employee; - final lOther$employee = other.employee; - if (l$employee != lOther$employee) { + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { return false; } - final l$guest = guest; - final lOther$guest = other.guest; - if (l$guest != lOther$guest) { + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { return false; } - final l$pupil = pupil; - final lOther$pupil = other.pupil; - if (l$pupil != lOther$pupil) { + final l$canteen = canteen; + final lOther$canteen = other.canteen; + if (l$canteen != lOther$canteen) { return false; } - final l$student = student; - final lOther$student = other.student; - if (l$student != lOther$student) { + final l$meals = meals; + final lOther$meals = other.meals; + if (l$meals != null && lOther$meals != null) { + if (l$meals.length != lOther$meals.length) { + return false; + } + for (int i = 0; i < l$meals.length; i++) { + final l$meals$entry = l$meals[i]; + final lOther$meals$entry = lOther$meals[i]; + if (l$meals$entry != lOther$meals$entry) { + return false; + } + } + } else if (l$meals != lOther$meals) { return false; } final l$$__typename = $__typename; @@ -737,124 +512,138 @@ class Fragment$mealInfo$price { } } -extension UtilityExtension$Fragment$mealInfo$price on Fragment$mealInfo$price { - CopyWith$Fragment$mealInfo$price get copyWith => - CopyWith$Fragment$mealInfo$price( +extension UtilityExtension$Fragment$mealPlan$lines on Fragment$mealPlan$lines { + CopyWith$Fragment$mealPlan$lines get copyWith => + CopyWith$Fragment$mealPlan$lines( this, (i) => i, ); } -abstract class CopyWith$Fragment$mealInfo$price { - factory CopyWith$Fragment$mealInfo$price( - Fragment$mealInfo$price instance, - TRes Function(Fragment$mealInfo$price) then, - ) = _CopyWithImpl$Fragment$mealInfo$price; +abstract class CopyWith$Fragment$mealPlan$lines { + factory CopyWith$Fragment$mealPlan$lines( + Fragment$mealPlan$lines instance, + TRes Function(Fragment$mealPlan$lines) then, + ) = _CopyWithImpl$Fragment$mealPlan$lines; - factory CopyWith$Fragment$mealInfo$price.stub(TRes res) = - _CopyWithStubImpl$Fragment$mealInfo$price; + factory CopyWith$Fragment$mealPlan$lines.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealPlan$lines; TRes call({ - int? employee, - int? guest, - int? pupil, - int? student, + String? id, + String? name, + Fragment$mealPlan$lines$canteen? canteen, + List? meals, String? $__typename, }); + CopyWith$Fragment$mealPlan$lines$canteen get canteen; + TRes meals( + Iterable? Function( + Iterable>?) + _fn); } -class _CopyWithImpl$Fragment$mealInfo$price - implements CopyWith$Fragment$mealInfo$price { - _CopyWithImpl$Fragment$mealInfo$price( +class _CopyWithImpl$Fragment$mealPlan$lines + implements CopyWith$Fragment$mealPlan$lines { + _CopyWithImpl$Fragment$mealPlan$lines( this._instance, this._then, ); - final Fragment$mealInfo$price _instance; + final Fragment$mealPlan$lines _instance; - final TRes Function(Fragment$mealInfo$price) _then; + final TRes Function(Fragment$mealPlan$lines) _then; static const _undefined = {}; TRes call({ - Object? employee = _undefined, - Object? guest = _undefined, - Object? pupil = _undefined, - Object? student = _undefined, + Object? id = _undefined, + Object? name = _undefined, + Object? canteen = _undefined, + Object? meals = _undefined, Object? $__typename = _undefined, }) => - _then(Fragment$mealInfo$price( - employee: employee == _undefined || employee == null - ? _instance.employee - : (employee as int), - guest: guest == _undefined || guest == null - ? _instance.guest - : (guest as int), - pupil: pupil == _undefined || pupil == null - ? _instance.pupil - : (pupil as int), - student: student == _undefined || student == null - ? _instance.student - : (student as int), + _then(Fragment$mealPlan$lines( + id: id == _undefined || id == null ? _instance.id : (id as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + canteen: canteen == _undefined || canteen == null + ? _instance.canteen + : (canteen as Fragment$mealPlan$lines$canteen), + meals: meals == _undefined + ? _instance.meals + : (meals as List?), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String), )); -} - -class _CopyWithStubImpl$Fragment$mealInfo$price - implements CopyWith$Fragment$mealInfo$price { - _CopyWithStubImpl$Fragment$mealInfo$price(this._res); - - TRes _res; + CopyWith$Fragment$mealPlan$lines$canteen get canteen { + final local$canteen = _instance.canteen; + return CopyWith$Fragment$mealPlan$lines$canteen( + local$canteen, (e) => call(canteen: e)); + } - call({ - int? employee, - int? guest, - int? pupil, - int? student, + TRes meals( + Iterable? Function( + Iterable>?) + _fn) => + call( + meals: _fn(_instance.meals?.map((e) => CopyWith$Fragment$mealInfo( + e, + (i) => i, + )))?.toList()); +} + +class _CopyWithStubImpl$Fragment$mealPlan$lines + implements CopyWith$Fragment$mealPlan$lines { + _CopyWithStubImpl$Fragment$mealPlan$lines(this._res); + + TRes _res; + + call({ + String? id, + String? name, + Fragment$mealPlan$lines$canteen? canteen, + List? meals, String? $__typename, }) => _res; + CopyWith$Fragment$mealPlan$lines$canteen get canteen => + CopyWith$Fragment$mealPlan$lines$canteen.stub(_res); + meals(_fn) => _res; } -class Fragment$mealInfo$statistics { - Fragment$mealInfo$statistics({ - this.lastServed, - this.nextServed, - required this.relativeFrequency, - this.$__typename = 'MealStatistics', +class Fragment$mealPlan$lines$canteen { + Fragment$mealPlan$lines$canteen({ + required this.id, + required this.name, + this.$__typename = 'Canteen', }); - factory Fragment$mealInfo$statistics.fromJson(Map json) { - final l$lastServed = json['lastServed']; - final l$nextServed = json['nextServed']; - final l$relativeFrequency = json['relativeFrequency']; + factory Fragment$mealPlan$lines$canteen.fromJson(Map json) { + final l$id = json['id']; + final l$name = json['name']; final l$$__typename = json['__typename']; - return Fragment$mealInfo$statistics( - lastServed: (l$lastServed as String?), - nextServed: (l$nextServed as String?), - relativeFrequency: (l$relativeFrequency as num).toDouble(), + return Fragment$mealPlan$lines$canteen( + id: (l$id as String), + name: (l$name as String), $__typename: (l$$__typename as String), ); } - final String? lastServed; - - final String? nextServed; + final String id; - final double relativeFrequency; + final String name; final String $__typename; Map toJson() { final _resultData = {}; - final l$lastServed = lastServed; - _resultData['lastServed'] = l$lastServed; - final l$nextServed = nextServed; - _resultData['nextServed'] = l$nextServed; - final l$relativeFrequency = relativeFrequency; - _resultData['relativeFrequency'] = l$relativeFrequency; + final l$id = id; + _resultData['id'] = l$id; + final l$name = name; + _resultData['name'] = l$name; final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -862,14 +651,12 @@ class Fragment$mealInfo$statistics { @override int get hashCode { - final l$lastServed = lastServed; - final l$nextServed = nextServed; - final l$relativeFrequency = relativeFrequency; + final l$id = id; + final l$name = name; final l$$__typename = $__typename; return Object.hashAll([ - l$lastServed, - l$nextServed, - l$relativeFrequency, + l$id, + l$name, l$$__typename, ]); } @@ -879,23 +666,18 @@ class Fragment$mealInfo$statistics { if (identical(this, other)) { return true; } - if (!(other is Fragment$mealInfo$statistics) || + if (!(other is Fragment$mealPlan$lines$canteen) || runtimeType != other.runtimeType) { return false; } - final l$lastServed = lastServed; - final lOther$lastServed = other.lastServed; - if (l$lastServed != lOther$lastServed) { - return false; - } - final l$nextServed = nextServed; - final lOther$nextServed = other.nextServed; - if (l$nextServed != lOther$nextServed) { + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { return false; } - final l$relativeFrequency = relativeFrequency; - final lOther$relativeFrequency = other.relativeFrequency; - if (l$relativeFrequency != lOther$relativeFrequency) { + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { return false; } final l$$__typename = $__typename; @@ -907,120 +689,165 @@ class Fragment$mealInfo$statistics { } } -extension UtilityExtension$Fragment$mealInfo$statistics - on Fragment$mealInfo$statistics { - CopyWith$Fragment$mealInfo$statistics - get copyWith => CopyWith$Fragment$mealInfo$statistics( +extension UtilityExtension$Fragment$mealPlan$lines$canteen + on Fragment$mealPlan$lines$canteen { + CopyWith$Fragment$mealPlan$lines$canteen + get copyWith => CopyWith$Fragment$mealPlan$lines$canteen( this, (i) => i, ); } -abstract class CopyWith$Fragment$mealInfo$statistics { - factory CopyWith$Fragment$mealInfo$statistics( - Fragment$mealInfo$statistics instance, - TRes Function(Fragment$mealInfo$statistics) then, - ) = _CopyWithImpl$Fragment$mealInfo$statistics; +abstract class CopyWith$Fragment$mealPlan$lines$canteen { + factory CopyWith$Fragment$mealPlan$lines$canteen( + Fragment$mealPlan$lines$canteen instance, + TRes Function(Fragment$mealPlan$lines$canteen) then, + ) = _CopyWithImpl$Fragment$mealPlan$lines$canteen; - factory CopyWith$Fragment$mealInfo$statistics.stub(TRes res) = - _CopyWithStubImpl$Fragment$mealInfo$statistics; + factory CopyWith$Fragment$mealPlan$lines$canteen.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealPlan$lines$canteen; TRes call({ - String? lastServed, - String? nextServed, - double? relativeFrequency, + String? id, + String? name, String? $__typename, }); } -class _CopyWithImpl$Fragment$mealInfo$statistics - implements CopyWith$Fragment$mealInfo$statistics { - _CopyWithImpl$Fragment$mealInfo$statistics( +class _CopyWithImpl$Fragment$mealPlan$lines$canteen + implements CopyWith$Fragment$mealPlan$lines$canteen { + _CopyWithImpl$Fragment$mealPlan$lines$canteen( this._instance, this._then, ); - final Fragment$mealInfo$statistics _instance; + final Fragment$mealPlan$lines$canteen _instance; - final TRes Function(Fragment$mealInfo$statistics) _then; + final TRes Function(Fragment$mealPlan$lines$canteen) _then; static const _undefined = {}; TRes call({ - Object? lastServed = _undefined, - Object? nextServed = _undefined, - Object? relativeFrequency = _undefined, + Object? id = _undefined, + Object? name = _undefined, Object? $__typename = _undefined, }) => - _then(Fragment$mealInfo$statistics( - lastServed: lastServed == _undefined - ? _instance.lastServed - : (lastServed as String?), - nextServed: nextServed == _undefined - ? _instance.nextServed - : (nextServed as String?), - relativeFrequency: - relativeFrequency == _undefined || relativeFrequency == null - ? _instance.relativeFrequency - : (relativeFrequency as double), + _then(Fragment$mealPlan$lines$canteen( + id: id == _undefined || id == null ? _instance.id : (id as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String), )); } -class _CopyWithStubImpl$Fragment$mealInfo$statistics - implements CopyWith$Fragment$mealInfo$statistics { - _CopyWithStubImpl$Fragment$mealInfo$statistics(this._res); +class _CopyWithStubImpl$Fragment$mealPlan$lines$canteen + implements CopyWith$Fragment$mealPlan$lines$canteen { + _CopyWithStubImpl$Fragment$mealPlan$lines$canteen(this._res); TRes _res; call({ - String? lastServed, - String? nextServed, - double? relativeFrequency, + String? id, + String? name, String? $__typename, }) => _res; } -class Fragment$mealInfo$ratings { - Fragment$mealInfo$ratings({ - required this.averageRating, - this.personalRating, - required this.ratingsCount, - this.$__typename = 'Ratings', +class Fragment$mealInfo { + Fragment$mealInfo({ + required this.id, + required this.name, + required this.mealType, + required this.price, + required this.allergens, + required this.additives, + required this.statistics, + required this.ratings, + required this.images, + this.$__typename = 'Meal', }); - factory Fragment$mealInfo$ratings.fromJson(Map json) { - final l$averageRating = json['averageRating']; - final l$personalRating = json['personalRating']; - final l$ratingsCount = json['ratingsCount']; + factory Fragment$mealInfo.fromJson(Map json) { + final l$id = json['id']; + final l$name = json['name']; + final l$mealType = json['mealType']; + final l$price = json['price']; + final l$allergens = json['allergens']; + final l$additives = json['additives']; + final l$statistics = json['statistics']; + final l$ratings = json['ratings']; + final l$images = json['images']; final l$$__typename = json['__typename']; - return Fragment$mealInfo$ratings( - averageRating: (l$averageRating as num).toDouble(), - personalRating: (l$personalRating as int?), - ratingsCount: (l$ratingsCount as int), + return Fragment$mealInfo( + id: (l$id as String), + name: (l$name as String), + mealType: fromJson$Enum$MealType((l$mealType as String)), + price: + Fragment$mealInfo$price.fromJson((l$price as Map)), + allergens: (l$allergens as List) + .map((e) => fromJson$Enum$Allergen((e as String))) + .toList(), + additives: (l$additives as List) + .map((e) => fromJson$Enum$Additive((e as String))) + .toList(), + statistics: Fragment$mealInfo$statistics.fromJson( + (l$statistics as Map)), + ratings: Fragment$mealInfo$ratings.fromJson( + (l$ratings as Map)), + images: (l$images as List) + .map((e) => + Fragment$mealInfo$images.fromJson((e as Map))) + .toList(), $__typename: (l$$__typename as String), ); } - final double averageRating; + final String id; - final int? personalRating; + final String name; - final int ratingsCount; + final Enum$MealType mealType; + + final Fragment$mealInfo$price price; + + final List allergens; + + final List additives; + + final Fragment$mealInfo$statistics statistics; + + final Fragment$mealInfo$ratings ratings; + + final List images; final String $__typename; Map toJson() { final _resultData = {}; - final l$averageRating = averageRating; - _resultData['averageRating'] = l$averageRating; - final l$personalRating = personalRating; - _resultData['personalRating'] = l$personalRating; - final l$ratingsCount = ratingsCount; - _resultData['ratingsCount'] = l$ratingsCount; + final l$id = id; + _resultData['id'] = l$id; + final l$name = name; + _resultData['name'] = l$name; + final l$mealType = mealType; + _resultData['mealType'] = toJson$Enum$MealType(l$mealType); + final l$price = price; + _resultData['price'] = l$price.toJson(); + final l$allergens = allergens; + _resultData['allergens'] = + l$allergens.map((e) => toJson$Enum$Allergen(e)).toList(); + final l$additives = additives; + _resultData['additives'] = + l$additives.map((e) => toJson$Enum$Additive(e)).toList(); + final l$statistics = statistics; + _resultData['statistics'] = l$statistics.toJson(); + final l$ratings = ratings; + _resultData['ratings'] = l$ratings.toJson(); + final l$images = images; + _resultData['images'] = l$images.map((e) => e.toJson()).toList(); final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -1028,42 +855,104 @@ class Fragment$mealInfo$ratings { @override int get hashCode { - final l$averageRating = averageRating; - final l$personalRating = personalRating; - final l$ratingsCount = ratingsCount; + final l$id = id; + final l$name = name; + final l$mealType = mealType; + final l$price = price; + final l$allergens = allergens; + final l$additives = additives; + final l$statistics = statistics; + final l$ratings = ratings; + final l$images = images; final l$$__typename = $__typename; return Object.hashAll([ - l$averageRating, - l$personalRating, - l$ratingsCount, - l$$__typename, - ]); - } - + l$id, + l$name, + l$mealType, + l$price, + Object.hashAll(l$allergens.map((v) => v)), + Object.hashAll(l$additives.map((v) => v)), + l$statistics, + l$ratings, + Object.hashAll(l$images.map((v) => v)), + l$$__typename, + ]); + } + @override bool operator ==(Object other) { if (identical(this, other)) { return true; } - if (!(other is Fragment$mealInfo$ratings) || - runtimeType != other.runtimeType) { + if (!(other is Fragment$mealInfo) || runtimeType != other.runtimeType) { return false; } - final l$averageRating = averageRating; - final lOther$averageRating = other.averageRating; - if (l$averageRating != lOther$averageRating) { + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { return false; } - final l$personalRating = personalRating; - final lOther$personalRating = other.personalRating; - if (l$personalRating != lOther$personalRating) { + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { return false; } - final l$ratingsCount = ratingsCount; - final lOther$ratingsCount = other.ratingsCount; - if (l$ratingsCount != lOther$ratingsCount) { + final l$mealType = mealType; + final lOther$mealType = other.mealType; + if (l$mealType != lOther$mealType) { + return false; + } + final l$price = price; + final lOther$price = other.price; + if (l$price != lOther$price) { + return false; + } + final l$allergens = allergens; + final lOther$allergens = other.allergens; + if (l$allergens.length != lOther$allergens.length) { + return false; + } + for (int i = 0; i < l$allergens.length; i++) { + final l$allergens$entry = l$allergens[i]; + final lOther$allergens$entry = lOther$allergens[i]; + if (l$allergens$entry != lOther$allergens$entry) { + return false; + } + } + final l$additives = additives; + final lOther$additives = other.additives; + if (l$additives.length != lOther$additives.length) { + return false; + } + for (int i = 0; i < l$additives.length; i++) { + final l$additives$entry = l$additives[i]; + final lOther$additives$entry = lOther$additives[i]; + if (l$additives$entry != lOther$additives$entry) { + return false; + } + } + final l$statistics = statistics; + final lOther$statistics = other.statistics; + if (l$statistics != lOther$statistics) { + return false; + } + final l$ratings = ratings; + final lOther$ratings = other.ratings; + if (l$ratings != lOther$ratings) { + return false; + } + final l$images = images; + final lOther$images = other.images; + if (l$images.length != lOther$images.length) { return false; } + for (int i = 0; i < l$images.length; i++) { + final l$images$entry = l$images[i]; + final lOther$images$entry = lOther$images[i]; + if (l$images$entry != lOther$images$entry) { + return false; + } + } final l$$__typename = $__typename; final lOther$$__typename = other.$__typename; if (l$$__typename != lOther$$__typename) { @@ -1073,100 +962,963 @@ class Fragment$mealInfo$ratings { } } -extension UtilityExtension$Fragment$mealInfo$ratings - on Fragment$mealInfo$ratings { - CopyWith$Fragment$mealInfo$ratings get copyWith => - CopyWith$Fragment$mealInfo$ratings( +extension UtilityExtension$Fragment$mealInfo on Fragment$mealInfo { + CopyWith$Fragment$mealInfo get copyWith => + CopyWith$Fragment$mealInfo( this, (i) => i, ); } -abstract class CopyWith$Fragment$mealInfo$ratings { - factory CopyWith$Fragment$mealInfo$ratings( - Fragment$mealInfo$ratings instance, - TRes Function(Fragment$mealInfo$ratings) then, - ) = _CopyWithImpl$Fragment$mealInfo$ratings; +abstract class CopyWith$Fragment$mealInfo { + factory CopyWith$Fragment$mealInfo( + Fragment$mealInfo instance, + TRes Function(Fragment$mealInfo) then, + ) = _CopyWithImpl$Fragment$mealInfo; - factory CopyWith$Fragment$mealInfo$ratings.stub(TRes res) = - _CopyWithStubImpl$Fragment$mealInfo$ratings; + factory CopyWith$Fragment$mealInfo.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo; TRes call({ - double? averageRating, - int? personalRating, - int? ratingsCount, + String? id, + String? name, + Enum$MealType? mealType, + Fragment$mealInfo$price? price, + List? allergens, + List? additives, + Fragment$mealInfo$statistics? statistics, + Fragment$mealInfo$ratings? ratings, + List? images, String? $__typename, }); + CopyWith$Fragment$mealInfo$price get price; + CopyWith$Fragment$mealInfo$statistics get statistics; + CopyWith$Fragment$mealInfo$ratings get ratings; + TRes images( + Iterable Function( + Iterable< + CopyWith$Fragment$mealInfo$images>) + _fn); } -class _CopyWithImpl$Fragment$mealInfo$ratings - implements CopyWith$Fragment$mealInfo$ratings { - _CopyWithImpl$Fragment$mealInfo$ratings( +class _CopyWithImpl$Fragment$mealInfo + implements CopyWith$Fragment$mealInfo { + _CopyWithImpl$Fragment$mealInfo( this._instance, this._then, ); - final Fragment$mealInfo$ratings _instance; + final Fragment$mealInfo _instance; - final TRes Function(Fragment$mealInfo$ratings) _then; + final TRes Function(Fragment$mealInfo) _then; static const _undefined = {}; TRes call({ - Object? averageRating = _undefined, - Object? personalRating = _undefined, - Object? ratingsCount = _undefined, + Object? id = _undefined, + Object? name = _undefined, + Object? mealType = _undefined, + Object? price = _undefined, + Object? allergens = _undefined, + Object? additives = _undefined, + Object? statistics = _undefined, + Object? ratings = _undefined, + Object? images = _undefined, Object? $__typename = _undefined, }) => - _then(Fragment$mealInfo$ratings( - averageRating: averageRating == _undefined || averageRating == null - ? _instance.averageRating - : (averageRating as double), - personalRating: personalRating == _undefined - ? _instance.personalRating - : (personalRating as int?), - ratingsCount: ratingsCount == _undefined || ratingsCount == null - ? _instance.ratingsCount - : (ratingsCount as int), + _then(Fragment$mealInfo( + id: id == _undefined || id == null ? _instance.id : (id as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + mealType: mealType == _undefined || mealType == null + ? _instance.mealType + : (mealType as Enum$MealType), + price: price == _undefined || price == null + ? _instance.price + : (price as Fragment$mealInfo$price), + allergens: allergens == _undefined || allergens == null + ? _instance.allergens + : (allergens as List), + additives: additives == _undefined || additives == null + ? _instance.additives + : (additives as List), + statistics: statistics == _undefined || statistics == null + ? _instance.statistics + : (statistics as Fragment$mealInfo$statistics), + ratings: ratings == _undefined || ratings == null + ? _instance.ratings + : (ratings as Fragment$mealInfo$ratings), + images: images == _undefined || images == null + ? _instance.images + : (images as List), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String), )); + CopyWith$Fragment$mealInfo$price get price { + final local$price = _instance.price; + return CopyWith$Fragment$mealInfo$price(local$price, (e) => call(price: e)); + } + + CopyWith$Fragment$mealInfo$statistics get statistics { + final local$statistics = _instance.statistics; + return CopyWith$Fragment$mealInfo$statistics( + local$statistics, (e) => call(statistics: e)); + } + + CopyWith$Fragment$mealInfo$ratings get ratings { + final local$ratings = _instance.ratings; + return CopyWith$Fragment$mealInfo$ratings( + local$ratings, (e) => call(ratings: e)); + } + + TRes images( + Iterable Function( + Iterable< + CopyWith$Fragment$mealInfo$images< + Fragment$mealInfo$images>>) + _fn) => + call( + images: + _fn(_instance.images.map((e) => CopyWith$Fragment$mealInfo$images( + e, + (i) => i, + ))).toList()); } -class _CopyWithStubImpl$Fragment$mealInfo$ratings - implements CopyWith$Fragment$mealInfo$ratings { - _CopyWithStubImpl$Fragment$mealInfo$ratings(this._res); +class _CopyWithStubImpl$Fragment$mealInfo + implements CopyWith$Fragment$mealInfo { + _CopyWithStubImpl$Fragment$mealInfo(this._res); TRes _res; call({ - double? averageRating, - int? personalRating, - int? ratingsCount, + String? id, + String? name, + Enum$MealType? mealType, + Fragment$mealInfo$price? price, + List? allergens, + List? additives, + Fragment$mealInfo$statistics? statistics, + Fragment$mealInfo$ratings? ratings, + List? images, String? $__typename, }) => _res; + CopyWith$Fragment$mealInfo$price get price => + CopyWith$Fragment$mealInfo$price.stub(_res); + CopyWith$Fragment$mealInfo$statistics get statistics => + CopyWith$Fragment$mealInfo$statistics.stub(_res); + CopyWith$Fragment$mealInfo$ratings get ratings => + CopyWith$Fragment$mealInfo$ratings.stub(_res); + images(_fn) => _res; } -class Fragment$mealInfo$images { - Fragment$mealInfo$images({ - required this.id, - required this.url, - required this.rank, - required this.personalDownvote, - required this.personalUpvote, - required this.downvotes, - required this.upvotes, - this.$__typename = 'Image', - }); - - factory Fragment$mealInfo$images.fromJson(Map json) { - final l$id = json['id']; - final l$url = json['url']; - final l$rank = json['rank']; - final l$personalDownvote = json['personalDownvote']; - final l$personalUpvote = json['personalUpvote']; +const fragmentDefinitionmealInfo = FragmentDefinitionNode( + name: NameNode(value: 'mealInfo'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'Meal'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'mealType'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'price'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'employee'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'guest'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'pupil'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'student'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'allergens'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'additives'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'statistics'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'lastServed'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'nextServed'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'relativeFrequency'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'ratings'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'averageRating'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'personalRating'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'ratingsCount'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'images'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'url'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'rank'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'personalDownvote'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'personalUpvote'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'downvotes'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'upvotes'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentmealInfo = DocumentNode(definitions: [ + fragmentDefinitionmealInfo, +]); + +extension ClientExtension$Fragment$mealInfo on graphql.GraphQLClient { + void writeFragment$mealInfo({ + required Fragment$mealInfo data, + required Map idFields, + bool broadcast = true, + }) => + this.writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'mealInfo', + document: documentNodeFragmentmealInfo, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$mealInfo? readFragment$mealInfo({ + required Map idFields, + bool optimistic = true, + }) { + final result = this.readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'mealInfo', + document: documentNodeFragmentmealInfo, + ), + ), + optimistic: optimistic, + ); + return result == null ? null : Fragment$mealInfo.fromJson(result); + } +} + +class Fragment$mealInfo$price { + Fragment$mealInfo$price({ + required this.employee, + required this.guest, + required this.pupil, + required this.student, + this.$__typename = 'Price', + }); + + factory Fragment$mealInfo$price.fromJson(Map json) { + final l$employee = json['employee']; + final l$guest = json['guest']; + final l$pupil = json['pupil']; + final l$student = json['student']; + final l$$__typename = json['__typename']; + return Fragment$mealInfo$price( + employee: (l$employee as int), + guest: (l$guest as int), + pupil: (l$pupil as int), + student: (l$student as int), + $__typename: (l$$__typename as String), + ); + } + + final int employee; + + final int guest; + + final int pupil; + + final int student; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$employee = employee; + _resultData['employee'] = l$employee; + final l$guest = guest; + _resultData['guest'] = l$guest; + final l$pupil = pupil; + _resultData['pupil'] = l$pupil; + final l$student = student; + _resultData['student'] = l$student; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$employee = employee; + final l$guest = guest; + final l$pupil = pupil; + final l$student = student; + final l$$__typename = $__typename; + return Object.hashAll([ + l$employee, + l$guest, + l$pupil, + l$student, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$mealInfo$price) || + runtimeType != other.runtimeType) { + return false; + } + final l$employee = employee; + final lOther$employee = other.employee; + if (l$employee != lOther$employee) { + return false; + } + final l$guest = guest; + final lOther$guest = other.guest; + if (l$guest != lOther$guest) { + return false; + } + final l$pupil = pupil; + final lOther$pupil = other.pupil; + if (l$pupil != lOther$pupil) { + return false; + } + final l$student = student; + final lOther$student = other.student; + if (l$student != lOther$student) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$mealInfo$price on Fragment$mealInfo$price { + CopyWith$Fragment$mealInfo$price get copyWith => + CopyWith$Fragment$mealInfo$price( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$mealInfo$price { + factory CopyWith$Fragment$mealInfo$price( + Fragment$mealInfo$price instance, + TRes Function(Fragment$mealInfo$price) then, + ) = _CopyWithImpl$Fragment$mealInfo$price; + + factory CopyWith$Fragment$mealInfo$price.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo$price; + + TRes call({ + int? employee, + int? guest, + int? pupil, + int? student, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$mealInfo$price + implements CopyWith$Fragment$mealInfo$price { + _CopyWithImpl$Fragment$mealInfo$price( + this._instance, + this._then, + ); + + final Fragment$mealInfo$price _instance; + + final TRes Function(Fragment$mealInfo$price) _then; + + static const _undefined = {}; + + TRes call({ + Object? employee = _undefined, + Object? guest = _undefined, + Object? pupil = _undefined, + Object? student = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$mealInfo$price( + employee: employee == _undefined || employee == null + ? _instance.employee + : (employee as int), + guest: guest == _undefined || guest == null + ? _instance.guest + : (guest as int), + pupil: pupil == _undefined || pupil == null + ? _instance.pupil + : (pupil as int), + student: student == _undefined || student == null + ? _instance.student + : (student as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$mealInfo$price + implements CopyWith$Fragment$mealInfo$price { + _CopyWithStubImpl$Fragment$mealInfo$price(this._res); + + TRes _res; + + call({ + int? employee, + int? guest, + int? pupil, + int? student, + String? $__typename, + }) => + _res; +} + +class Fragment$mealInfo$statistics { + Fragment$mealInfo$statistics({ + this.lastServed, + this.nextServed, + required this.relativeFrequency, + this.$__typename = 'MealStatistics', + }); + + factory Fragment$mealInfo$statistics.fromJson(Map json) { + final l$lastServed = json['lastServed']; + final l$nextServed = json['nextServed']; + final l$relativeFrequency = json['relativeFrequency']; + final l$$__typename = json['__typename']; + return Fragment$mealInfo$statistics( + lastServed: (l$lastServed as String?), + nextServed: (l$nextServed as String?), + relativeFrequency: (l$relativeFrequency as num).toDouble(), + $__typename: (l$$__typename as String), + ); + } + + final String? lastServed; + + final String? nextServed; + + final double relativeFrequency; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$lastServed = lastServed; + _resultData['lastServed'] = l$lastServed; + final l$nextServed = nextServed; + _resultData['nextServed'] = l$nextServed; + final l$relativeFrequency = relativeFrequency; + _resultData['relativeFrequency'] = l$relativeFrequency; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$lastServed = lastServed; + final l$nextServed = nextServed; + final l$relativeFrequency = relativeFrequency; + final l$$__typename = $__typename; + return Object.hashAll([ + l$lastServed, + l$nextServed, + l$relativeFrequency, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$mealInfo$statistics) || + runtimeType != other.runtimeType) { + return false; + } + final l$lastServed = lastServed; + final lOther$lastServed = other.lastServed; + if (l$lastServed != lOther$lastServed) { + return false; + } + final l$nextServed = nextServed; + final lOther$nextServed = other.nextServed; + if (l$nextServed != lOther$nextServed) { + return false; + } + final l$relativeFrequency = relativeFrequency; + final lOther$relativeFrequency = other.relativeFrequency; + if (l$relativeFrequency != lOther$relativeFrequency) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$mealInfo$statistics + on Fragment$mealInfo$statistics { + CopyWith$Fragment$mealInfo$statistics + get copyWith => CopyWith$Fragment$mealInfo$statistics( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$mealInfo$statistics { + factory CopyWith$Fragment$mealInfo$statistics( + Fragment$mealInfo$statistics instance, + TRes Function(Fragment$mealInfo$statistics) then, + ) = _CopyWithImpl$Fragment$mealInfo$statistics; + + factory CopyWith$Fragment$mealInfo$statistics.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo$statistics; + + TRes call({ + String? lastServed, + String? nextServed, + double? relativeFrequency, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$mealInfo$statistics + implements CopyWith$Fragment$mealInfo$statistics { + _CopyWithImpl$Fragment$mealInfo$statistics( + this._instance, + this._then, + ); + + final Fragment$mealInfo$statistics _instance; + + final TRes Function(Fragment$mealInfo$statistics) _then; + + static const _undefined = {}; + + TRes call({ + Object? lastServed = _undefined, + Object? nextServed = _undefined, + Object? relativeFrequency = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$mealInfo$statistics( + lastServed: lastServed == _undefined + ? _instance.lastServed + : (lastServed as String?), + nextServed: nextServed == _undefined + ? _instance.nextServed + : (nextServed as String?), + relativeFrequency: + relativeFrequency == _undefined || relativeFrequency == null + ? _instance.relativeFrequency + : (relativeFrequency as double), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$mealInfo$statistics + implements CopyWith$Fragment$mealInfo$statistics { + _CopyWithStubImpl$Fragment$mealInfo$statistics(this._res); + + TRes _res; + + call({ + String? lastServed, + String? nextServed, + double? relativeFrequency, + String? $__typename, + }) => + _res; +} + +class Fragment$mealInfo$ratings { + Fragment$mealInfo$ratings({ + required this.averageRating, + this.personalRating, + required this.ratingsCount, + this.$__typename = 'Ratings', + }); + + factory Fragment$mealInfo$ratings.fromJson(Map json) { + final l$averageRating = json['averageRating']; + final l$personalRating = json['personalRating']; + final l$ratingsCount = json['ratingsCount']; + final l$$__typename = json['__typename']; + return Fragment$mealInfo$ratings( + averageRating: (l$averageRating as num).toDouble(), + personalRating: (l$personalRating as int?), + ratingsCount: (l$ratingsCount as int), + $__typename: (l$$__typename as String), + ); + } + + final double averageRating; + + final int? personalRating; + + final int ratingsCount; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$averageRating = averageRating; + _resultData['averageRating'] = l$averageRating; + final l$personalRating = personalRating; + _resultData['personalRating'] = l$personalRating; + final l$ratingsCount = ratingsCount; + _resultData['ratingsCount'] = l$ratingsCount; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$averageRating = averageRating; + final l$personalRating = personalRating; + final l$ratingsCount = ratingsCount; + final l$$__typename = $__typename; + return Object.hashAll([ + l$averageRating, + l$personalRating, + l$ratingsCount, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$mealInfo$ratings) || + runtimeType != other.runtimeType) { + return false; + } + final l$averageRating = averageRating; + final lOther$averageRating = other.averageRating; + if (l$averageRating != lOther$averageRating) { + return false; + } + final l$personalRating = personalRating; + final lOther$personalRating = other.personalRating; + if (l$personalRating != lOther$personalRating) { + return false; + } + final l$ratingsCount = ratingsCount; + final lOther$ratingsCount = other.ratingsCount; + if (l$ratingsCount != lOther$ratingsCount) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$mealInfo$ratings + on Fragment$mealInfo$ratings { + CopyWith$Fragment$mealInfo$ratings get copyWith => + CopyWith$Fragment$mealInfo$ratings( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$mealInfo$ratings { + factory CopyWith$Fragment$mealInfo$ratings( + Fragment$mealInfo$ratings instance, + TRes Function(Fragment$mealInfo$ratings) then, + ) = _CopyWithImpl$Fragment$mealInfo$ratings; + + factory CopyWith$Fragment$mealInfo$ratings.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo$ratings; + + TRes call({ + double? averageRating, + int? personalRating, + int? ratingsCount, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$mealInfo$ratings + implements CopyWith$Fragment$mealInfo$ratings { + _CopyWithImpl$Fragment$mealInfo$ratings( + this._instance, + this._then, + ); + + final Fragment$mealInfo$ratings _instance; + + final TRes Function(Fragment$mealInfo$ratings) _then; + + static const _undefined = {}; + + TRes call({ + Object? averageRating = _undefined, + Object? personalRating = _undefined, + Object? ratingsCount = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$mealInfo$ratings( + averageRating: averageRating == _undefined || averageRating == null + ? _instance.averageRating + : (averageRating as double), + personalRating: personalRating == _undefined + ? _instance.personalRating + : (personalRating as int?), + ratingsCount: ratingsCount == _undefined || ratingsCount == null + ? _instance.ratingsCount + : (ratingsCount as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$mealInfo$ratings + implements CopyWith$Fragment$mealInfo$ratings { + _CopyWithStubImpl$Fragment$mealInfo$ratings(this._res); + + TRes _res; + + call({ + double? averageRating, + int? personalRating, + int? ratingsCount, + String? $__typename, + }) => + _res; +} + +class Fragment$mealInfo$images { + Fragment$mealInfo$images({ + required this.id, + required this.url, + required this.rank, + required this.personalDownvote, + required this.personalUpvote, + required this.downvotes, + required this.upvotes, + this.$__typename = 'Image', + }); + + factory Fragment$mealInfo$images.fromJson(Map json) { + final l$id = json['id']; + final l$url = json['url']; + final l$rank = json['rank']; + final l$personalDownvote = json['personalDownvote']; + final l$personalUpvote = json['personalUpvote']; final l$downvotes = json['downvotes']; final l$upvotes = json['upvotes']; final l$$__typename = json['__typename']; @@ -1182,61 +1934,347 @@ class Fragment$mealInfo$images { ); } - final String id; + final String id; + + final String url; + + final double rank; + + final bool personalDownvote; + + final bool personalUpvote; + + final int downvotes; + + final int upvotes; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$id = id; + _resultData['id'] = l$id; + final l$url = url; + _resultData['url'] = l$url; + final l$rank = rank; + _resultData['rank'] = l$rank; + final l$personalDownvote = personalDownvote; + _resultData['personalDownvote'] = l$personalDownvote; + final l$personalUpvote = personalUpvote; + _resultData['personalUpvote'] = l$personalUpvote; + final l$downvotes = downvotes; + _resultData['downvotes'] = l$downvotes; + final l$upvotes = upvotes; + _resultData['upvotes'] = l$upvotes; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$id = id; + final l$url = url; + final l$rank = rank; + final l$personalDownvote = personalDownvote; + final l$personalUpvote = personalUpvote; + final l$downvotes = downvotes; + final l$upvotes = upvotes; + final l$$__typename = $__typename; + return Object.hashAll([ + l$id, + l$url, + l$rank, + l$personalDownvote, + l$personalUpvote, + l$downvotes, + l$upvotes, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$mealInfo$images) || + runtimeType != other.runtimeType) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$url = url; + final lOther$url = other.url; + if (l$url != lOther$url) { + return false; + } + final l$rank = rank; + final lOther$rank = other.rank; + if (l$rank != lOther$rank) { + return false; + } + final l$personalDownvote = personalDownvote; + final lOther$personalDownvote = other.personalDownvote; + if (l$personalDownvote != lOther$personalDownvote) { + return false; + } + final l$personalUpvote = personalUpvote; + final lOther$personalUpvote = other.personalUpvote; + if (l$personalUpvote != lOther$personalUpvote) { + return false; + } + final l$downvotes = downvotes; + final lOther$downvotes = other.downvotes; + if (l$downvotes != lOther$downvotes) { + return false; + } + final l$upvotes = upvotes; + final lOther$upvotes = other.upvotes; + if (l$upvotes != lOther$upvotes) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$mealInfo$images + on Fragment$mealInfo$images { + CopyWith$Fragment$mealInfo$images get copyWith => + CopyWith$Fragment$mealInfo$images( + this, + (i) => i, + ); +} - final String url; +abstract class CopyWith$Fragment$mealInfo$images { + factory CopyWith$Fragment$mealInfo$images( + Fragment$mealInfo$images instance, + TRes Function(Fragment$mealInfo$images) then, + ) = _CopyWithImpl$Fragment$mealInfo$images; - final double rank; + factory CopyWith$Fragment$mealInfo$images.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo$images; - final bool personalDownvote; + TRes call({ + String? id, + String? url, + double? rank, + bool? personalDownvote, + bool? personalUpvote, + int? downvotes, + int? upvotes, + String? $__typename, + }); +} - final bool personalUpvote; +class _CopyWithImpl$Fragment$mealInfo$images + implements CopyWith$Fragment$mealInfo$images { + _CopyWithImpl$Fragment$mealInfo$images( + this._instance, + this._then, + ); - final int downvotes; + final Fragment$mealInfo$images _instance; - final int upvotes; + final TRes Function(Fragment$mealInfo$images) _then; + + static const _undefined = {}; + + TRes call({ + Object? id = _undefined, + Object? url = _undefined, + Object? rank = _undefined, + Object? personalDownvote = _undefined, + Object? personalUpvote = _undefined, + Object? downvotes = _undefined, + Object? upvotes = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$mealInfo$images( + id: id == _undefined || id == null ? _instance.id : (id as String), + url: url == _undefined || url == null ? _instance.url : (url as String), + rank: rank == _undefined || rank == null + ? _instance.rank + : (rank as double), + personalDownvote: + personalDownvote == _undefined || personalDownvote == null + ? _instance.personalDownvote + : (personalDownvote as bool), + personalUpvote: personalUpvote == _undefined || personalUpvote == null + ? _instance.personalUpvote + : (personalUpvote as bool), + downvotes: downvotes == _undefined || downvotes == null + ? _instance.downvotes + : (downvotes as int), + upvotes: upvotes == _undefined || upvotes == null + ? _instance.upvotes + : (upvotes as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$mealInfo$images + implements CopyWith$Fragment$mealInfo$images { + _CopyWithStubImpl$Fragment$mealInfo$images(this._res); + + TRes _res; + + call({ + String? id, + String? url, + double? rank, + bool? personalDownvote, + bool? personalUpvote, + int? downvotes, + int? upvotes, + String? $__typename, + }) => + _res; +} + +class Variables$Query$GetMealPlanForDay { + factory Variables$Query$GetMealPlanForDay({required String date}) => + Variables$Query$GetMealPlanForDay._({ + r'date': date, + }); + + Variables$Query$GetMealPlanForDay._(this._$data); + + factory Variables$Query$GetMealPlanForDay.fromJson( + Map data) { + final result$data = {}; + final l$date = data['date']; + result$data['date'] = (l$date as String); + return Variables$Query$GetMealPlanForDay._(result$data); + } + + Map _$data; + + String get date => (_$data['date'] as String); + Map toJson() { + final result$data = {}; + final l$date = date; + result$data['date'] = l$date; + return result$data; + } + + CopyWith$Variables$Query$GetMealPlanForDay + get copyWith => CopyWith$Variables$Query$GetMealPlanForDay( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Query$GetMealPlanForDay) || + runtimeType != other.runtimeType) { + return false; + } + final l$date = date; + final lOther$date = other.date; + if (l$date != lOther$date) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$date = date; + return Object.hashAll([l$date]); + } +} + +abstract class CopyWith$Variables$Query$GetMealPlanForDay { + factory CopyWith$Variables$Query$GetMealPlanForDay( + Variables$Query$GetMealPlanForDay instance, + TRes Function(Variables$Query$GetMealPlanForDay) then, + ) = _CopyWithImpl$Variables$Query$GetMealPlanForDay; + + factory CopyWith$Variables$Query$GetMealPlanForDay.stub(TRes res) = + _CopyWithStubImpl$Variables$Query$GetMealPlanForDay; + + TRes call({String? date}); +} + +class _CopyWithImpl$Variables$Query$GetMealPlanForDay + implements CopyWith$Variables$Query$GetMealPlanForDay { + _CopyWithImpl$Variables$Query$GetMealPlanForDay( + this._instance, + this._then, + ); + + final Variables$Query$GetMealPlanForDay _instance; + + final TRes Function(Variables$Query$GetMealPlanForDay) _then; + + static const _undefined = {}; + + TRes call({Object? date = _undefined}) => + _then(Variables$Query$GetMealPlanForDay._({ + ..._instance._$data, + if (date != _undefined && date != null) 'date': (date as String), + })); +} + +class _CopyWithStubImpl$Variables$Query$GetMealPlanForDay + implements CopyWith$Variables$Query$GetMealPlanForDay { + _CopyWithStubImpl$Variables$Query$GetMealPlanForDay(this._res); + + TRes _res; + + call({String? date}) => _res; +} + +class Query$GetMealPlanForDay { + Query$GetMealPlanForDay({ + required this.getCanteens, + this.$__typename = 'QueryRoot', + }); + + factory Query$GetMealPlanForDay.fromJson(Map json) { + final l$getCanteens = json['getCanteens']; + final l$$__typename = json['__typename']; + return Query$GetMealPlanForDay( + getCanteens: (l$getCanteens as List) + .map((e) => Fragment$mealPlan.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final List getCanteens; final String $__typename; Map toJson() { final _resultData = {}; - final l$id = id; - _resultData['id'] = l$id; - final l$url = url; - _resultData['url'] = l$url; - final l$rank = rank; - _resultData['rank'] = l$rank; - final l$personalDownvote = personalDownvote; - _resultData['personalDownvote'] = l$personalDownvote; - final l$personalUpvote = personalUpvote; - _resultData['personalUpvote'] = l$personalUpvote; - final l$downvotes = downvotes; - _resultData['downvotes'] = l$downvotes; - final l$upvotes = upvotes; - _resultData['upvotes'] = l$upvotes; + final l$getCanteens = getCanteens; + _resultData['getCanteens'] = l$getCanteens.map((e) => e.toJson()).toList(); final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; } @override - int get hashCode { - final l$id = id; - final l$url = url; - final l$rank = rank; - final l$personalDownvote = personalDownvote; - final l$personalUpvote = personalUpvote; - final l$downvotes = downvotes; - final l$upvotes = upvotes; + int get hashCode { + final l$getCanteens = getCanteens; final l$$__typename = $__typename; return Object.hashAll([ - l$id, - l$url, - l$rank, - l$personalDownvote, - l$personalUpvote, - l$downvotes, - l$upvotes, + Object.hashAll(l$getCanteens.map((v) => v)), l$$__typename, ]); } @@ -1246,44 +2284,21 @@ class Fragment$mealInfo$images { if (identical(this, other)) { return true; } - if (!(other is Fragment$mealInfo$images) || + if (!(other is Query$GetMealPlanForDay) || runtimeType != other.runtimeType) { return false; } - final l$id = id; - final lOther$id = other.id; - if (l$id != lOther$id) { - return false; - } - final l$url = url; - final lOther$url = other.url; - if (l$url != lOther$url) { - return false; - } - final l$rank = rank; - final lOther$rank = other.rank; - if (l$rank != lOther$rank) { - return false; - } - final l$personalDownvote = personalDownvote; - final lOther$personalDownvote = other.personalDownvote; - if (l$personalDownvote != lOther$personalDownvote) { - return false; - } - final l$personalUpvote = personalUpvote; - final lOther$personalUpvote = other.personalUpvote; - if (l$personalUpvote != lOther$personalUpvote) { - return false; - } - final l$downvotes = downvotes; - final lOther$downvotes = other.downvotes; - if (l$downvotes != lOther$downvotes) { + final l$getCanteens = getCanteens; + final lOther$getCanteens = other.getCanteens; + if (l$getCanteens.length != lOther$getCanteens.length) { return false; } - final l$upvotes = upvotes; - final lOther$upvotes = other.upvotes; - if (l$upvotes != lOther$upvotes) { - return false; + for (int i = 0; i < l$getCanteens.length; i++) { + final l$getCanteens$entry = l$getCanteens[i]; + final lOther$getCanteens$entry = lOther$getCanteens[i]; + if (l$getCanteens$entry != lOther$getCanteens$entry) { + return false; + } } final l$$__typename = $__typename; final lOther$$__typename = other.$__typename; @@ -1294,131 +2309,326 @@ class Fragment$mealInfo$images { } } -extension UtilityExtension$Fragment$mealInfo$images - on Fragment$mealInfo$images { - CopyWith$Fragment$mealInfo$images get copyWith => - CopyWith$Fragment$mealInfo$images( +extension UtilityExtension$Query$GetMealPlanForDay on Query$GetMealPlanForDay { + CopyWith$Query$GetMealPlanForDay get copyWith => + CopyWith$Query$GetMealPlanForDay( this, (i) => i, ); } -abstract class CopyWith$Fragment$mealInfo$images { - factory CopyWith$Fragment$mealInfo$images( - Fragment$mealInfo$images instance, - TRes Function(Fragment$mealInfo$images) then, - ) = _CopyWithImpl$Fragment$mealInfo$images; +abstract class CopyWith$Query$GetMealPlanForDay { + factory CopyWith$Query$GetMealPlanForDay( + Query$GetMealPlanForDay instance, + TRes Function(Query$GetMealPlanForDay) then, + ) = _CopyWithImpl$Query$GetMealPlanForDay; - factory CopyWith$Fragment$mealInfo$images.stub(TRes res) = - _CopyWithStubImpl$Fragment$mealInfo$images; + factory CopyWith$Query$GetMealPlanForDay.stub(TRes res) = + _CopyWithStubImpl$Query$GetMealPlanForDay; TRes call({ - String? id, - String? url, - double? rank, - bool? personalDownvote, - bool? personalUpvote, - int? downvotes, - int? upvotes, + List? getCanteens, String? $__typename, }); + TRes getCanteens( + Iterable Function( + Iterable>) + _fn); } -class _CopyWithImpl$Fragment$mealInfo$images - implements CopyWith$Fragment$mealInfo$images { - _CopyWithImpl$Fragment$mealInfo$images( +class _CopyWithImpl$Query$GetMealPlanForDay + implements CopyWith$Query$GetMealPlanForDay { + _CopyWithImpl$Query$GetMealPlanForDay( this._instance, this._then, ); - final Fragment$mealInfo$images _instance; + final Query$GetMealPlanForDay _instance; - final TRes Function(Fragment$mealInfo$images) _then; + final TRes Function(Query$GetMealPlanForDay) _then; + + static const _undefined = {}; + + TRes call({ + Object? getCanteens = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetMealPlanForDay( + getCanteens: getCanteens == _undefined || getCanteens == null + ? _instance.getCanteens + : (getCanteens as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes getCanteens( + Iterable Function( + Iterable>) + _fn) => + call( + getCanteens: + _fn(_instance.getCanteens.map((e) => CopyWith$Fragment$mealPlan( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$GetMealPlanForDay + implements CopyWith$Query$GetMealPlanForDay { + _CopyWithStubImpl$Query$GetMealPlanForDay(this._res); + + TRes _res; + + call({ + List? getCanteens, + String? $__typename, + }) => + _res; + getCanteens(_fn) => _res; +} + +const documentNodeQueryGetMealPlanForDay = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'GetMealPlanForDay'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'date')), + type: NamedTypeNode( + name: NameNode(value: 'NaiveDate'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'getCanteens'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'mealPlan'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionmealPlan, + fragmentDefinitionmealInfo, +]); +Query$GetMealPlanForDay _parserFn$Query$GetMealPlanForDay( + Map data) => + Query$GetMealPlanForDay.fromJson(data); +typedef OnQueryComplete$Query$GetMealPlanForDay = FutureOr Function( + Map?, + Query$GetMealPlanForDay?, +); + +class Options$Query$GetMealPlanForDay + extends graphql.QueryOptions { + Options$Query$GetMealPlanForDay({ + String? operationName, + required Variables$Query$GetMealPlanForDay variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetMealPlanForDay? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$GetMealPlanForDay? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null + ? null + : _parserFn$Query$GetMealPlanForDay(data), + ), + onError: onError, + document: documentNodeQueryGetMealPlanForDay, + parserFn: _parserFn$Query$GetMealPlanForDay, + ); + + final OnQueryComplete$Query$GetMealPlanForDay? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$GetMealPlanForDay + extends graphql.WatchQueryOptions { + WatchOptions$Query$GetMealPlanForDay({ + String? operationName, + required Variables$Query$GetMealPlanForDay variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetMealPlanForDay? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryGetMealPlanForDay, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$GetMealPlanForDay, + ); +} - static const _undefined = {}; +class FetchMoreOptions$Query$GetMealPlanForDay + extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$GetMealPlanForDay({ + required graphql.UpdateQuery updateQuery, + required Variables$Query$GetMealPlanForDay variables, + }) : super( + updateQuery: updateQuery, + variables: variables.toJson(), + document: documentNodeQueryGetMealPlanForDay, + ); +} - TRes call({ - Object? id = _undefined, - Object? url = _undefined, - Object? rank = _undefined, - Object? personalDownvote = _undefined, - Object? personalUpvote = _undefined, - Object? downvotes = _undefined, - Object? upvotes = _undefined, - Object? $__typename = _undefined, +extension ClientExtension$Query$GetMealPlanForDay on graphql.GraphQLClient { + Future> query$GetMealPlanForDay( + Options$Query$GetMealPlanForDay options) async => + await this.query(options); + graphql.ObservableQuery watchQuery$GetMealPlanForDay( + WatchOptions$Query$GetMealPlanForDay options) => + this.watchQuery(options); + void writeQuery$GetMealPlanForDay({ + required Query$GetMealPlanForDay data, + required Variables$Query$GetMealPlanForDay variables, + bool broadcast = true, }) => - _then(Fragment$mealInfo$images( - id: id == _undefined || id == null ? _instance.id : (id as String), - url: url == _undefined || url == null ? _instance.url : (url as String), - rank: rank == _undefined || rank == null - ? _instance.rank - : (rank as double), - personalDownvote: - personalDownvote == _undefined || personalDownvote == null - ? _instance.personalDownvote - : (personalDownvote as bool), - personalUpvote: personalUpvote == _undefined || personalUpvote == null - ? _instance.personalUpvote - : (personalUpvote as bool), - downvotes: downvotes == _undefined || downvotes == null - ? _instance.downvotes - : (downvotes as int), - upvotes: upvotes == _undefined || upvotes == null - ? _instance.upvotes - : (upvotes as int), - $__typename: $__typename == _undefined || $__typename == null - ? _instance.$__typename - : ($__typename as String), - )); + this.writeQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryGetMealPlanForDay), + variables: variables.toJson(), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Query$GetMealPlanForDay? readQuery$GetMealPlanForDay({ + required Variables$Query$GetMealPlanForDay variables, + bool optimistic = true, + }) { + final result = this.readQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryGetMealPlanForDay), + variables: variables.toJson(), + ), + optimistic: optimistic, + ); + return result == null ? null : Query$GetMealPlanForDay.fromJson(result); + } } -class _CopyWithStubImpl$Fragment$mealInfo$images - implements CopyWith$Fragment$mealInfo$images { - _CopyWithStubImpl$Fragment$mealInfo$images(this._res); - - TRes _res; +graphql_flutter.QueryHookResult + useQuery$GetMealPlanForDay(Options$Query$GetMealPlanForDay options) => + graphql_flutter.useQuery(options); +graphql.ObservableQuery + useWatchQuery$GetMealPlanForDay( + WatchOptions$Query$GetMealPlanForDay options) => + graphql_flutter.useWatchQuery(options); - call({ - String? id, - String? url, - double? rank, - bool? personalDownvote, - bool? personalUpvote, - int? downvotes, - int? upvotes, - String? $__typename, - }) => - _res; +class Query$GetMealPlanForDay$Widget + extends graphql_flutter.Query { + Query$GetMealPlanForDay$Widget({ + widgets.Key? key, + required Options$Query$GetMealPlanForDay options, + required graphql_flutter.QueryBuilder builder, + }) : super( + key: key, + options: options, + builder: builder, + ); } -class Variables$Query$GetMealPlanForDay { - factory Variables$Query$GetMealPlanForDay({required String date}) => - Variables$Query$GetMealPlanForDay._({ +class Variables$Query$GetCanteenDate { + factory Variables$Query$GetCanteenDate({ + required String canteenId, + required String date, + }) => + Variables$Query$GetCanteenDate._({ + r'canteenId': canteenId, r'date': date, }); - Variables$Query$GetMealPlanForDay._(this._$data); + Variables$Query$GetCanteenDate._(this._$data); - factory Variables$Query$GetMealPlanForDay.fromJson( - Map data) { + factory Variables$Query$GetCanteenDate.fromJson(Map data) { final result$data = {}; + final l$canteenId = data['canteenId']; + result$data['canteenId'] = (l$canteenId as String); final l$date = data['date']; result$data['date'] = (l$date as String); - return Variables$Query$GetMealPlanForDay._(result$data); + return Variables$Query$GetCanteenDate._(result$data); } Map _$data; + String get canteenId => (_$data['canteenId'] as String); String get date => (_$data['date'] as String); Map toJson() { final result$data = {}; + final l$canteenId = canteenId; + result$data['canteenId'] = l$canteenId; final l$date = date; result$data['date'] = l$date; return result$data; } - CopyWith$Variables$Query$GetMealPlanForDay - get copyWith => CopyWith$Variables$Query$GetMealPlanForDay( + CopyWith$Variables$Query$GetCanteenDate + get copyWith => CopyWith$Variables$Query$GetCanteenDate( this, (i) => i, ); @@ -1427,10 +2637,15 @@ class Variables$Query$GetMealPlanForDay { if (identical(this, other)) { return true; } - if (!(other is Variables$Query$GetMealPlanForDay) || + if (!(other is Variables$Query$GetCanteenDate) || runtimeType != other.runtimeType) { return false; } + final l$canteenId = canteenId; + final lOther$canteenId = other.canteenId; + if (l$canteenId != lOther$canteenId) { + return false; + } final l$date = date; final lOther$date = other.date; if (l$date != lOther$date) { @@ -1441,78 +2656,93 @@ class Variables$Query$GetMealPlanForDay { @override int get hashCode { + final l$canteenId = canteenId; final l$date = date; - return Object.hashAll([l$date]); + return Object.hashAll([ + l$canteenId, + l$date, + ]); } } -abstract class CopyWith$Variables$Query$GetMealPlanForDay { - factory CopyWith$Variables$Query$GetMealPlanForDay( - Variables$Query$GetMealPlanForDay instance, - TRes Function(Variables$Query$GetMealPlanForDay) then, - ) = _CopyWithImpl$Variables$Query$GetMealPlanForDay; +abstract class CopyWith$Variables$Query$GetCanteenDate { + factory CopyWith$Variables$Query$GetCanteenDate( + Variables$Query$GetCanteenDate instance, + TRes Function(Variables$Query$GetCanteenDate) then, + ) = _CopyWithImpl$Variables$Query$GetCanteenDate; - factory CopyWith$Variables$Query$GetMealPlanForDay.stub(TRes res) = - _CopyWithStubImpl$Variables$Query$GetMealPlanForDay; + factory CopyWith$Variables$Query$GetCanteenDate.stub(TRes res) = + _CopyWithStubImpl$Variables$Query$GetCanteenDate; - TRes call({String? date}); + TRes call({ + String? canteenId, + String? date, + }); } -class _CopyWithImpl$Variables$Query$GetMealPlanForDay - implements CopyWith$Variables$Query$GetMealPlanForDay { - _CopyWithImpl$Variables$Query$GetMealPlanForDay( +class _CopyWithImpl$Variables$Query$GetCanteenDate + implements CopyWith$Variables$Query$GetCanteenDate { + _CopyWithImpl$Variables$Query$GetCanteenDate( this._instance, this._then, ); - final Variables$Query$GetMealPlanForDay _instance; + final Variables$Query$GetCanteenDate _instance; - final TRes Function(Variables$Query$GetMealPlanForDay) _then; + final TRes Function(Variables$Query$GetCanteenDate) _then; static const _undefined = {}; - TRes call({Object? date = _undefined}) => - _then(Variables$Query$GetMealPlanForDay._({ + TRes call({ + Object? canteenId = _undefined, + Object? date = _undefined, + }) => + _then(Variables$Query$GetCanteenDate._({ ..._instance._$data, + if (canteenId != _undefined && canteenId != null) + 'canteenId': (canteenId as String), if (date != _undefined && date != null) 'date': (date as String), })); } -class _CopyWithStubImpl$Variables$Query$GetMealPlanForDay - implements CopyWith$Variables$Query$GetMealPlanForDay { - _CopyWithStubImpl$Variables$Query$GetMealPlanForDay(this._res); +class _CopyWithStubImpl$Variables$Query$GetCanteenDate + implements CopyWith$Variables$Query$GetCanteenDate { + _CopyWithStubImpl$Variables$Query$GetCanteenDate(this._res); TRes _res; - call({String? date}) => _res; + call({ + String? canteenId, + String? date, + }) => + _res; } -class Query$GetMealPlanForDay { - Query$GetMealPlanForDay({ - required this.getCanteens, +class Query$GetCanteenDate { + Query$GetCanteenDate({ + this.getCanteen, this.$__typename = 'QueryRoot', }); - factory Query$GetMealPlanForDay.fromJson(Map json) { - final l$getCanteens = json['getCanteens']; + factory Query$GetCanteenDate.fromJson(Map json) { + final l$getCanteen = json['getCanteen']; final l$$__typename = json['__typename']; - return Query$GetMealPlanForDay( - getCanteens: (l$getCanteens as List) - .map((e) => Query$GetMealPlanForDay$getCanteens.fromJson( - (e as Map))) - .toList(), + return Query$GetCanteenDate( + getCanteen: l$getCanteen == null + ? null + : Fragment$mealPlan.fromJson((l$getCanteen as Map)), $__typename: (l$$__typename as String), ); } - final List getCanteens; + final Fragment$mealPlan? getCanteen; final String $__typename; Map toJson() { final _resultData = {}; - final l$getCanteens = getCanteens; - _resultData['getCanteens'] = l$getCanteens.map((e) => e.toJson()).toList(); + final l$getCanteen = getCanteen; + _resultData['getCanteen'] = l$getCanteen?.toJson(); final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -1520,10 +2750,10 @@ class Query$GetMealPlanForDay { @override int get hashCode { - final l$getCanteens = getCanteens; + final l$getCanteen = getCanteen; final l$$__typename = $__typename; return Object.hashAll([ - Object.hashAll(l$getCanteens.map((v) => v)), + l$getCanteen, l$$__typename, ]); } @@ -1533,22 +2763,14 @@ class Query$GetMealPlanForDay { if (identical(this, other)) { return true; } - if (!(other is Query$GetMealPlanForDay) || - runtimeType != other.runtimeType) { + if (!(other is Query$GetCanteenDate) || runtimeType != other.runtimeType) { return false; } - final l$getCanteens = getCanteens; - final lOther$getCanteens = other.getCanteens; - if (l$getCanteens.length != lOther$getCanteens.length) { + final l$getCanteen = getCanteen; + final lOther$getCanteen = other.getCanteen; + if (l$getCanteen != lOther$getCanteen) { return false; } - for (int i = 0; i < l$getCanteens.length; i++) { - final l$getCanteens$entry = l$getCanteens[i]; - final lOther$getCanteens$entry = lOther$getCanteens[i]; - if (l$getCanteens$entry != lOther$getCanteens$entry) { - return false; - } - } final l$$__typename = $__typename; final lOther$$__typename = other.$__typename; if (l$$__typename != lOther$$__typename) { @@ -1558,93 +2780,93 @@ class Query$GetMealPlanForDay { } } -extension UtilityExtension$Query$GetMealPlanForDay on Query$GetMealPlanForDay { - CopyWith$Query$GetMealPlanForDay get copyWith => - CopyWith$Query$GetMealPlanForDay( +extension UtilityExtension$Query$GetCanteenDate on Query$GetCanteenDate { + CopyWith$Query$GetCanteenDate get copyWith => + CopyWith$Query$GetCanteenDate( this, (i) => i, ); } -abstract class CopyWith$Query$GetMealPlanForDay { - factory CopyWith$Query$GetMealPlanForDay( - Query$GetMealPlanForDay instance, - TRes Function(Query$GetMealPlanForDay) then, - ) = _CopyWithImpl$Query$GetMealPlanForDay; +abstract class CopyWith$Query$GetCanteenDate { + factory CopyWith$Query$GetCanteenDate( + Query$GetCanteenDate instance, + TRes Function(Query$GetCanteenDate) then, + ) = _CopyWithImpl$Query$GetCanteenDate; - factory CopyWith$Query$GetMealPlanForDay.stub(TRes res) = - _CopyWithStubImpl$Query$GetMealPlanForDay; + factory CopyWith$Query$GetCanteenDate.stub(TRes res) = + _CopyWithStubImpl$Query$GetCanteenDate; TRes call({ - List? getCanteens, + Fragment$mealPlan? getCanteen, String? $__typename, }); - TRes getCanteens( - Iterable Function( - Iterable< - CopyWith$Query$GetMealPlanForDay$getCanteens< - Query$GetMealPlanForDay$getCanteens>>) - _fn); + CopyWith$Fragment$mealPlan get getCanteen; } -class _CopyWithImpl$Query$GetMealPlanForDay - implements CopyWith$Query$GetMealPlanForDay { - _CopyWithImpl$Query$GetMealPlanForDay( +class _CopyWithImpl$Query$GetCanteenDate + implements CopyWith$Query$GetCanteenDate { + _CopyWithImpl$Query$GetCanteenDate( this._instance, this._then, ); - final Query$GetMealPlanForDay _instance; + final Query$GetCanteenDate _instance; - final TRes Function(Query$GetMealPlanForDay) _then; + final TRes Function(Query$GetCanteenDate) _then; static const _undefined = {}; TRes call({ - Object? getCanteens = _undefined, + Object? getCanteen = _undefined, Object? $__typename = _undefined, }) => - _then(Query$GetMealPlanForDay( - getCanteens: getCanteens == _undefined || getCanteens == null - ? _instance.getCanteens - : (getCanteens as List), + _then(Query$GetCanteenDate( + getCanteen: getCanteen == _undefined + ? _instance.getCanteen + : (getCanteen as Fragment$mealPlan?), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String), )); - TRes getCanteens( - Iterable Function( - Iterable< - CopyWith$Query$GetMealPlanForDay$getCanteens< - Query$GetMealPlanForDay$getCanteens>>) - _fn) => - call( - getCanteens: _fn(_instance.getCanteens - .map((e) => CopyWith$Query$GetMealPlanForDay$getCanteens( - e, - (i) => i, - ))).toList()); + CopyWith$Fragment$mealPlan get getCanteen { + final local$getCanteen = _instance.getCanteen; + return local$getCanteen == null + ? CopyWith$Fragment$mealPlan.stub(_then(_instance)) + : CopyWith$Fragment$mealPlan( + local$getCanteen, (e) => call(getCanteen: e)); + } } -class _CopyWithStubImpl$Query$GetMealPlanForDay - implements CopyWith$Query$GetMealPlanForDay { - _CopyWithStubImpl$Query$GetMealPlanForDay(this._res); +class _CopyWithStubImpl$Query$GetCanteenDate + implements CopyWith$Query$GetCanteenDate { + _CopyWithStubImpl$Query$GetCanteenDate(this._res); TRes _res; call({ - List? getCanteens, + Fragment$mealPlan? getCanteen, String? $__typename, }) => _res; - getCanteens(_fn) => _res; + CopyWith$Fragment$mealPlan get getCanteen => + CopyWith$Fragment$mealPlan.stub(_res); } -const documentNodeQueryGetMealPlanForDay = DocumentNode(definitions: [ +const documentNodeQueryGetCanteenDate = DocumentNode(definitions: [ OperationDefinitionNode( type: OperationType.query, - name: NameNode(value: 'GetMealPlanForDay'), + name: NameNode(value: 'GetCanteenDate'), variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'canteenId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ), VariableDefinitionNode( variable: VariableNode(name: NameNode(value: 'date')), type: NamedTypeNode( @@ -1653,97 +2875,24 @@ const documentNodeQueryGetMealPlanForDay = DocumentNode(definitions: [ ), defaultValue: DefaultValueNode(value: null), directives: [], - ) + ), ], directives: [], selectionSet: SelectionSetNode(selections: [ FieldNode( - name: NameNode(value: 'getCanteens'), + name: NameNode(value: 'getCanteen'), alias: null, - arguments: [], + arguments: [ + ArgumentNode( + name: NameNode(value: 'canteenId'), + value: VariableNode(name: NameNode(value: 'canteenId')), + ) + ], directives: [], selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'lines'), - alias: null, - arguments: [], + FragmentSpreadNode( + name: NameNode(value: 'mealPlan'), directives: [], - selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'id'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'name'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'canteen'), - alias: null, - arguments: [], - directives: [], - selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'id'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'name'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: '__typename'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - ]), - ), - FieldNode( - name: NameNode(value: 'meals'), - alias: null, - arguments: [ - ArgumentNode( - name: NameNode(value: 'date'), - value: VariableNode(name: NameNode(value: 'date')), - ) - ], - directives: [], - selectionSet: SelectionSetNode(selections: [ - FragmentSpreadNode( - name: NameNode(value: 'mealInfo'), - directives: [], - ), - FieldNode( - name: NameNode(value: '__typename'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - ]), - ), - FieldNode( - name: NameNode(value: '__typename'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - ]), ), FieldNode( name: NameNode(value: '__typename'), @@ -1763,29 +2912,30 @@ const documentNodeQueryGetMealPlanForDay = DocumentNode(definitions: [ ), ]), ), + fragmentDefinitionmealPlan, fragmentDefinitionmealInfo, ]); -Query$GetMealPlanForDay _parserFn$Query$GetMealPlanForDay( +Query$GetCanteenDate _parserFn$Query$GetCanteenDate( Map data) => - Query$GetMealPlanForDay.fromJson(data); -typedef OnQueryComplete$Query$GetMealPlanForDay = FutureOr Function( + Query$GetCanteenDate.fromJson(data); +typedef OnQueryComplete$Query$GetCanteenDate = FutureOr Function( Map?, - Query$GetMealPlanForDay?, + Query$GetCanteenDate?, ); -class Options$Query$GetMealPlanForDay - extends graphql.QueryOptions { - Options$Query$GetMealPlanForDay({ +class Options$Query$GetCanteenDate + extends graphql.QueryOptions { + Options$Query$GetCanteenDate({ String? operationName, - required Variables$Query$GetMealPlanForDay variables, + required Variables$Query$GetCanteenDate variables, graphql.FetchPolicy? fetchPolicy, graphql.ErrorPolicy? errorPolicy, graphql.CacheRereadPolicy? cacheRereadPolicy, Object? optimisticResult, - Query$GetMealPlanForDay? typedOptimisticResult, + Query$GetCanteenDate? typedOptimisticResult, Duration? pollInterval, graphql.Context? context, - OnQueryComplete$Query$GetMealPlanForDay? onComplete, + OnQueryComplete$Query$GetCanteenDate? onComplete, graphql.OnQueryError? onError, }) : onCompleteWithParsed = onComplete, super( @@ -1801,16 +2951,14 @@ class Options$Query$GetMealPlanForDay ? null : (data) => onComplete( data, - data == null - ? null - : _parserFn$Query$GetMealPlanForDay(data), + data == null ? null : _parserFn$Query$GetCanteenDate(data), ), onError: onError, - document: documentNodeQueryGetMealPlanForDay, - parserFn: _parserFn$Query$GetMealPlanForDay, + document: documentNodeQueryGetCanteenDate, + parserFn: _parserFn$Query$GetCanteenDate, ); - final OnQueryComplete$Query$GetMealPlanForDay? onCompleteWithParsed; + final OnQueryComplete$Query$GetCanteenDate? onCompleteWithParsed; @override List get properties => [ @@ -1821,16 +2969,16 @@ class Options$Query$GetMealPlanForDay ]; } -class WatchOptions$Query$GetMealPlanForDay - extends graphql.WatchQueryOptions { - WatchOptions$Query$GetMealPlanForDay({ +class WatchOptions$Query$GetCanteenDate + extends graphql.WatchQueryOptions { + WatchOptions$Query$GetCanteenDate({ String? operationName, - required Variables$Query$GetMealPlanForDay variables, + required Variables$Query$GetCanteenDate variables, graphql.FetchPolicy? fetchPolicy, graphql.ErrorPolicy? errorPolicy, graphql.CacheRereadPolicy? cacheRereadPolicy, Object? optimisticResult, - Query$GetMealPlanForDay? typedOptimisticResult, + Query$GetCanteenDate? typedOptimisticResult, graphql.Context? context, Duration? pollInterval, bool? eagerlyFetchResults, @@ -1844,78 +2992,75 @@ class WatchOptions$Query$GetMealPlanForDay cacheRereadPolicy: cacheRereadPolicy, optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), context: context, - document: documentNodeQueryGetMealPlanForDay, + document: documentNodeQueryGetCanteenDate, pollInterval: pollInterval, eagerlyFetchResults: eagerlyFetchResults, carryForwardDataOnException: carryForwardDataOnException, fetchResults: fetchResults, - parserFn: _parserFn$Query$GetMealPlanForDay, + parserFn: _parserFn$Query$GetCanteenDate, ); } -class FetchMoreOptions$Query$GetMealPlanForDay - extends graphql.FetchMoreOptions { - FetchMoreOptions$Query$GetMealPlanForDay({ +class FetchMoreOptions$Query$GetCanteenDate extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$GetCanteenDate({ required graphql.UpdateQuery updateQuery, - required Variables$Query$GetMealPlanForDay variables, + required Variables$Query$GetCanteenDate variables, }) : super( updateQuery: updateQuery, variables: variables.toJson(), - document: documentNodeQueryGetMealPlanForDay, + document: documentNodeQueryGetCanteenDate, ); } -extension ClientExtension$Query$GetMealPlanForDay on graphql.GraphQLClient { - Future> query$GetMealPlanForDay( - Options$Query$GetMealPlanForDay options) async => +extension ClientExtension$Query$GetCanteenDate on graphql.GraphQLClient { + Future> query$GetCanteenDate( + Options$Query$GetCanteenDate options) async => await this.query(options); - graphql.ObservableQuery watchQuery$GetMealPlanForDay( - WatchOptions$Query$GetMealPlanForDay options) => + graphql.ObservableQuery watchQuery$GetCanteenDate( + WatchOptions$Query$GetCanteenDate options) => this.watchQuery(options); - void writeQuery$GetMealPlanForDay({ - required Query$GetMealPlanForDay data, - required Variables$Query$GetMealPlanForDay variables, + void writeQuery$GetCanteenDate({ + required Query$GetCanteenDate data, + required Variables$Query$GetCanteenDate variables, bool broadcast = true, }) => this.writeQuery( graphql.Request( operation: - graphql.Operation(document: documentNodeQueryGetMealPlanForDay), + graphql.Operation(document: documentNodeQueryGetCanteenDate), variables: variables.toJson(), ), data: data.toJson(), broadcast: broadcast, ); - Query$GetMealPlanForDay? readQuery$GetMealPlanForDay({ - required Variables$Query$GetMealPlanForDay variables, + Query$GetCanteenDate? readQuery$GetCanteenDate({ + required Variables$Query$GetCanteenDate variables, bool optimistic = true, }) { final result = this.readQuery( graphql.Request( - operation: - graphql.Operation(document: documentNodeQueryGetMealPlanForDay), + operation: graphql.Operation(document: documentNodeQueryGetCanteenDate), variables: variables.toJson(), ), optimistic: optimistic, ); - return result == null ? null : Query$GetMealPlanForDay.fromJson(result); + return result == null ? null : Query$GetCanteenDate.fromJson(result); } } -graphql_flutter.QueryHookResult - useQuery$GetMealPlanForDay(Options$Query$GetMealPlanForDay options) => - graphql_flutter.useQuery(options); -graphql.ObservableQuery - useWatchQuery$GetMealPlanForDay( - WatchOptions$Query$GetMealPlanForDay options) => - graphql_flutter.useWatchQuery(options); +graphql_flutter.QueryHookResult useQuery$GetCanteenDate( + Options$Query$GetCanteenDate options) => + graphql_flutter.useQuery(options); +graphql.ObservableQuery useWatchQuery$GetCanteenDate( + WatchOptions$Query$GetCanteenDate options) => + graphql_flutter.useWatchQuery(options); -class Query$GetMealPlanForDay$Widget - extends graphql_flutter.Query { - Query$GetMealPlanForDay$Widget({ +class Query$GetCanteenDate$Widget + extends graphql_flutter.Query { + Query$GetCanteenDate$Widget({ widgets.Key? key, - required Options$Query$GetMealPlanForDay options, - required graphql_flutter.QueryBuilder builder, + required Options$Query$GetCanteenDate options, + required graphql_flutter.QueryBuilder builder, }) : super( key: key, options: options, @@ -1923,416 +3068,175 @@ class Query$GetMealPlanForDay$Widget ); } -class Query$GetMealPlanForDay$getCanteens { - Query$GetMealPlanForDay$getCanteens({ - required this.lines, - this.$__typename = 'Canteen', - }); - - factory Query$GetMealPlanForDay$getCanteens.fromJson( - Map json) { - final l$lines = json['lines']; - final l$$__typename = json['__typename']; - return Query$GetMealPlanForDay$getCanteens( - lines: (l$lines as List) - .map((e) => Query$GetMealPlanForDay$getCanteens$lines.fromJson( - (e as Map))) - .toList(), - $__typename: (l$$__typename as String), - ); - } - - final List lines; - - final String $__typename; - - Map toJson() { - final _resultData = {}; - final l$lines = lines; - _resultData['lines'] = l$lines.map((e) => e.toJson()).toList(); - final l$$__typename = $__typename; - _resultData['__typename'] = l$$__typename; - return _resultData; - } - - @override - int get hashCode { - final l$lines = lines; - final l$$__typename = $__typename; - return Object.hashAll([ - Object.hashAll(l$lines.map((v) => v)), - l$$__typename, - ]); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (!(other is Query$GetMealPlanForDay$getCanteens) || - runtimeType != other.runtimeType) { - return false; - } - final l$lines = lines; - final lOther$lines = other.lines; - if (l$lines.length != lOther$lines.length) { - return false; - } - for (int i = 0; i < l$lines.length; i++) { - final l$lines$entry = l$lines[i]; - final lOther$lines$entry = lOther$lines[i]; - if (l$lines$entry != lOther$lines$entry) { - return false; - } - } - final l$$__typename = $__typename; - final lOther$$__typename = other.$__typename; - if (l$$__typename != lOther$$__typename) { - return false; - } - return true; - } -} - -extension UtilityExtension$Query$GetMealPlanForDay$getCanteens - on Query$GetMealPlanForDay$getCanteens { - CopyWith$Query$GetMealPlanForDay$getCanteens< - Query$GetMealPlanForDay$getCanteens> - get copyWith => CopyWith$Query$GetMealPlanForDay$getCanteens( - this, - (i) => i, - ); -} - -abstract class CopyWith$Query$GetMealPlanForDay$getCanteens { - factory CopyWith$Query$GetMealPlanForDay$getCanteens( - Query$GetMealPlanForDay$getCanteens instance, - TRes Function(Query$GetMealPlanForDay$getCanteens) then, - ) = _CopyWithImpl$Query$GetMealPlanForDay$getCanteens; - - factory CopyWith$Query$GetMealPlanForDay$getCanteens.stub(TRes res) = - _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens; - - TRes call({ - List? lines, - String? $__typename, - }); - TRes lines( - Iterable Function( - Iterable< - CopyWith$Query$GetMealPlanForDay$getCanteens$lines< - Query$GetMealPlanForDay$getCanteens$lines>>) - _fn); -} - -class _CopyWithImpl$Query$GetMealPlanForDay$getCanteens - implements CopyWith$Query$GetMealPlanForDay$getCanteens { - _CopyWithImpl$Query$GetMealPlanForDay$getCanteens( - this._instance, - this._then, - ); - - final Query$GetMealPlanForDay$getCanteens _instance; - - final TRes Function(Query$GetMealPlanForDay$getCanteens) _then; - - static const _undefined = {}; - - TRes call({ - Object? lines = _undefined, - Object? $__typename = _undefined, - }) => - _then(Query$GetMealPlanForDay$getCanteens( - lines: lines == _undefined || lines == null - ? _instance.lines - : (lines as List), - $__typename: $__typename == _undefined || $__typename == null - ? _instance.$__typename - : ($__typename as String), - )); - TRes lines( - Iterable Function( - Iterable< - CopyWith$Query$GetMealPlanForDay$getCanteens$lines< - Query$GetMealPlanForDay$getCanteens$lines>>) - _fn) => - call( - lines: _fn(_instance.lines - .map((e) => CopyWith$Query$GetMealPlanForDay$getCanteens$lines( - e, - (i) => i, - ))).toList()); -} - -class _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens - implements CopyWith$Query$GetMealPlanForDay$getCanteens { - _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens(this._res); - - TRes _res; - - call({ - List? lines, - String? $__typename, +class Variables$Query$GetMeal { + factory Variables$Query$GetMeal({ + required String date, + required String mealId, + required String lineId, }) => - _res; - lines(_fn) => _res; -} + Variables$Query$GetMeal._({ + r'date': date, + r'mealId': mealId, + r'lineId': lineId, + }); -class Query$GetMealPlanForDay$getCanteens$lines { - Query$GetMealPlanForDay$getCanteens$lines({ - required this.id, - required this.name, - required this.canteen, - this.meals, - this.$__typename = 'Line', - }); + Variables$Query$GetMeal._(this._$data); - factory Query$GetMealPlanForDay$getCanteens$lines.fromJson( - Map json) { - final l$id = json['id']; - final l$name = json['name']; - final l$canteen = json['canteen']; - final l$meals = json['meals']; - final l$$__typename = json['__typename']; - return Query$GetMealPlanForDay$getCanteens$lines( - id: (l$id as String), - name: (l$name as String), - canteen: Query$GetMealPlanForDay$getCanteens$lines$canteen.fromJson( - (l$canteen as Map)), - meals: (l$meals as List?) - ?.map((e) => Fragment$mealInfo.fromJson((e as Map))) - .toList(), - $__typename: (l$$__typename as String), - ); + factory Variables$Query$GetMeal.fromJson(Map data) { + final result$data = {}; + final l$date = data['date']; + result$data['date'] = (l$date as String); + final l$mealId = data['mealId']; + result$data['mealId'] = (l$mealId as String); + final l$lineId = data['lineId']; + result$data['lineId'] = (l$lineId as String); + return Variables$Query$GetMeal._(result$data); } - final String id; - - final String name; - - final Query$GetMealPlanForDay$getCanteens$lines$canteen canteen; - - final List? meals; - - final String $__typename; + Map _$data; + String get date => (_$data['date'] as String); + String get mealId => (_$data['mealId'] as String); + String get lineId => (_$data['lineId'] as String); Map toJson() { - final _resultData = {}; - final l$id = id; - _resultData['id'] = l$id; - final l$name = name; - _resultData['name'] = l$name; - final l$canteen = canteen; - _resultData['canteen'] = l$canteen.toJson(); - final l$meals = meals; - _resultData['meals'] = l$meals?.map((e) => e.toJson()).toList(); - final l$$__typename = $__typename; - _resultData['__typename'] = l$$__typename; - return _resultData; - } - - @override - int get hashCode { - final l$id = id; - final l$name = name; - final l$canteen = canteen; - final l$meals = meals; - final l$$__typename = $__typename; - return Object.hashAll([ - l$id, - l$name, - l$canteen, - l$meals == null ? null : Object.hashAll(l$meals.map((v) => v)), - l$$__typename, - ]); + final result$data = {}; + final l$date = date; + result$data['date'] = l$date; + final l$mealId = mealId; + result$data['mealId'] = l$mealId; + final l$lineId = lineId; + result$data['lineId'] = l$lineId; + return result$data; } + CopyWith$Variables$Query$GetMeal get copyWith => + CopyWith$Variables$Query$GetMeal( + this, + (i) => i, + ); @override bool operator ==(Object other) { if (identical(this, other)) { return true; } - if (!(other is Query$GetMealPlanForDay$getCanteens$lines) || + if (!(other is Variables$Query$GetMeal) || runtimeType != other.runtimeType) { return false; } - final l$id = id; - final lOther$id = other.id; - if (l$id != lOther$id) { - return false; - } - final l$name = name; - final lOther$name = other.name; - if (l$name != lOther$name) { - return false; - } - final l$canteen = canteen; - final lOther$canteen = other.canteen; - if (l$canteen != lOther$canteen) { + final l$date = date; + final lOther$date = other.date; + if (l$date != lOther$date) { return false; } - final l$meals = meals; - final lOther$meals = other.meals; - if (l$meals != null && lOther$meals != null) { - if (l$meals.length != lOther$meals.length) { - return false; - } - for (int i = 0; i < l$meals.length; i++) { - final l$meals$entry = l$meals[i]; - final lOther$meals$entry = lOther$meals[i]; - if (l$meals$entry != lOther$meals$entry) { - return false; - } - } - } else if (l$meals != lOther$meals) { + final l$mealId = mealId; + final lOther$mealId = other.mealId; + if (l$mealId != lOther$mealId) { return false; } - final l$$__typename = $__typename; - final lOther$$__typename = other.$__typename; - if (l$$__typename != lOther$$__typename) { + final l$lineId = lineId; + final lOther$lineId = other.lineId; + if (l$lineId != lOther$lineId) { return false; } return true; } -} -extension UtilityExtension$Query$GetMealPlanForDay$getCanteens$lines - on Query$GetMealPlanForDay$getCanteens$lines { - CopyWith$Query$GetMealPlanForDay$getCanteens$lines< - Query$GetMealPlanForDay$getCanteens$lines> - get copyWith => CopyWith$Query$GetMealPlanForDay$getCanteens$lines( - this, - (i) => i, - ); + @override + int get hashCode { + final l$date = date; + final l$mealId = mealId; + final l$lineId = lineId; + return Object.hashAll([ + l$date, + l$mealId, + l$lineId, + ]); + } } -abstract class CopyWith$Query$GetMealPlanForDay$getCanteens$lines { - factory CopyWith$Query$GetMealPlanForDay$getCanteens$lines( - Query$GetMealPlanForDay$getCanteens$lines instance, - TRes Function(Query$GetMealPlanForDay$getCanteens$lines) then, - ) = _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines; +abstract class CopyWith$Variables$Query$GetMeal { + factory CopyWith$Variables$Query$GetMeal( + Variables$Query$GetMeal instance, + TRes Function(Variables$Query$GetMeal) then, + ) = _CopyWithImpl$Variables$Query$GetMeal; - factory CopyWith$Query$GetMealPlanForDay$getCanteens$lines.stub(TRes res) = - _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines; + factory CopyWith$Variables$Query$GetMeal.stub(TRes res) = + _CopyWithStubImpl$Variables$Query$GetMeal; TRes call({ - String? id, - String? name, - Query$GetMealPlanForDay$getCanteens$lines$canteen? canteen, - List? meals, - String? $__typename, + String? date, + String? mealId, + String? lineId, }); - CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen get canteen; - TRes meals( - Iterable? Function( - Iterable>?) - _fn); } -class _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines - implements CopyWith$Query$GetMealPlanForDay$getCanteens$lines { - _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines( +class _CopyWithImpl$Variables$Query$GetMeal + implements CopyWith$Variables$Query$GetMeal { + _CopyWithImpl$Variables$Query$GetMeal( this._instance, this._then, ); - final Query$GetMealPlanForDay$getCanteens$lines _instance; + final Variables$Query$GetMeal _instance; - final TRes Function(Query$GetMealPlanForDay$getCanteens$lines) _then; + final TRes Function(Variables$Query$GetMeal) _then; static const _undefined = {}; TRes call({ - Object? id = _undefined, - Object? name = _undefined, - Object? canteen = _undefined, - Object? meals = _undefined, - Object? $__typename = _undefined, + Object? date = _undefined, + Object? mealId = _undefined, + Object? lineId = _undefined, }) => - _then(Query$GetMealPlanForDay$getCanteens$lines( - id: id == _undefined || id == null ? _instance.id : (id as String), - name: name == _undefined || name == null - ? _instance.name - : (name as String), - canteen: canteen == _undefined || canteen == null - ? _instance.canteen - : (canteen as Query$GetMealPlanForDay$getCanteens$lines$canteen), - meals: meals == _undefined - ? _instance.meals - : (meals as List?), - $__typename: $__typename == _undefined || $__typename == null - ? _instance.$__typename - : ($__typename as String), - )); - CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen get canteen { - final local$canteen = _instance.canteen; - return CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen( - local$canteen, (e) => call(canteen: e)); - } - - TRes meals( - Iterable? Function( - Iterable>?) - _fn) => - call( - meals: _fn(_instance.meals?.map((e) => CopyWith$Fragment$mealInfo( - e, - (i) => i, - )))?.toList()); + _then(Variables$Query$GetMeal._({ + ..._instance._$data, + if (date != _undefined && date != null) 'date': (date as String), + if (mealId != _undefined && mealId != null) + 'mealId': (mealId as String), + if (lineId != _undefined && lineId != null) + 'lineId': (lineId as String), + })); } -class _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines - implements CopyWith$Query$GetMealPlanForDay$getCanteens$lines { - _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines(this._res); +class _CopyWithStubImpl$Variables$Query$GetMeal + implements CopyWith$Variables$Query$GetMeal { + _CopyWithStubImpl$Variables$Query$GetMeal(this._res); TRes _res; call({ - String? id, - String? name, - Query$GetMealPlanForDay$getCanteens$lines$canteen? canteen, - List? meals, - String? $__typename, + String? date, + String? mealId, + String? lineId, }) => _res; - CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen - get canteen => - CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen.stub(_res); - meals(_fn) => _res; } -class Query$GetMealPlanForDay$getCanteens$lines$canteen { - Query$GetMealPlanForDay$getCanteens$lines$canteen({ - required this.id, - required this.name, - this.$__typename = 'Canteen', +class Query$GetMeal { + Query$GetMeal({ + this.getMeal, + this.$__typename = 'QueryRoot', }); - factory Query$GetMealPlanForDay$getCanteens$lines$canteen.fromJson( - Map json) { - final l$id = json['id']; - final l$name = json['name']; + factory Query$GetMeal.fromJson(Map json) { + final l$getMeal = json['getMeal']; final l$$__typename = json['__typename']; - return Query$GetMealPlanForDay$getCanteens$lines$canteen( - id: (l$id as String), - name: (l$name as String), + return Query$GetMeal( + getMeal: l$getMeal == null + ? null + : Fragment$mealInfo.fromJson((l$getMeal as Map)), $__typename: (l$$__typename as String), ); } - final String id; - - final String name; + final Fragment$mealInfo? getMeal; final String $__typename; Map toJson() { final _resultData = {}; - final l$id = id; - _resultData['id'] = l$id; - final l$name = name; - _resultData['name'] = l$name; + final l$getMeal = getMeal; + _resultData['getMeal'] = l$getMeal?.toJson(); final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -2340,12 +3244,10 @@ class Query$GetMealPlanForDay$getCanteens$lines$canteen { @override int get hashCode { - final l$id = id; - final l$name = name; + final l$getMeal = getMeal; final l$$__typename = $__typename; return Object.hashAll([ - l$id, - l$name, + l$getMeal, l$$__typename, ]); } @@ -2355,18 +3257,12 @@ class Query$GetMealPlanForDay$getCanteens$lines$canteen { if (identical(this, other)) { return true; } - if (!(other is Query$GetMealPlanForDay$getCanteens$lines$canteen) || - runtimeType != other.runtimeType) { - return false; - } - final l$id = id; - final lOther$id = other.id; - if (l$id != lOther$id) { + if (!(other is Query$GetMeal) || runtimeType != other.runtimeType) { return false; } - final l$name = name; - final lOther$name = other.name; - if (l$name != lOther$name) { + final l$getMeal = getMeal; + final lOther$getMeal = other.getMeal; + if (l$getMeal != lOther$getMeal) { return false; } final l$$__typename = $__typename; @@ -2378,77 +3274,300 @@ class Query$GetMealPlanForDay$getCanteens$lines$canteen { } } -extension UtilityExtension$Query$GetMealPlanForDay$getCanteens$lines$canteen - on Query$GetMealPlanForDay$getCanteens$lines$canteen { - CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen< - Query$GetMealPlanForDay$getCanteens$lines$canteen> - get copyWith => - CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen( - this, - (i) => i, - ); +extension UtilityExtension$Query$GetMeal on Query$GetMeal { + CopyWith$Query$GetMeal get copyWith => CopyWith$Query$GetMeal( + this, + (i) => i, + ); } -abstract class CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen< - TRes> { - factory CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen( - Query$GetMealPlanForDay$getCanteens$lines$canteen instance, - TRes Function(Query$GetMealPlanForDay$getCanteens$lines$canteen) then, - ) = _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen; +abstract class CopyWith$Query$GetMeal { + factory CopyWith$Query$GetMeal( + Query$GetMeal instance, + TRes Function(Query$GetMeal) then, + ) = _CopyWithImpl$Query$GetMeal; - factory CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen.stub( - TRes res) = - _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen; + factory CopyWith$Query$GetMeal.stub(TRes res) = + _CopyWithStubImpl$Query$GetMeal; TRes call({ - String? id, - String? name, + Fragment$mealInfo? getMeal, String? $__typename, }); + CopyWith$Fragment$mealInfo get getMeal; } -class _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen - implements - CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen { - _CopyWithImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen( +class _CopyWithImpl$Query$GetMeal + implements CopyWith$Query$GetMeal { + _CopyWithImpl$Query$GetMeal( this._instance, this._then, ); - final Query$GetMealPlanForDay$getCanteens$lines$canteen _instance; + final Query$GetMeal _instance; - final TRes Function(Query$GetMealPlanForDay$getCanteens$lines$canteen) _then; + final TRes Function(Query$GetMeal) _then; static const _undefined = {}; TRes call({ - Object? id = _undefined, - Object? name = _undefined, + Object? getMeal = _undefined, Object? $__typename = _undefined, }) => - _then(Query$GetMealPlanForDay$getCanteens$lines$canteen( - id: id == _undefined || id == null ? _instance.id : (id as String), - name: name == _undefined || name == null - ? _instance.name - : (name as String), + _then(Query$GetMeal( + getMeal: getMeal == _undefined + ? _instance.getMeal + : (getMeal as Fragment$mealInfo?), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String), )); + CopyWith$Fragment$mealInfo get getMeal { + final local$getMeal = _instance.getMeal; + return local$getMeal == null + ? CopyWith$Fragment$mealInfo.stub(_then(_instance)) + : CopyWith$Fragment$mealInfo(local$getMeal, (e) => call(getMeal: e)); + } } -class _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen - implements - CopyWith$Query$GetMealPlanForDay$getCanteens$lines$canteen { - _CopyWithStubImpl$Query$GetMealPlanForDay$getCanteens$lines$canteen( - this._res); +class _CopyWithStubImpl$Query$GetMeal + implements CopyWith$Query$GetMeal { + _CopyWithStubImpl$Query$GetMeal(this._res); TRes _res; call({ - String? id, - String? name, + Fragment$mealInfo? getMeal, String? $__typename, }) => _res; + CopyWith$Fragment$mealInfo get getMeal => + CopyWith$Fragment$mealInfo.stub(_res); +} + +const documentNodeQueryGetMeal = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'GetMeal'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'date')), + type: NamedTypeNode( + name: NameNode(value: 'NaiveDate'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ), + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'mealId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ), + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'lineId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ), + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'getMeal'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'date'), + value: VariableNode(name: NameNode(value: 'date')), + ), + ArgumentNode( + name: NameNode(value: 'mealId'), + value: VariableNode(name: NameNode(value: 'mealId')), + ), + ArgumentNode( + name: NameNode(value: 'lineId'), + value: VariableNode(name: NameNode(value: 'lineId')), + ), + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'mealInfo'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionmealInfo, +]); +Query$GetMeal _parserFn$Query$GetMeal(Map data) => + Query$GetMeal.fromJson(data); +typedef OnQueryComplete$Query$GetMeal = FutureOr Function( + Map?, + Query$GetMeal?, +); + +class Options$Query$GetMeal extends graphql.QueryOptions { + Options$Query$GetMeal({ + String? operationName, + required Variables$Query$GetMeal variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetMeal? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$GetMeal? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$GetMeal(data), + ), + onError: onError, + document: documentNodeQueryGetMeal, + parserFn: _parserFn$Query$GetMeal, + ); + + final OnQueryComplete$Query$GetMeal? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$GetMeal + extends graphql.WatchQueryOptions { + WatchOptions$Query$GetMeal({ + String? operationName, + required Variables$Query$GetMeal variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetMeal? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryGetMeal, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$GetMeal, + ); +} + +class FetchMoreOptions$Query$GetMeal extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$GetMeal({ + required graphql.UpdateQuery updateQuery, + required Variables$Query$GetMeal variables, + }) : super( + updateQuery: updateQuery, + variables: variables.toJson(), + document: documentNodeQueryGetMeal, + ); +} + +extension ClientExtension$Query$GetMeal on graphql.GraphQLClient { + Future> query$GetMeal( + Options$Query$GetMeal options) async => + await this.query(options); + graphql.ObservableQuery watchQuery$GetMeal( + WatchOptions$Query$GetMeal options) => + this.watchQuery(options); + void writeQuery$GetMeal({ + required Query$GetMeal data, + required Variables$Query$GetMeal variables, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryGetMeal), + variables: variables.toJson(), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Query$GetMeal? readQuery$GetMeal({ + required Variables$Query$GetMeal variables, + bool optimistic = true, + }) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryGetMeal), + variables: variables.toJson(), + ), + optimistic: optimistic, + ); + return result == null ? null : Query$GetMeal.fromJson(result); + } +} + +graphql_flutter.QueryHookResult useQuery$GetMeal( + Options$Query$GetMeal options) => + graphql_flutter.useQuery(options); +graphql.ObservableQuery useWatchQuery$GetMeal( + WatchOptions$Query$GetMeal options) => + graphql_flutter.useWatchQuery(options); + +class Query$GetMeal$Widget extends graphql_flutter.Query { + Query$GetMeal$Widget({ + widgets.Key? key, + required Options$Query$GetMeal options, + required graphql_flutter.QueryBuilder builder, + }) : super( + key: key, + options: options, + builder: builder, + ); } diff --git a/app/lib/view_model/repository/interface/IServerAccess.dart b/app/lib/view_model/repository/interface/IServerAccess.dart index a7250fa8..c705eb5d 100644 --- a/app/lib/view_model/repository/interface/IServerAccess.dart +++ b/app/lib/view_model/repository/interface/IServerAccess.dart @@ -1,4 +1,5 @@ import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import '../data_classes/meal/ImageData.dart'; import '../data_classes/meal/Meal.dart'; @@ -21,7 +22,8 @@ abstract class IServerAccess { /// This method returns the meal with the committed id. /// @param id The id of the meal /// @return The meal with the committed id or an error - Future> getMealFromId(String id); + // TODO is it clever to always pass the whole object when only the ids are needed? + Future> getMealFromId(Meal meal, Line line, DateTime date); /// This method updates the rating of the committed meal on the server. /// @param rating The new rating of the meal diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index bb147c7f..671ae008 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -3,17 +3,19 @@ import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/meal/Price.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:intl/intl.dart'; void main() async { final GraphQlServerAccess serverAccess = - GraphQlServerAccess("1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); + GraphQlServerAccess("1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); test('environment endpoint defined', () { expect(const String.fromEnvironment('API_URL').isNotEmpty, true, reason: - "define secret file with `--dart-define-from-file=`, see README"); + "define secret file with `--dart-define-from-file=`, see README"); }); test('remove downvote', () async { @@ -89,4 +91,20 @@ void main() async { price: Price(student: 22, employee: 33, pupil: 11, guest: 123))); expect(deleted, true); }); + + test('date format', () { + final dateFormat = DateFormat(GraphQlServerAccess.dateFormatPattern); + expect(dateFormat.format(DateTime(2022, 3, 1)), "2022-03-01"); + }); + + + test('update all', () async { + var result = await serverAccess.updateAll(); + expect(result.toString(), true); // TODO how to get out of result? + }); + + test('update canteen', () async { + var result = await serverAccess.updateCanteen(Canteen(id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", name: "Canteen"), DateTime(2020, 11, 1)); + expect(result.toString(), true); // TODO how to get out of result? + }); } From 82bb829c402782100d9ea236ce41270e2a6a4460 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Tue, 11 Jul 2023 18:23:00 +0200 Subject: [PATCH 033/184] query tests --- .../model/api_server/GraphQlServerAccess.dart | 4 +- .../api_server/GraphQlServerAccess_test.dart | 45 ++++++++++++++++--- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index 4bd2567c..e33401ac 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -12,6 +12,7 @@ import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; +import 'package:app/view_model/repository/error_handling/NoMealException.dart'; import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; @@ -144,8 +145,9 @@ class GraphQlServerAccess implements IServerAccess { final meal_data = result.parsedData?.getMeal; if (meal_data == null) { - return Failure(Exception("")); + return Failure(NoMealException("Could not request meal from api: ${result.exception}")); } + return Success(_convertMeal(meal_data)); } diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index 671ae008..c690a306 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -4,18 +4,20 @@ import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/meal/Price.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; +import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:intl/intl.dart'; void main() async { final GraphQlServerAccess serverAccess = - GraphQlServerAccess("1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); + GraphQlServerAccess("1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); test('environment endpoint defined', () { expect(const String.fromEnvironment('API_URL').isNotEmpty, true, reason: - "define secret file with `--dart-define-from-file=`, see README"); + "define secret file with `--dart-define-from-file=`, see README"); }); test('remove downvote', () async { @@ -97,14 +99,45 @@ void main() async { expect(dateFormat.format(DateTime(2022, 3, 1)), "2022-03-01"); }); - test('update all', () async { var result = await serverAccess.updateAll(); - expect(result.toString(), true); // TODO how to get out of result? + + var res = switch (result) { + Success(value: final mealplan) => expect(mealplan.toString(), ""), + Failure(value: final exception) => 1, + }; }); test('update canteen', () async { - var result = await serverAccess.updateCanteen(Canteen(id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", name: "Canteen"), DateTime(2020, 11, 1)); - expect(result.toString(), true); // TODO how to get out of result? + var result = await serverAccess.updateCanteen( + Canteen(id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", name: "Canteen"), + DateTime(2020, 11, 1)); + + var res = switch (result) { + Success(value: final mealplan) => expect(mealplan.toString(), ""), + Failure(value: final exception) => 1, + }; + }); + + test('meal from id', () async { + var result = await serverAccess.getMealFromId( + Meal( + id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", + name: "Best Meal", + foodType: FoodType.porkAw, + price: Price(student: 212, employee: 32, pupil: 123, guest: 342)), + Line( + id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", + name: "Line name", + canteen: Canteen( + id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", + name: "Canteen name"), + position: 22), + DateTime(2020, 11, 2)); + + var res = switch (result) { + Success(value: final mealplan) => expect(mealplan.toString(), ""), + Failure(value: final exception) => 1, + }; }); } From d50b2f1747cea73ca2cf72a824d0b7b6832824fe Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Tue, 11 Jul 2023 19:24:51 +0200 Subject: [PATCH 034/184] more conversion functions --- .../model/api_server/GraphQlServerAccess.dart | 146 ++- .../model/api_server/requests/querys.graphql | 22 +- .../api_server/requests/querys.graphql.dart | 1026 ++++++++++++----- .../data_classes/meal/Allergen.dart | 4 +- .../api_server/GraphQlServerAccess_test.dart | 10 +- 5 files changed, 888 insertions(+), 320 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index e33401ac..a4b6d0e7 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -1,10 +1,14 @@ import 'package:app/model/api_server/requests/querys.graphql.dart'; import 'package:app/model/api_server/requests/schema.graphql.dart'; +import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; +import 'package:app/view_model/repository/data_classes/meal/Additive.dart'; +import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/meal/Price.dart'; +import 'package:app/view_model/repository/data_classes/meal/Side.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; @@ -15,6 +19,7 @@ import 'package:app/view_model/repository/data_classes/settings/ReportCategory.d import 'package:app/view_model/repository/error_handling/NoMealException.dart'; import 'package:app/view_model/repository/error_handling/Result.dart'; +import 'package:flutter/cupertino.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:intl/intl.dart'; @@ -129,6 +134,11 @@ class GraphQlServerAccess implements IServerAccess { date: _dateFormat.format(date)))); final parsedData = result.parsedData; + final exception = result.exception; + if (exception != null) { + return Failure(exception); + } + final mealPlan = _convertMealPlan(parsedData?.getCanteens ?? [], date); completeList.addAll(mealPlan); @@ -142,13 +152,18 @@ class GraphQlServerAccess implements IServerAccess { final result = await _client.query$GetMeal(Options$Query$GetMeal( variables: Variables$Query$GetMeal( date: _dateFormat.format(date), mealId: meal.id, lineId: line.id))); - final meal_data = result.parsedData?.getMeal; + final mealData = result.parsedData?.getMeal; + final exception = result.exception; + if (exception != null) { + return Failure(exception); + } - if (meal_data == null) { - return Failure(NoMealException("Could not request meal from api: ${result.exception}")); + if (mealData == null) { + return Failure(NoMealException( + "Could not request meal from api: ${result.exception}")); } - return Success(_convertMeal(meal_data)); + return Success(_convertMeal(mealData)); } @override @@ -159,10 +174,14 @@ class GraphQlServerAccess implements IServerAccess { variables: Variables$Query$GetCanteenDate( canteenId: canteen.id, date: _dateFormat.format(date)))); final parsedData = result.parsedData; + final exception = result.exception; + if (exception != null) { + return Failure(exception); + } final mealPlan = _convertMealPlan([parsedData?.getCanteen].nonNulls.toList(), date); - return Success(mealPlan); // TODO when error? + return Success(mealPlan); } } @@ -196,10 +215,115 @@ List _convertMealPlan( Meal _convertMeal(Fragment$mealInfo meal) { return Meal( - id: meal.id, - name: meal.name, - foodType: _convertMealType(meal.mealType), - price: _convertPrice(meal.price)); + id: meal.id, + name: meal.name, + foodType: _convertMealType(meal.mealType), + price: _convertPrice(meal.price), + additives: meal.additives.map((e) => _convertAdditive(e)).nonNulls.toList(), + allergens: + meal.allergens.map((e) => _convertAllergen(e)).nonNulls.toList(), + averageRating: meal.ratings.averageRating, + individualRating: meal.ratings.personalRating, + numberOfRatings: meal.ratings.ratingsCount, + lastServed: _convertDate(meal.statistics.lastServed), + nextServed: _convertDate(meal.statistics.nextServed), + relativeFrequency: _specifyFrequency(meal.statistics.relativeFrequency), + images: meal.images.map((e) => _convertImage(e)).toList(), + sides: meal.sides.map((e) => _convertSide(e)).toList(), + ); +} + +Frequency _specifyFrequency(double frequency) { + throw UnsupportedError("message"); +} + +DateTime? _convertDate(String? date) { + final format = DateFormat(GraphQlServerAccess.dateFormatPattern); + try { + return format.parse(date ?? ""); + } catch (e) { + return null; + } +} + +Side _convertSide(Fragment$mealInfo$sides e) { + return Side( + id: e.id, + name: e.name, + foodType: _convertMealType(e.mealType), + price: _convertPrice(e.price), + allergens: e.allergens.map((e) => _convertAllergen(e)).nonNulls.toList(), + additives: e.additives.map((e) => _convertAdditive(e)).nonNulls.toList()); +} + +ImageData _convertImage(Fragment$mealInfo$images e) { + return ImageData( + id: e.id, + url: e.url, + imageRank: e.rank, + positiveRating: e.upvotes, + negativeRating: e.downvotes, + individualRating: e.personalUpvote + ? 1 + : e.personalDownvote + ? -1 + : 0); +} + +Allergen? _convertAllergen(Enum$Allergen e) { + return switch (e) { + Enum$Allergen.CA => Allergen.ca, + Enum$Allergen.DI => Allergen.di, + Enum$Allergen.EI => Allergen.ei, + Enum$Allergen.ER => Allergen.er, + Enum$Allergen.FI => Allergen.fi, + Enum$Allergen.GE => Allergen.ge, + Enum$Allergen.HF => Allergen.hf, + Enum$Allergen.HA => Allergen.ha, + Enum$Allergen.KA => Allergen.ka, + Enum$Allergen.KR => Allergen.kr, + Enum$Allergen.LU => Allergen.lu, + Enum$Allergen.MA => Allergen.ma, + Enum$Allergen.ML => Allergen.ml, + Enum$Allergen.PA => Allergen.pa, + Enum$Allergen.PE => Allergen.pe, + Enum$Allergen.PI => Allergen.pi, + Enum$Allergen.QU => Allergen.qu, + Enum$Allergen.RO => Allergen.ro, + Enum$Allergen.SA => Allergen.sa, + Enum$Allergen.SE => Allergen.se, + Enum$Allergen.SF => Allergen.sf, + Enum$Allergen.SN => Allergen.sn, + Enum$Allergen.SO => Allergen.so, + Enum$Allergen.WA => Allergen.wa, + Enum$Allergen.WE => Allergen.we, + Enum$Allergen.WT => Allergen.wt, + Enum$Allergen.LA => Allergen.la, + Enum$Allergen.GL => Allergen.gl, + Enum$Allergen.$unknown => null, + }; +} + +Additive? _convertAdditive(Enum$Additive e) { + return switch (e) { + Enum$Additive.COLORANT => Additive.colorant, + Enum$Additive.PRESERVING_AGENTS => Additive.preservingAgents, + Enum$Additive.ANTIOXIDANT_AGENTS => Additive.antioxidantAgents, + Enum$Additive.FLAVOUR_ENHANCER => Additive.flavourEnhancer, + Enum$Additive.PHOSPHATE => Additive.phosphate, + Enum$Additive.SURFACE_WAXED => Additive.surfaceWaxed, + Enum$Additive.SULPHUR => Additive.sulphur, + Enum$Additive.ARTIFICIALLY_BLACKENED_OLIVES => + Additive.artificiallyBlackenedOlives, + Enum$Additive.SWEETENER => Additive.sweetener, + Enum$Additive.LAXATIVE_IF_OVERUSED => Additive.laxativeIfOverused, + Enum$Additive.PHENYLALANINE => Additive.phenylalanine, + Enum$Additive.ALCOHOL => Additive.alcohol, + Enum$Additive.PRESSED_MEET => Additive.pressedMeat, + Enum$Additive.GLAZING_WITH_CACAO => Additive.glazingWithCacao, + Enum$Additive.PRESSED_FISH => Additive.pressedFish, + Enum$Additive.$unknown => null, + }; } FoodType _convertMealType(Enum$MealType mealType) { @@ -225,7 +349,7 @@ FoodType _convertMealType(Enum$MealType mealType) { } } -Price _convertPrice(Fragment$mealInfo$price price) { +Price _convertPrice(Fragment$price price) { return Price( student: price.student, employee: price.employee, @@ -237,8 +361,6 @@ Canteen _convertCanteen(Fragment$mealPlan$lines$canteen canteen) { return Canteen(id: canteen.id, name: canteen.name); } -// ---------------------- utility functions ---------------------- - Enum$ReportReason _convertToReportReason(ReportCategory reportReason) { switch (reportReason) { case ReportCategory.offensive: diff --git a/app/lib/model/api_server/requests/querys.graphql b/app/lib/model/api_server/requests/querys.graphql index 54ebd97a..e1101a08 100644 --- a/app/lib/model/api_server/requests/querys.graphql +++ b/app/lib/model/api_server/requests/querys.graphql @@ -35,10 +35,7 @@ fragment mealInfo on Meal { name mealType price { - employee - guest - pupil - student + ...price } allergens additives @@ -61,4 +58,21 @@ fragment mealInfo on Meal { downvotes upvotes } + sides { + id + name + additives + allergens + price { + ...price + } + mealType + } +} + +fragment price on Price { + employee + guest + pupil + student } \ No newline at end of file diff --git a/app/lib/model/api_server/requests/querys.graphql.dart b/app/lib/model/api_server/requests/querys.graphql.dart index 4d2abde3..e8948cc9 100644 --- a/app/lib/model/api_server/requests/querys.graphql.dart +++ b/app/lib/model/api_server/requests/querys.graphql.dart @@ -354,6 +354,7 @@ const fragmentDefinitionmealPlan = FragmentDefinitionNode( const documentNodeFragmentmealPlan = DocumentNode(definitions: [ fragmentDefinitionmealPlan, fragmentDefinitionmealInfo, + fragmentDefinitionprice, ]); extension ClientExtension$Fragment$mealPlan on graphql.GraphQLClient { @@ -768,6 +769,7 @@ class Fragment$mealInfo { required this.statistics, required this.ratings, required this.images, + required this.sides, this.$__typename = 'Meal', }); @@ -781,13 +783,13 @@ class Fragment$mealInfo { final l$statistics = json['statistics']; final l$ratings = json['ratings']; final l$images = json['images']; + final l$sides = json['sides']; final l$$__typename = json['__typename']; return Fragment$mealInfo( id: (l$id as String), name: (l$name as String), mealType: fromJson$Enum$MealType((l$mealType as String)), - price: - Fragment$mealInfo$price.fromJson((l$price as Map)), + price: Fragment$price.fromJson((l$price as Map)), allergens: (l$allergens as List) .map((e) => fromJson$Enum$Allergen((e as String))) .toList(), @@ -802,6 +804,10 @@ class Fragment$mealInfo { .map((e) => Fragment$mealInfo$images.fromJson((e as Map))) .toList(), + sides: (l$sides as List) + .map((e) => + Fragment$mealInfo$sides.fromJson((e as Map))) + .toList(), $__typename: (l$$__typename as String), ); } @@ -812,7 +818,7 @@ class Fragment$mealInfo { final Enum$MealType mealType; - final Fragment$mealInfo$price price; + final Fragment$price price; final List allergens; @@ -824,6 +830,8 @@ class Fragment$mealInfo { final List images; + final List sides; + final String $__typename; Map toJson() { @@ -848,6 +856,8 @@ class Fragment$mealInfo { _resultData['ratings'] = l$ratings.toJson(); final l$images = images; _resultData['images'] = l$images.map((e) => e.toJson()).toList(); + final l$sides = sides; + _resultData['sides'] = l$sides.map((e) => e.toJson()).toList(); final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -864,6 +874,7 @@ class Fragment$mealInfo { final l$statistics = statistics; final l$ratings = ratings; final l$images = images; + final l$sides = sides; final l$$__typename = $__typename; return Object.hashAll([ l$id, @@ -875,6 +886,7 @@ class Fragment$mealInfo { l$statistics, l$ratings, Object.hashAll(l$images.map((v) => v)), + Object.hashAll(l$sides.map((v) => v)), l$$__typename, ]); } @@ -953,6 +965,18 @@ class Fragment$mealInfo { return false; } } + final l$sides = sides; + final lOther$sides = other.sides; + if (l$sides.length != lOther$sides.length) { + return false; + } + for (int i = 0; i < l$sides.length; i++) { + final l$sides$entry = l$sides[i]; + final lOther$sides$entry = lOther$sides[i]; + if (l$sides$entry != lOther$sides$entry) { + return false; + } + } final l$$__typename = $__typename; final lOther$$__typename = other.$__typename; if (l$$__typename != lOther$$__typename) { @@ -983,15 +1007,16 @@ abstract class CopyWith$Fragment$mealInfo { String? id, String? name, Enum$MealType? mealType, - Fragment$mealInfo$price? price, + Fragment$price? price, List? allergens, List? additives, Fragment$mealInfo$statistics? statistics, Fragment$mealInfo$ratings? ratings, List? images, + List? sides, String? $__typename, }); - CopyWith$Fragment$mealInfo$price get price; + CopyWith$Fragment$price get price; CopyWith$Fragment$mealInfo$statistics get statistics; CopyWith$Fragment$mealInfo$ratings get ratings; TRes images( @@ -999,6 +1024,11 @@ abstract class CopyWith$Fragment$mealInfo { Iterable< CopyWith$Fragment$mealInfo$images>) _fn); + TRes sides( + Iterable Function( + Iterable< + CopyWith$Fragment$mealInfo$sides>) + _fn); } class _CopyWithImpl$Fragment$mealInfo @@ -1024,6 +1054,7 @@ class _CopyWithImpl$Fragment$mealInfo Object? statistics = _undefined, Object? ratings = _undefined, Object? images = _undefined, + Object? sides = _undefined, Object? $__typename = _undefined, }) => _then(Fragment$mealInfo( @@ -1036,7 +1067,7 @@ class _CopyWithImpl$Fragment$mealInfo : (mealType as Enum$MealType), price: price == _undefined || price == null ? _instance.price - : (price as Fragment$mealInfo$price), + : (price as Fragment$price), allergens: allergens == _undefined || allergens == null ? _instance.allergens : (allergens as List), @@ -1052,13 +1083,16 @@ class _CopyWithImpl$Fragment$mealInfo images: images == _undefined || images == null ? _instance.images : (images as List), + sides: sides == _undefined || sides == null + ? _instance.sides + : (sides as List), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String), )); - CopyWith$Fragment$mealInfo$price get price { + CopyWith$Fragment$price get price { final local$price = _instance.price; - return CopyWith$Fragment$mealInfo$price(local$price, (e) => call(price: e)); + return CopyWith$Fragment$price(local$price, (e) => call(price: e)); } CopyWith$Fragment$mealInfo$statistics get statistics { @@ -1085,6 +1119,18 @@ class _CopyWithImpl$Fragment$mealInfo e, (i) => i, ))).toList()); + TRes sides( + Iterable Function( + Iterable< + CopyWith$Fragment$mealInfo$sides< + Fragment$mealInfo$sides>>) + _fn) => + call( + sides: + _fn(_instance.sides.map((e) => CopyWith$Fragment$mealInfo$sides( + e, + (i) => i, + ))).toList()); } class _CopyWithStubImpl$Fragment$mealInfo @@ -1097,22 +1143,23 @@ class _CopyWithStubImpl$Fragment$mealInfo String? id, String? name, Enum$MealType? mealType, - Fragment$mealInfo$price? price, + Fragment$price? price, List? allergens, List? additives, Fragment$mealInfo$statistics? statistics, Fragment$mealInfo$ratings? ratings, List? images, + List? sides, String? $__typename, }) => _res; - CopyWith$Fragment$mealInfo$price get price => - CopyWith$Fragment$mealInfo$price.stub(_res); + CopyWith$Fragment$price get price => CopyWith$Fragment$price.stub(_res); CopyWith$Fragment$mealInfo$statistics get statistics => CopyWith$Fragment$mealInfo$statistics.stub(_res); CopyWith$Fragment$mealInfo$ratings get ratings => CopyWith$Fragment$mealInfo$ratings.stub(_res); images(_fn) => _res; + sides(_fn) => _res; } const fragmentDefinitionmealInfo = FragmentDefinitionNode( @@ -1151,33 +1198,9 @@ const fragmentDefinitionmealInfo = FragmentDefinitionNode( arguments: [], directives: [], selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'employee'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'guest'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'pupil'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'student'), - alias: null, - arguments: [], + FragmentSpreadNode( + name: NameNode(value: 'price'), directives: [], - selectionSet: null, ), FieldNode( name: NameNode(value: '__typename'), @@ -1338,6 +1361,75 @@ const fragmentDefinitionmealInfo = FragmentDefinitionNode( ), ]), ), + FieldNode( + name: NameNode(value: 'sides'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'additives'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'allergens'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'price'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'price'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'mealType'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), FieldNode( name: NameNode(value: '__typename'), alias: null, @@ -1349,6 +1441,7 @@ const fragmentDefinitionmealInfo = FragmentDefinitionNode( ); const documentNodeFragmentmealInfo = DocumentNode(definitions: [ fragmentDefinitionmealInfo, + fragmentDefinitionprice, ]); extension ClientExtension$Fragment$mealInfo on graphql.GraphQLClient { @@ -1386,50 +1479,43 @@ extension ClientExtension$Fragment$mealInfo on graphql.GraphQLClient { } } -class Fragment$mealInfo$price { - Fragment$mealInfo$price({ - required this.employee, - required this.guest, - required this.pupil, - required this.student, - this.$__typename = 'Price', +class Fragment$mealInfo$statistics { + Fragment$mealInfo$statistics({ + this.lastServed, + this.nextServed, + required this.relativeFrequency, + this.$__typename = 'MealStatistics', }); - factory Fragment$mealInfo$price.fromJson(Map json) { - final l$employee = json['employee']; - final l$guest = json['guest']; - final l$pupil = json['pupil']; - final l$student = json['student']; + factory Fragment$mealInfo$statistics.fromJson(Map json) { + final l$lastServed = json['lastServed']; + final l$nextServed = json['nextServed']; + final l$relativeFrequency = json['relativeFrequency']; final l$$__typename = json['__typename']; - return Fragment$mealInfo$price( - employee: (l$employee as int), - guest: (l$guest as int), - pupil: (l$pupil as int), - student: (l$student as int), + return Fragment$mealInfo$statistics( + lastServed: (l$lastServed as String?), + nextServed: (l$nextServed as String?), + relativeFrequency: (l$relativeFrequency as num).toDouble(), $__typename: (l$$__typename as String), ); } - final int employee; - - final int guest; + final String? lastServed; - final int pupil; + final String? nextServed; - final int student; + final double relativeFrequency; final String $__typename; Map toJson() { final _resultData = {}; - final l$employee = employee; - _resultData['employee'] = l$employee; - final l$guest = guest; - _resultData['guest'] = l$guest; - final l$pupil = pupil; - _resultData['pupil'] = l$pupil; - final l$student = student; - _resultData['student'] = l$student; + final l$lastServed = lastServed; + _resultData['lastServed'] = l$lastServed; + final l$nextServed = nextServed; + _resultData['nextServed'] = l$nextServed; + final l$relativeFrequency = relativeFrequency; + _resultData['relativeFrequency'] = l$relativeFrequency; final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -1437,16 +1523,14 @@ class Fragment$mealInfo$price { @override int get hashCode { - final l$employee = employee; - final l$guest = guest; - final l$pupil = pupil; - final l$student = student; + final l$lastServed = lastServed; + final l$nextServed = nextServed; + final l$relativeFrequency = relativeFrequency; final l$$__typename = $__typename; return Object.hashAll([ - l$employee, - l$guest, - l$pupil, - l$student, + l$lastServed, + l$nextServed, + l$relativeFrequency, l$$__typename, ]); } @@ -1456,28 +1540,23 @@ class Fragment$mealInfo$price { if (identical(this, other)) { return true; } - if (!(other is Fragment$mealInfo$price) || + if (!(other is Fragment$mealInfo$statistics) || runtimeType != other.runtimeType) { return false; } - final l$employee = employee; - final lOther$employee = other.employee; - if (l$employee != lOther$employee) { - return false; - } - final l$guest = guest; - final lOther$guest = other.guest; - if (l$guest != lOther$guest) { + final l$lastServed = lastServed; + final lOther$lastServed = other.lastServed; + if (l$lastServed != lOther$lastServed) { return false; } - final l$pupil = pupil; - final lOther$pupil = other.pupil; - if (l$pupil != lOther$pupil) { + final l$nextServed = nextServed; + final lOther$nextServed = other.nextServed; + if (l$nextServed != lOther$nextServed) { return false; } - final l$student = student; - final lOther$student = other.student; - if (l$student != lOther$student) { + final l$relativeFrequency = relativeFrequency; + final lOther$relativeFrequency = other.relativeFrequency; + if (l$relativeFrequency != lOther$relativeFrequency) { return false; } final l$$__typename = $__typename; @@ -1489,241 +1568,71 @@ class Fragment$mealInfo$price { } } -extension UtilityExtension$Fragment$mealInfo$price on Fragment$mealInfo$price { - CopyWith$Fragment$mealInfo$price get copyWith => - CopyWith$Fragment$mealInfo$price( - this, - (i) => i, - ); +extension UtilityExtension$Fragment$mealInfo$statistics + on Fragment$mealInfo$statistics { + CopyWith$Fragment$mealInfo$statistics + get copyWith => CopyWith$Fragment$mealInfo$statistics( + this, + (i) => i, + ); } -abstract class CopyWith$Fragment$mealInfo$price { - factory CopyWith$Fragment$mealInfo$price( - Fragment$mealInfo$price instance, - TRes Function(Fragment$mealInfo$price) then, - ) = _CopyWithImpl$Fragment$mealInfo$price; +abstract class CopyWith$Fragment$mealInfo$statistics { + factory CopyWith$Fragment$mealInfo$statistics( + Fragment$mealInfo$statistics instance, + TRes Function(Fragment$mealInfo$statistics) then, + ) = _CopyWithImpl$Fragment$mealInfo$statistics; - factory CopyWith$Fragment$mealInfo$price.stub(TRes res) = - _CopyWithStubImpl$Fragment$mealInfo$price; + factory CopyWith$Fragment$mealInfo$statistics.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo$statistics; TRes call({ - int? employee, - int? guest, - int? pupil, - int? student, + String? lastServed, + String? nextServed, + double? relativeFrequency, String? $__typename, }); } -class _CopyWithImpl$Fragment$mealInfo$price - implements CopyWith$Fragment$mealInfo$price { - _CopyWithImpl$Fragment$mealInfo$price( +class _CopyWithImpl$Fragment$mealInfo$statistics + implements CopyWith$Fragment$mealInfo$statistics { + _CopyWithImpl$Fragment$mealInfo$statistics( this._instance, this._then, ); - final Fragment$mealInfo$price _instance; + final Fragment$mealInfo$statistics _instance; - final TRes Function(Fragment$mealInfo$price) _then; + final TRes Function(Fragment$mealInfo$statistics) _then; static const _undefined = {}; TRes call({ - Object? employee = _undefined, - Object? guest = _undefined, - Object? pupil = _undefined, - Object? student = _undefined, + Object? lastServed = _undefined, + Object? nextServed = _undefined, + Object? relativeFrequency = _undefined, Object? $__typename = _undefined, }) => - _then(Fragment$mealInfo$price( - employee: employee == _undefined || employee == null - ? _instance.employee - : (employee as int), - guest: guest == _undefined || guest == null - ? _instance.guest - : (guest as int), - pupil: pupil == _undefined || pupil == null - ? _instance.pupil - : (pupil as int), - student: student == _undefined || student == null - ? _instance.student - : (student as int), + _then(Fragment$mealInfo$statistics( + lastServed: lastServed == _undefined + ? _instance.lastServed + : (lastServed as String?), + nextServed: nextServed == _undefined + ? _instance.nextServed + : (nextServed as String?), + relativeFrequency: + relativeFrequency == _undefined || relativeFrequency == null + ? _instance.relativeFrequency + : (relativeFrequency as double), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String), )); } -class _CopyWithStubImpl$Fragment$mealInfo$price - implements CopyWith$Fragment$mealInfo$price { - _CopyWithStubImpl$Fragment$mealInfo$price(this._res); - - TRes _res; - - call({ - int? employee, - int? guest, - int? pupil, - int? student, - String? $__typename, - }) => - _res; -} - -class Fragment$mealInfo$statistics { - Fragment$mealInfo$statistics({ - this.lastServed, - this.nextServed, - required this.relativeFrequency, - this.$__typename = 'MealStatistics', - }); - - factory Fragment$mealInfo$statistics.fromJson(Map json) { - final l$lastServed = json['lastServed']; - final l$nextServed = json['nextServed']; - final l$relativeFrequency = json['relativeFrequency']; - final l$$__typename = json['__typename']; - return Fragment$mealInfo$statistics( - lastServed: (l$lastServed as String?), - nextServed: (l$nextServed as String?), - relativeFrequency: (l$relativeFrequency as num).toDouble(), - $__typename: (l$$__typename as String), - ); - } - - final String? lastServed; - - final String? nextServed; - - final double relativeFrequency; - - final String $__typename; - - Map toJson() { - final _resultData = {}; - final l$lastServed = lastServed; - _resultData['lastServed'] = l$lastServed; - final l$nextServed = nextServed; - _resultData['nextServed'] = l$nextServed; - final l$relativeFrequency = relativeFrequency; - _resultData['relativeFrequency'] = l$relativeFrequency; - final l$$__typename = $__typename; - _resultData['__typename'] = l$$__typename; - return _resultData; - } - - @override - int get hashCode { - final l$lastServed = lastServed; - final l$nextServed = nextServed; - final l$relativeFrequency = relativeFrequency; - final l$$__typename = $__typename; - return Object.hashAll([ - l$lastServed, - l$nextServed, - l$relativeFrequency, - l$$__typename, - ]); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (!(other is Fragment$mealInfo$statistics) || - runtimeType != other.runtimeType) { - return false; - } - final l$lastServed = lastServed; - final lOther$lastServed = other.lastServed; - if (l$lastServed != lOther$lastServed) { - return false; - } - final l$nextServed = nextServed; - final lOther$nextServed = other.nextServed; - if (l$nextServed != lOther$nextServed) { - return false; - } - final l$relativeFrequency = relativeFrequency; - final lOther$relativeFrequency = other.relativeFrequency; - if (l$relativeFrequency != lOther$relativeFrequency) { - return false; - } - final l$$__typename = $__typename; - final lOther$$__typename = other.$__typename; - if (l$$__typename != lOther$$__typename) { - return false; - } - return true; - } -} - -extension UtilityExtension$Fragment$mealInfo$statistics - on Fragment$mealInfo$statistics { - CopyWith$Fragment$mealInfo$statistics - get copyWith => CopyWith$Fragment$mealInfo$statistics( - this, - (i) => i, - ); -} - -abstract class CopyWith$Fragment$mealInfo$statistics { - factory CopyWith$Fragment$mealInfo$statistics( - Fragment$mealInfo$statistics instance, - TRes Function(Fragment$mealInfo$statistics) then, - ) = _CopyWithImpl$Fragment$mealInfo$statistics; - - factory CopyWith$Fragment$mealInfo$statistics.stub(TRes res) = - _CopyWithStubImpl$Fragment$mealInfo$statistics; - - TRes call({ - String? lastServed, - String? nextServed, - double? relativeFrequency, - String? $__typename, - }); -} - -class _CopyWithImpl$Fragment$mealInfo$statistics - implements CopyWith$Fragment$mealInfo$statistics { - _CopyWithImpl$Fragment$mealInfo$statistics( - this._instance, - this._then, - ); - - final Fragment$mealInfo$statistics _instance; - - final TRes Function(Fragment$mealInfo$statistics) _then; - - static const _undefined = {}; - - TRes call({ - Object? lastServed = _undefined, - Object? nextServed = _undefined, - Object? relativeFrequency = _undefined, - Object? $__typename = _undefined, - }) => - _then(Fragment$mealInfo$statistics( - lastServed: lastServed == _undefined - ? _instance.lastServed - : (lastServed as String?), - nextServed: nextServed == _undefined - ? _instance.nextServed - : (nextServed as String?), - relativeFrequency: - relativeFrequency == _undefined || relativeFrequency == null - ? _instance.relativeFrequency - : (relativeFrequency as double), - $__typename: $__typename == _undefined || $__typename == null - ? _instance.$__typename - : ($__typename as String), - )); -} - -class _CopyWithStubImpl$Fragment$mealInfo$statistics - implements CopyWith$Fragment$mealInfo$statistics { - _CopyWithStubImpl$Fragment$mealInfo$statistics(this._res); +class _CopyWithStubImpl$Fragment$mealInfo$statistics + implements CopyWith$Fragment$mealInfo$statistics { + _CopyWithStubImpl$Fragment$mealInfo$statistics(this._res); TRes _res; @@ -2143,6 +2052,522 @@ class _CopyWithStubImpl$Fragment$mealInfo$images _res; } +class Fragment$mealInfo$sides { + Fragment$mealInfo$sides({ + required this.id, + required this.name, + required this.additives, + required this.allergens, + required this.price, + required this.mealType, + this.$__typename = 'Side', + }); + + factory Fragment$mealInfo$sides.fromJson(Map json) { + final l$id = json['id']; + final l$name = json['name']; + final l$additives = json['additives']; + final l$allergens = json['allergens']; + final l$price = json['price']; + final l$mealType = json['mealType']; + final l$$__typename = json['__typename']; + return Fragment$mealInfo$sides( + id: (l$id as String), + name: (l$name as String), + additives: (l$additives as List) + .map((e) => fromJson$Enum$Additive((e as String))) + .toList(), + allergens: (l$allergens as List) + .map((e) => fromJson$Enum$Allergen((e as String))) + .toList(), + price: Fragment$price.fromJson((l$price as Map)), + mealType: fromJson$Enum$MealType((l$mealType as String)), + $__typename: (l$$__typename as String), + ); + } + + final String id; + + final String name; + + final List additives; + + final List allergens; + + final Fragment$price price; + + final Enum$MealType mealType; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$id = id; + _resultData['id'] = l$id; + final l$name = name; + _resultData['name'] = l$name; + final l$additives = additives; + _resultData['additives'] = + l$additives.map((e) => toJson$Enum$Additive(e)).toList(); + final l$allergens = allergens; + _resultData['allergens'] = + l$allergens.map((e) => toJson$Enum$Allergen(e)).toList(); + final l$price = price; + _resultData['price'] = l$price.toJson(); + final l$mealType = mealType; + _resultData['mealType'] = toJson$Enum$MealType(l$mealType); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$id = id; + final l$name = name; + final l$additives = additives; + final l$allergens = allergens; + final l$price = price; + final l$mealType = mealType; + final l$$__typename = $__typename; + return Object.hashAll([ + l$id, + l$name, + Object.hashAll(l$additives.map((v) => v)), + Object.hashAll(l$allergens.map((v) => v)), + l$price, + l$mealType, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$mealInfo$sides) || + runtimeType != other.runtimeType) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$additives = additives; + final lOther$additives = other.additives; + if (l$additives.length != lOther$additives.length) { + return false; + } + for (int i = 0; i < l$additives.length; i++) { + final l$additives$entry = l$additives[i]; + final lOther$additives$entry = lOther$additives[i]; + if (l$additives$entry != lOther$additives$entry) { + return false; + } + } + final l$allergens = allergens; + final lOther$allergens = other.allergens; + if (l$allergens.length != lOther$allergens.length) { + return false; + } + for (int i = 0; i < l$allergens.length; i++) { + final l$allergens$entry = l$allergens[i]; + final lOther$allergens$entry = lOther$allergens[i]; + if (l$allergens$entry != lOther$allergens$entry) { + return false; + } + } + final l$price = price; + final lOther$price = other.price; + if (l$price != lOther$price) { + return false; + } + final l$mealType = mealType; + final lOther$mealType = other.mealType; + if (l$mealType != lOther$mealType) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$mealInfo$sides on Fragment$mealInfo$sides { + CopyWith$Fragment$mealInfo$sides get copyWith => + CopyWith$Fragment$mealInfo$sides( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$mealInfo$sides { + factory CopyWith$Fragment$mealInfo$sides( + Fragment$mealInfo$sides instance, + TRes Function(Fragment$mealInfo$sides) then, + ) = _CopyWithImpl$Fragment$mealInfo$sides; + + factory CopyWith$Fragment$mealInfo$sides.stub(TRes res) = + _CopyWithStubImpl$Fragment$mealInfo$sides; + + TRes call({ + String? id, + String? name, + List? additives, + List? allergens, + Fragment$price? price, + Enum$MealType? mealType, + String? $__typename, + }); + CopyWith$Fragment$price get price; +} + +class _CopyWithImpl$Fragment$mealInfo$sides + implements CopyWith$Fragment$mealInfo$sides { + _CopyWithImpl$Fragment$mealInfo$sides( + this._instance, + this._then, + ); + + final Fragment$mealInfo$sides _instance; + + final TRes Function(Fragment$mealInfo$sides) _then; + + static const _undefined = {}; + + TRes call({ + Object? id = _undefined, + Object? name = _undefined, + Object? additives = _undefined, + Object? allergens = _undefined, + Object? price = _undefined, + Object? mealType = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$mealInfo$sides( + id: id == _undefined || id == null ? _instance.id : (id as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + additives: additives == _undefined || additives == null + ? _instance.additives + : (additives as List), + allergens: allergens == _undefined || allergens == null + ? _instance.allergens + : (allergens as List), + price: price == _undefined || price == null + ? _instance.price + : (price as Fragment$price), + mealType: mealType == _undefined || mealType == null + ? _instance.mealType + : (mealType as Enum$MealType), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Fragment$price get price { + final local$price = _instance.price; + return CopyWith$Fragment$price(local$price, (e) => call(price: e)); + } +} + +class _CopyWithStubImpl$Fragment$mealInfo$sides + implements CopyWith$Fragment$mealInfo$sides { + _CopyWithStubImpl$Fragment$mealInfo$sides(this._res); + + TRes _res; + + call({ + String? id, + String? name, + List? additives, + List? allergens, + Fragment$price? price, + Enum$MealType? mealType, + String? $__typename, + }) => + _res; + CopyWith$Fragment$price get price => CopyWith$Fragment$price.stub(_res); +} + +class Fragment$price { + Fragment$price({ + required this.employee, + required this.guest, + required this.pupil, + required this.student, + this.$__typename = 'Price', + }); + + factory Fragment$price.fromJson(Map json) { + final l$employee = json['employee']; + final l$guest = json['guest']; + final l$pupil = json['pupil']; + final l$student = json['student']; + final l$$__typename = json['__typename']; + return Fragment$price( + employee: (l$employee as int), + guest: (l$guest as int), + pupil: (l$pupil as int), + student: (l$student as int), + $__typename: (l$$__typename as String), + ); + } + + final int employee; + + final int guest; + + final int pupil; + + final int student; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$employee = employee; + _resultData['employee'] = l$employee; + final l$guest = guest; + _resultData['guest'] = l$guest; + final l$pupil = pupil; + _resultData['pupil'] = l$pupil; + final l$student = student; + _resultData['student'] = l$student; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$employee = employee; + final l$guest = guest; + final l$pupil = pupil; + final l$student = student; + final l$$__typename = $__typename; + return Object.hashAll([ + l$employee, + l$guest, + l$pupil, + l$student, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$price) || runtimeType != other.runtimeType) { + return false; + } + final l$employee = employee; + final lOther$employee = other.employee; + if (l$employee != lOther$employee) { + return false; + } + final l$guest = guest; + final lOther$guest = other.guest; + if (l$guest != lOther$guest) { + return false; + } + final l$pupil = pupil; + final lOther$pupil = other.pupil; + if (l$pupil != lOther$pupil) { + return false; + } + final l$student = student; + final lOther$student = other.student; + if (l$student != lOther$student) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$price on Fragment$price { + CopyWith$Fragment$price get copyWith => + CopyWith$Fragment$price( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$price { + factory CopyWith$Fragment$price( + Fragment$price instance, + TRes Function(Fragment$price) then, + ) = _CopyWithImpl$Fragment$price; + + factory CopyWith$Fragment$price.stub(TRes res) = + _CopyWithStubImpl$Fragment$price; + + TRes call({ + int? employee, + int? guest, + int? pupil, + int? student, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$price + implements CopyWith$Fragment$price { + _CopyWithImpl$Fragment$price( + this._instance, + this._then, + ); + + final Fragment$price _instance; + + final TRes Function(Fragment$price) _then; + + static const _undefined = {}; + + TRes call({ + Object? employee = _undefined, + Object? guest = _undefined, + Object? pupil = _undefined, + Object? student = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$price( + employee: employee == _undefined || employee == null + ? _instance.employee + : (employee as int), + guest: guest == _undefined || guest == null + ? _instance.guest + : (guest as int), + pupil: pupil == _undefined || pupil == null + ? _instance.pupil + : (pupil as int), + student: student == _undefined || student == null + ? _instance.student + : (student as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$price + implements CopyWith$Fragment$price { + _CopyWithStubImpl$Fragment$price(this._res); + + TRes _res; + + call({ + int? employee, + int? guest, + int? pupil, + int? student, + String? $__typename, + }) => + _res; +} + +const fragmentDefinitionprice = FragmentDefinitionNode( + name: NameNode(value: 'price'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'Price'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'employee'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'guest'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'pupil'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'student'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentprice = DocumentNode(definitions: [ + fragmentDefinitionprice, +]); + +extension ClientExtension$Fragment$price on graphql.GraphQLClient { + void writeFragment$price({ + required Fragment$price data, + required Map idFields, + bool broadcast = true, + }) => + this.writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'price', + document: documentNodeFragmentprice, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$price? readFragment$price({ + required Map idFields, + bool optimistic = true, + }) { + final result = this.readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'price', + document: documentNodeFragmentprice, + ), + ), + optimistic: optimistic, + ); + return result == null ? null : Fragment$price.fromJson(result); + } +} + class Variables$Query$GetMealPlanForDay { factory Variables$Query$GetMealPlanForDay({required String date}) => Variables$Query$GetMealPlanForDay._({ @@ -2434,6 +2859,7 @@ const documentNodeQueryGetMealPlanForDay = DocumentNode(definitions: [ ), fragmentDefinitionmealPlan, fragmentDefinitionmealInfo, + fragmentDefinitionprice, ]); Query$GetMealPlanForDay _parserFn$Query$GetMealPlanForDay( Map data) => @@ -2914,6 +3340,7 @@ const documentNodeQueryGetCanteenDate = DocumentNode(definitions: [ ), fragmentDefinitionmealPlan, fragmentDefinitionmealInfo, + fragmentDefinitionprice, ]); Query$GetCanteenDate _parserFn$Query$GetCanteenDate( Map data) => @@ -3422,6 +3849,7 @@ const documentNodeQueryGetMeal = DocumentNode(definitions: [ ]), ), fragmentDefinitionmealInfo, + fragmentDefinitionprice, ]); Query$GetMeal _parserFn$Query$GetMeal(Map data) => Query$GetMeal.fromJson(data); diff --git a/app/lib/view_model/repository/data_classes/meal/Allergen.dart b/app/lib/view_model/repository/data_classes/meal/Allergen.dart index ec83833e..bb0c1a08 100644 --- a/app/lib/view_model/repository/data_classes/meal/Allergen.dart +++ b/app/lib/view_model/repository/data_classes/meal/Allergen.dart @@ -24,5 +24,7 @@ enum Allergen { so, wa, we, - wt + wt, + la, + gl } \ No newline at end of file diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index c690a306..51f9c790 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:app/model/api_server/GraphQlServerAccess.dart'; import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; @@ -104,7 +106,7 @@ void main() async { var res = switch (result) { Success(value: final mealplan) => expect(mealplan.toString(), ""), - Failure(value: final exception) => 1, + Failure(value: final exception) => expect(exception.toString(), ""), }; }); @@ -115,7 +117,7 @@ void main() async { var res = switch (result) { Success(value: final mealplan) => expect(mealplan.toString(), ""), - Failure(value: final exception) => 1, + Failure(value: final exception) => expect(exception.toString(), ""), }; }); @@ -136,8 +138,8 @@ void main() async { DateTime(2020, 11, 2)); var res = switch (result) { - Success(value: final mealplan) => expect(mealplan.toString(), ""), - Failure(value: final exception) => 1, + Success(value: final mealplan) => expect(mealplan.additives, ""), + Failure(value: final exception) => expect(exception, true, reason: "exception while request"), }; }); } From 709525ee5609bb7eec0d28ae26177fbaf9872321 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Tue, 11 Jul 2023 19:36:30 +0200 Subject: [PATCH 035/184] formatting fixes --- .../model/api_server/GraphQlServerAccess.dart | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index a4b6d0e7..8d79d83d 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -59,8 +59,8 @@ class GraphQlServerAccess implements IServerAccess { final result = await _client.mutate$RemoveUpvote( Options$Mutation$RemoveUpvote( variables: Variables$Mutation$RemoveUpvote(imageId: image.id))); - final parsedData = result.parsedData; - return parsedData?.removeUpvote ?? false; + + return result.parsedData?.removeUpvote ?? false; } @override @@ -69,8 +69,8 @@ class GraphQlServerAccess implements IServerAccess { final result = await _client.mutate$AddDownvote( Options$Mutation$AddDownvote( variables: Variables$Mutation$AddDownvote(imageId: image.id))); - final parsedData = result.parsedData; - return parsedData?.addDownvote ?? false; + + return result.parsedData?.addDownvote ?? false; } @override @@ -78,8 +78,8 @@ class GraphQlServerAccess implements IServerAccess { // TODO auth final result = await _client.mutate$AddUpvote(Options$Mutation$AddUpvote( variables: Variables$Mutation$AddUpvote(imageId: image.id))); - final parsedData = result.parsedData; - return parsedData?.addUpvote ?? false; + + return result.parsedData?.addUpvote ?? false; } @override @@ -88,8 +88,8 @@ class GraphQlServerAccess implements IServerAccess { final result = await _client.mutate$LinkImage(Options$Mutation$LinkImage( variables: Variables$Mutation$LinkImage(imageUrl: url, mealId: meal.id))); - final parsedData = result.parsedData; - return parsedData?.addImage ?? false; + + return result.parsedData?.addImage ?? false; } @override @@ -100,8 +100,8 @@ class GraphQlServerAccess implements IServerAccess { variables: Variables$Mutation$ReportImage( imageId: image.id, reason: _convertToReportReason(reportReason)))); - final parsedData = result.parsedData; - return parsedData?.reportImage ?? false; + + return result.parsedData?.reportImage ?? false; } @override @@ -111,8 +111,8 @@ class GraphQlServerAccess implements IServerAccess { Options$Mutation$UpdateRating( variables: Variables$Mutation$UpdateRating( mealId: meal.id, rating: rating))); - final parsedData = result.parsedData; - return parsedData?.setRating ?? false; + + return result.parsedData?.setRating ?? false; } // ---------------------- queries ---------------------- @@ -127,19 +127,19 @@ class GraphQlServerAccess implements IServerAccess { // TODO parallel? for (int offset = 0; offset < daysToParse; offset++) { + final date = today.add(Duration(days: offset)); final result = await _client.query$GetMealPlanForDay( Options$Query$GetMealPlanForDay( variables: Variables$Query$GetMealPlanForDay( date: _dateFormat.format(date)))); - final parsedData = result.parsedData; final exception = result.exception; if (exception != null) { return Failure(exception); } - final mealPlan = _convertMealPlan(parsedData?.getCanteens ?? [], date); + final mealPlan = _convertMealPlan(result.parsedData?.getCanteens ?? [], date); completeList.addAll(mealPlan); } @@ -152,6 +152,7 @@ class GraphQlServerAccess implements IServerAccess { final result = await _client.query$GetMeal(Options$Query$GetMeal( variables: Variables$Query$GetMeal( date: _dateFormat.format(date), mealId: meal.id, lineId: line.id))); + final mealData = result.parsedData?.getMeal; final exception = result.exception; if (exception != null) { @@ -173,14 +174,14 @@ class GraphQlServerAccess implements IServerAccess { Options$Query$GetCanteenDate( variables: Variables$Query$GetCanteenDate( canteenId: canteen.id, date: _dateFormat.format(date)))); - final parsedData = result.parsedData; + final exception = result.exception; if (exception != null) { return Failure(exception); } final mealPlan = - _convertMealPlan([parsedData?.getCanteen].nonNulls.toList(), date); + _convertMealPlan([result.parsedData?.getCanteen].nonNulls.toList(), date); return Success(mealPlan); } } From 75c2f8b94bd459b49bbad5fe84ebb8637afa19cd Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Tue, 11 Jul 2023 21:49:11 +0200 Subject: [PATCH 036/184] added get canteen --- .../model/api_server/GraphQlServerAccess.dart | 46 +- .../model/api_server/requests/querys.graphql | 17 +- .../api_server/requests/querys.graphql.dart | 1004 +++++++++++++---- .../repository/interface/IServerAccess.dart | 6 + .../api_server/GraphQlServerAccess_test.dart | 29 +- 5 files changed, 863 insertions(+), 239 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index 8d79d83d..2f532352 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:app/model/api_server/requests/querys.graphql.dart'; import 'package:app/model/api_server/requests/schema.graphql.dart'; import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; @@ -127,7 +129,6 @@ class GraphQlServerAccess implements IServerAccess { // TODO parallel? for (int offset = 0; offset < daysToParse; offset++) { - final date = today.add(Duration(days: offset)); final result = await _client.query$GetMealPlanForDay( Options$Query$GetMealPlanForDay( @@ -139,7 +140,8 @@ class GraphQlServerAccess implements IServerAccess { return Failure(exception); } - final mealPlan = _convertMealPlan(result.parsedData?.getCanteens ?? [], date); + final mealPlan = + _convertMealPlan(result.parsedData?.getCanteens ?? [], date); completeList.addAll(mealPlan); } @@ -160,7 +162,7 @@ class GraphQlServerAccess implements IServerAccess { } if (mealData == null) { - return Failure(NoMealException( + return Failure(NoMealException(// Todo correct exception "Could not request meal from api: ${result.exception}")); } @@ -180,10 +182,36 @@ class GraphQlServerAccess implements IServerAccess { return Failure(exception); } - final mealPlan = - _convertMealPlan([result.parsedData?.getCanteen].nonNulls.toList(), date); + final mealPlan = _convertMealPlan( + [result.parsedData?.getCanteen].nonNulls.toList(), date); return Success(mealPlan); } + + static const defaultUuid = "00000000-0000-0000-0000-000000000000"; + + @override + Future> getCanteenOrDefault(String? id) async { + Fragment$canteen? canteen; + + final result = await _client.query$GetCanteen(Options$Query$GetCanteen( + variables: Variables$Query$GetCanteen(canteenId: id ?? defaultUuid))); + + final exception = result.exception; + if (exception != null) { + return Failure(exception); + } + + canteen = result.parsedData?.getCanteen; + + canteen ??= result.parsedData?.getCanteens.first; + + if (canteen == null) { + return Failure(NoMealException(// Todo correct exception + "Could not request default canteen from api: ${result.exception}")); + } + + return Success(_convertCanteen(canteen)); + } } // --------------- utility helper methods --------------- @@ -221,8 +249,7 @@ Meal _convertMeal(Fragment$mealInfo meal) { foodType: _convertMealType(meal.mealType), price: _convertPrice(meal.price), additives: meal.additives.map((e) => _convertAdditive(e)).nonNulls.toList(), - allergens: - meal.allergens.map((e) => _convertAllergen(e)).nonNulls.toList(), + allergens: meal.allergens.map((e) => _convertAllergen(e)).nonNulls.toList(), averageRating: meal.ratings.averageRating, individualRating: meal.ratings.personalRating, numberOfRatings: meal.ratings.ratingsCount, @@ -235,7 +262,8 @@ Meal _convertMeal(Fragment$mealInfo meal) { } Frequency _specifyFrequency(double frequency) { - throw UnsupportedError("message"); + // TODO + return Frequency.normal; } DateTime? _convertDate(String? date) { @@ -358,7 +386,7 @@ Price _convertPrice(Fragment$price price) { guest: price.guest); } -Canteen _convertCanteen(Fragment$mealPlan$lines$canteen canteen) { +Canteen _convertCanteen(Fragment$canteen canteen) { return Canteen(id: canteen.id, name: canteen.name); } diff --git a/app/lib/model/api_server/requests/querys.graphql b/app/lib/model/api_server/requests/querys.graphql index e1101a08..7756acbf 100644 --- a/app/lib/model/api_server/requests/querys.graphql +++ b/app/lib/model/api_server/requests/querys.graphql @@ -16,13 +16,26 @@ query GetMeal($date: NaiveDate!, $mealId: UUID!, $lineId: UUID!) { } } +query GetCanteen($canteenId: UUID!) { + getCanteen(canteenId: $canteenId) { + ...canteen + } + getCanteens { + ...canteen + } +} + +fragment canteen on Canteen { + id + name +} + fragment mealPlan on Canteen { lines { id name canteen { - id - name + ...canteen } meals(date: $date) { ...mealInfo diff --git a/app/lib/model/api_server/requests/querys.graphql.dart b/app/lib/model/api_server/requests/querys.graphql.dart index e8948cc9..4a6d64bc 100644 --- a/app/lib/model/api_server/requests/querys.graphql.dart +++ b/app/lib/model/api_server/requests/querys.graphql.dart @@ -5,6 +5,218 @@ import 'package:graphql/client.dart' as graphql; import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; import 'schema.graphql.dart'; +class Fragment$canteen { + Fragment$canteen({ + required this.id, + required this.name, + this.$__typename = 'Canteen', + }); + + factory Fragment$canteen.fromJson(Map json) { + final l$id = json['id']; + final l$name = json['name']; + final l$$__typename = json['__typename']; + return Fragment$canteen( + id: (l$id as String), + name: (l$name as String), + $__typename: (l$$__typename as String), + ); + } + + final String id; + + final String name; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$id = id; + _resultData['id'] = l$id; + final l$name = name; + _resultData['name'] = l$name; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$id = id; + final l$name = name; + final l$$__typename = $__typename; + return Object.hashAll([ + l$id, + l$name, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$canteen) || runtimeType != other.runtimeType) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$canteen on Fragment$canteen { + CopyWith$Fragment$canteen get copyWith => + CopyWith$Fragment$canteen( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$canteen { + factory CopyWith$Fragment$canteen( + Fragment$canteen instance, + TRes Function(Fragment$canteen) then, + ) = _CopyWithImpl$Fragment$canteen; + + factory CopyWith$Fragment$canteen.stub(TRes res) = + _CopyWithStubImpl$Fragment$canteen; + + TRes call({ + String? id, + String? name, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$canteen + implements CopyWith$Fragment$canteen { + _CopyWithImpl$Fragment$canteen( + this._instance, + this._then, + ); + + final Fragment$canteen _instance; + + final TRes Function(Fragment$canteen) _then; + + static const _undefined = {}; + + TRes call({ + Object? id = _undefined, + Object? name = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$canteen( + id: id == _undefined || id == null ? _instance.id : (id as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$canteen + implements CopyWith$Fragment$canteen { + _CopyWithStubImpl$Fragment$canteen(this._res); + + TRes _res; + + call({ + String? id, + String? name, + String? $__typename, + }) => + _res; +} + +const fragmentDefinitioncanteen = FragmentDefinitionNode( + name: NameNode(value: 'canteen'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'Canteen'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentcanteen = DocumentNode(definitions: [ + fragmentDefinitioncanteen, +]); + +extension ClientExtension$Fragment$canteen on graphql.GraphQLClient { + void writeFragment$canteen({ + required Fragment$canteen data, + required Map idFields, + bool broadcast = true, + }) => + this.writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'canteen', + document: documentNodeFragmentcanteen, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$canteen? readFragment$canteen({ + required Map idFields, + bool optimistic = true, + }) { + final result = this.readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'canteen', + document: documentNodeFragmentcanteen, + ), + ), + optimistic: optimistic, + ); + return result == null ? null : Fragment$canteen.fromJson(result); + } +} + class Variables$Fragment$mealPlan { factory Variables$Fragment$mealPlan({required String date}) => Variables$Fragment$mealPlan._({ @@ -286,19 +498,9 @@ const fragmentDefinitionmealPlan = FragmentDefinitionNode( arguments: [], directives: [], selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'id'), - alias: null, - arguments: [], - directives: [], - selectionSet: null, - ), - FieldNode( - name: NameNode(value: 'name'), - alias: null, - arguments: [], + FragmentSpreadNode( + name: NameNode(value: 'canteen'), directives: [], - selectionSet: null, ), FieldNode( name: NameNode(value: '__typename'), @@ -353,6 +555,7 @@ const fragmentDefinitionmealPlan = FragmentDefinitionNode( ); const documentNodeFragmentmealPlan = DocumentNode(definitions: [ fragmentDefinitionmealPlan, + fragmentDefinitioncanteen, fragmentDefinitionmealInfo, fragmentDefinitionprice, ]); @@ -414,8 +617,7 @@ class Fragment$mealPlan$lines { return Fragment$mealPlan$lines( id: (l$id as String), name: (l$name as String), - canteen: Fragment$mealPlan$lines$canteen.fromJson( - (l$canteen as Map)), + canteen: Fragment$canteen.fromJson((l$canteen as Map)), meals: (l$meals as List?) ?.map((e) => Fragment$mealInfo.fromJson((e as Map))) .toList(), @@ -427,7 +629,7 @@ class Fragment$mealPlan$lines { final String name; - final Fragment$mealPlan$lines$canteen canteen; + final Fragment$canteen canteen; final List? meals; @@ -533,11 +735,11 @@ abstract class CopyWith$Fragment$mealPlan$lines { TRes call({ String? id, String? name, - Fragment$mealPlan$lines$canteen? canteen, + Fragment$canteen? canteen, List? meals, String? $__typename, }); - CopyWith$Fragment$mealPlan$lines$canteen get canteen; + CopyWith$Fragment$canteen get canteen; TRes meals( Iterable? Function( Iterable>?) @@ -571,7 +773,7 @@ class _CopyWithImpl$Fragment$mealPlan$lines : (name as String), canteen: canteen == _undefined || canteen == null ? _instance.canteen - : (canteen as Fragment$mealPlan$lines$canteen), + : (canteen as Fragment$canteen), meals: meals == _undefined ? _instance.meals : (meals as List?), @@ -579,10 +781,9 @@ class _CopyWithImpl$Fragment$mealPlan$lines ? _instance.$__typename : ($__typename as String), )); - CopyWith$Fragment$mealPlan$lines$canteen get canteen { + CopyWith$Fragment$canteen get canteen { final local$canteen = _instance.canteen; - return CopyWith$Fragment$mealPlan$lines$canteen( - local$canteen, (e) => call(canteen: e)); + return CopyWith$Fragment$canteen(local$canteen, (e) => call(canteen: e)); } TRes meals( @@ -605,30 +806,66 @@ class _CopyWithStubImpl$Fragment$mealPlan$lines call({ String? id, String? name, - Fragment$mealPlan$lines$canteen? canteen, + Fragment$canteen? canteen, List? meals, String? $__typename, }) => _res; - CopyWith$Fragment$mealPlan$lines$canteen get canteen => - CopyWith$Fragment$mealPlan$lines$canteen.stub(_res); + CopyWith$Fragment$canteen get canteen => + CopyWith$Fragment$canteen.stub(_res); meals(_fn) => _res; } -class Fragment$mealPlan$lines$canteen { - Fragment$mealPlan$lines$canteen({ +class Fragment$mealInfo { + Fragment$mealInfo({ required this.id, required this.name, - this.$__typename = 'Canteen', + required this.mealType, + required this.price, + required this.allergens, + required this.additives, + required this.statistics, + required this.ratings, + required this.images, + required this.sides, + this.$__typename = 'Meal', }); - factory Fragment$mealPlan$lines$canteen.fromJson(Map json) { + factory Fragment$mealInfo.fromJson(Map json) { final l$id = json['id']; final l$name = json['name']; + final l$mealType = json['mealType']; + final l$price = json['price']; + final l$allergens = json['allergens']; + final l$additives = json['additives']; + final l$statistics = json['statistics']; + final l$ratings = json['ratings']; + final l$images = json['images']; + final l$sides = json['sides']; final l$$__typename = json['__typename']; - return Fragment$mealPlan$lines$canteen( + return Fragment$mealInfo( id: (l$id as String), name: (l$name as String), + mealType: fromJson$Enum$MealType((l$mealType as String)), + price: Fragment$price.fromJson((l$price as Map)), + allergens: (l$allergens as List) + .map((e) => fromJson$Enum$Allergen((e as String))) + .toList(), + additives: (l$additives as List) + .map((e) => fromJson$Enum$Additive((e as String))) + .toList(), + statistics: Fragment$mealInfo$statistics.fromJson( + (l$statistics as Map)), + ratings: Fragment$mealInfo$ratings.fromJson( + (l$ratings as Map)), + images: (l$images as List) + .map((e) => + Fragment$mealInfo$images.fromJson((e as Map))) + .toList(), + sides: (l$sides as List) + .map((e) => + Fragment$mealInfo$sides.fromJson((e as Map))) + .toList(), $__typename: (l$$__typename as String), ); } @@ -637,201 +874,22 @@ class Fragment$mealPlan$lines$canteen { final String name; - final String $__typename; - - Map toJson() { - final _resultData = {}; - final l$id = id; - _resultData['id'] = l$id; - final l$name = name; - _resultData['name'] = l$name; - final l$$__typename = $__typename; - _resultData['__typename'] = l$$__typename; - return _resultData; - } - - @override - int get hashCode { - final l$id = id; - final l$name = name; - final l$$__typename = $__typename; - return Object.hashAll([ - l$id, - l$name, - l$$__typename, - ]); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (!(other is Fragment$mealPlan$lines$canteen) || - runtimeType != other.runtimeType) { - return false; - } - final l$id = id; - final lOther$id = other.id; - if (l$id != lOther$id) { - return false; - } - final l$name = name; - final lOther$name = other.name; - if (l$name != lOther$name) { - return false; - } - final l$$__typename = $__typename; - final lOther$$__typename = other.$__typename; - if (l$$__typename != lOther$$__typename) { - return false; - } - return true; - } -} - -extension UtilityExtension$Fragment$mealPlan$lines$canteen - on Fragment$mealPlan$lines$canteen { - CopyWith$Fragment$mealPlan$lines$canteen - get copyWith => CopyWith$Fragment$mealPlan$lines$canteen( - this, - (i) => i, - ); -} - -abstract class CopyWith$Fragment$mealPlan$lines$canteen { - factory CopyWith$Fragment$mealPlan$lines$canteen( - Fragment$mealPlan$lines$canteen instance, - TRes Function(Fragment$mealPlan$lines$canteen) then, - ) = _CopyWithImpl$Fragment$mealPlan$lines$canteen; - - factory CopyWith$Fragment$mealPlan$lines$canteen.stub(TRes res) = - _CopyWithStubImpl$Fragment$mealPlan$lines$canteen; - - TRes call({ - String? id, - String? name, - String? $__typename, - }); -} - -class _CopyWithImpl$Fragment$mealPlan$lines$canteen - implements CopyWith$Fragment$mealPlan$lines$canteen { - _CopyWithImpl$Fragment$mealPlan$lines$canteen( - this._instance, - this._then, - ); - - final Fragment$mealPlan$lines$canteen _instance; - - final TRes Function(Fragment$mealPlan$lines$canteen) _then; - - static const _undefined = {}; - - TRes call({ - Object? id = _undefined, - Object? name = _undefined, - Object? $__typename = _undefined, - }) => - _then(Fragment$mealPlan$lines$canteen( - id: id == _undefined || id == null ? _instance.id : (id as String), - name: name == _undefined || name == null - ? _instance.name - : (name as String), - $__typename: $__typename == _undefined || $__typename == null - ? _instance.$__typename - : ($__typename as String), - )); -} - -class _CopyWithStubImpl$Fragment$mealPlan$lines$canteen - implements CopyWith$Fragment$mealPlan$lines$canteen { - _CopyWithStubImpl$Fragment$mealPlan$lines$canteen(this._res); - - TRes _res; - - call({ - String? id, - String? name, - String? $__typename, - }) => - _res; -} - -class Fragment$mealInfo { - Fragment$mealInfo({ - required this.id, - required this.name, - required this.mealType, - required this.price, - required this.allergens, - required this.additives, - required this.statistics, - required this.ratings, - required this.images, - required this.sides, - this.$__typename = 'Meal', - }); - - factory Fragment$mealInfo.fromJson(Map json) { - final l$id = json['id']; - final l$name = json['name']; - final l$mealType = json['mealType']; - final l$price = json['price']; - final l$allergens = json['allergens']; - final l$additives = json['additives']; - final l$statistics = json['statistics']; - final l$ratings = json['ratings']; - final l$images = json['images']; - final l$sides = json['sides']; - final l$$__typename = json['__typename']; - return Fragment$mealInfo( - id: (l$id as String), - name: (l$name as String), - mealType: fromJson$Enum$MealType((l$mealType as String)), - price: Fragment$price.fromJson((l$price as Map)), - allergens: (l$allergens as List) - .map((e) => fromJson$Enum$Allergen((e as String))) - .toList(), - additives: (l$additives as List) - .map((e) => fromJson$Enum$Additive((e as String))) - .toList(), - statistics: Fragment$mealInfo$statistics.fromJson( - (l$statistics as Map)), - ratings: Fragment$mealInfo$ratings.fromJson( - (l$ratings as Map)), - images: (l$images as List) - .map((e) => - Fragment$mealInfo$images.fromJson((e as Map))) - .toList(), - sides: (l$sides as List) - .map((e) => - Fragment$mealInfo$sides.fromJson((e as Map))) - .toList(), - $__typename: (l$$__typename as String), - ); - } - - final String id; - - final String name; - - final Enum$MealType mealType; - - final Fragment$price price; - - final List allergens; - - final List additives; - - final Fragment$mealInfo$statistics statistics; - - final Fragment$mealInfo$ratings ratings; - - final List images; - - final List sides; - + final Enum$MealType mealType; + + final Fragment$price price; + + final List allergens; + + final List additives; + + final Fragment$mealInfo$statistics statistics; + + final Fragment$mealInfo$ratings ratings; + + final List images; + + final List sides; + final String $__typename; Map toJson() { @@ -2858,6 +2916,7 @@ const documentNodeQueryGetMealPlanForDay = DocumentNode(definitions: [ ]), ), fragmentDefinitionmealPlan, + fragmentDefinitioncanteen, fragmentDefinitionmealInfo, fragmentDefinitionprice, ]); @@ -3339,6 +3398,7 @@ const documentNodeQueryGetCanteenDate = DocumentNode(definitions: [ ]), ), fragmentDefinitionmealPlan, + fragmentDefinitioncanteen, fragmentDefinitionmealInfo, fragmentDefinitionprice, ]); @@ -3999,3 +4059,499 @@ class Query$GetMeal$Widget extends graphql_flutter.Query { builder: builder, ); } + +class Variables$Query$GetCanteen { + factory Variables$Query$GetCanteen({required String canteenId}) => + Variables$Query$GetCanteen._({ + r'canteenId': canteenId, + }); + + Variables$Query$GetCanteen._(this._$data); + + factory Variables$Query$GetCanteen.fromJson(Map data) { + final result$data = {}; + final l$canteenId = data['canteenId']; + result$data['canteenId'] = (l$canteenId as String); + return Variables$Query$GetCanteen._(result$data); + } + + Map _$data; + + String get canteenId => (_$data['canteenId'] as String); + Map toJson() { + final result$data = {}; + final l$canteenId = canteenId; + result$data['canteenId'] = l$canteenId; + return result$data; + } + + CopyWith$Variables$Query$GetCanteen + get copyWith => CopyWith$Variables$Query$GetCanteen( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Query$GetCanteen) || + runtimeType != other.runtimeType) { + return false; + } + final l$canteenId = canteenId; + final lOther$canteenId = other.canteenId; + if (l$canteenId != lOther$canteenId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$canteenId = canteenId; + return Object.hashAll([l$canteenId]); + } +} + +abstract class CopyWith$Variables$Query$GetCanteen { + factory CopyWith$Variables$Query$GetCanteen( + Variables$Query$GetCanteen instance, + TRes Function(Variables$Query$GetCanteen) then, + ) = _CopyWithImpl$Variables$Query$GetCanteen; + + factory CopyWith$Variables$Query$GetCanteen.stub(TRes res) = + _CopyWithStubImpl$Variables$Query$GetCanteen; + + TRes call({String? canteenId}); +} + +class _CopyWithImpl$Variables$Query$GetCanteen + implements CopyWith$Variables$Query$GetCanteen { + _CopyWithImpl$Variables$Query$GetCanteen( + this._instance, + this._then, + ); + + final Variables$Query$GetCanteen _instance; + + final TRes Function(Variables$Query$GetCanteen) _then; + + static const _undefined = {}; + + TRes call({Object? canteenId = _undefined}) => + _then(Variables$Query$GetCanteen._({ + ..._instance._$data, + if (canteenId != _undefined && canteenId != null) + 'canteenId': (canteenId as String), + })); +} + +class _CopyWithStubImpl$Variables$Query$GetCanteen + implements CopyWith$Variables$Query$GetCanteen { + _CopyWithStubImpl$Variables$Query$GetCanteen(this._res); + + TRes _res; + + call({String? canteenId}) => _res; +} + +class Query$GetCanteen { + Query$GetCanteen({ + this.getCanteen, + required this.getCanteens, + this.$__typename = 'QueryRoot', + }); + + factory Query$GetCanteen.fromJson(Map json) { + final l$getCanteen = json['getCanteen']; + final l$getCanteens = json['getCanteens']; + final l$$__typename = json['__typename']; + return Query$GetCanteen( + getCanteen: l$getCanteen == null + ? null + : Fragment$canteen.fromJson((l$getCanteen as Map)), + getCanteens: (l$getCanteens as List) + .map((e) => Fragment$canteen.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final Fragment$canteen? getCanteen; + + final List getCanteens; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$getCanteen = getCanteen; + _resultData['getCanteen'] = l$getCanteen?.toJson(); + final l$getCanteens = getCanteens; + _resultData['getCanteens'] = l$getCanteens.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$getCanteen = getCanteen; + final l$getCanteens = getCanteens; + final l$$__typename = $__typename; + return Object.hashAll([ + l$getCanteen, + Object.hashAll(l$getCanteens.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetCanteen) || runtimeType != other.runtimeType) { + return false; + } + final l$getCanteen = getCanteen; + final lOther$getCanteen = other.getCanteen; + if (l$getCanteen != lOther$getCanteen) { + return false; + } + final l$getCanteens = getCanteens; + final lOther$getCanteens = other.getCanteens; + if (l$getCanteens.length != lOther$getCanteens.length) { + return false; + } + for (int i = 0; i < l$getCanteens.length; i++) { + final l$getCanteens$entry = l$getCanteens[i]; + final lOther$getCanteens$entry = lOther$getCanteens[i]; + if (l$getCanteens$entry != lOther$getCanteens$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetCanteen on Query$GetCanteen { + CopyWith$Query$GetCanteen get copyWith => + CopyWith$Query$GetCanteen( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetCanteen { + factory CopyWith$Query$GetCanteen( + Query$GetCanteen instance, + TRes Function(Query$GetCanteen) then, + ) = _CopyWithImpl$Query$GetCanteen; + + factory CopyWith$Query$GetCanteen.stub(TRes res) = + _CopyWithStubImpl$Query$GetCanteen; + + TRes call({ + Fragment$canteen? getCanteen, + List? getCanteens, + String? $__typename, + }); + CopyWith$Fragment$canteen get getCanteen; + TRes getCanteens( + Iterable Function( + Iterable>) + _fn); +} + +class _CopyWithImpl$Query$GetCanteen + implements CopyWith$Query$GetCanteen { + _CopyWithImpl$Query$GetCanteen( + this._instance, + this._then, + ); + + final Query$GetCanteen _instance; + + final TRes Function(Query$GetCanteen) _then; + + static const _undefined = {}; + + TRes call({ + Object? getCanteen = _undefined, + Object? getCanteens = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetCanteen( + getCanteen: getCanteen == _undefined + ? _instance.getCanteen + : (getCanteen as Fragment$canteen?), + getCanteens: getCanteens == _undefined || getCanteens == null + ? _instance.getCanteens + : (getCanteens as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Fragment$canteen get getCanteen { + final local$getCanteen = _instance.getCanteen; + return local$getCanteen == null + ? CopyWith$Fragment$canteen.stub(_then(_instance)) + : CopyWith$Fragment$canteen( + local$getCanteen, (e) => call(getCanteen: e)); + } + + TRes getCanteens( + Iterable Function( + Iterable>) + _fn) => + call( + getCanteens: + _fn(_instance.getCanteens.map((e) => CopyWith$Fragment$canteen( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$GetCanteen + implements CopyWith$Query$GetCanteen { + _CopyWithStubImpl$Query$GetCanteen(this._res); + + TRes _res; + + call({ + Fragment$canteen? getCanteen, + List? getCanteens, + String? $__typename, + }) => + _res; + CopyWith$Fragment$canteen get getCanteen => + CopyWith$Fragment$canteen.stub(_res); + getCanteens(_fn) => _res; +} + +const documentNodeQueryGetCanteen = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'GetCanteen'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'canteenId')), + type: NamedTypeNode( + name: NameNode(value: 'UUID'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'getCanteen'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'canteenId'), + value: VariableNode(name: NameNode(value: 'canteenId')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'canteen'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'getCanteens'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'canteen'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitioncanteen, +]); +Query$GetCanteen _parserFn$Query$GetCanteen(Map data) => + Query$GetCanteen.fromJson(data); +typedef OnQueryComplete$Query$GetCanteen = FutureOr Function( + Map?, + Query$GetCanteen?, +); + +class Options$Query$GetCanteen extends graphql.QueryOptions { + Options$Query$GetCanteen({ + String? operationName, + required Variables$Query$GetCanteen variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetCanteen? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$GetCanteen? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$GetCanteen(data), + ), + onError: onError, + document: documentNodeQueryGetCanteen, + parserFn: _parserFn$Query$GetCanteen, + ); + + final OnQueryComplete$Query$GetCanteen? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$GetCanteen + extends graphql.WatchQueryOptions { + WatchOptions$Query$GetCanteen({ + String? operationName, + required Variables$Query$GetCanteen variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetCanteen? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryGetCanteen, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$GetCanteen, + ); +} + +class FetchMoreOptions$Query$GetCanteen extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$GetCanteen({ + required graphql.UpdateQuery updateQuery, + required Variables$Query$GetCanteen variables, + }) : super( + updateQuery: updateQuery, + variables: variables.toJson(), + document: documentNodeQueryGetCanteen, + ); +} + +extension ClientExtension$Query$GetCanteen on graphql.GraphQLClient { + Future> query$GetCanteen( + Options$Query$GetCanteen options) async => + await this.query(options); + graphql.ObservableQuery watchQuery$GetCanteen( + WatchOptions$Query$GetCanteen options) => + this.watchQuery(options); + void writeQuery$GetCanteen({ + required Query$GetCanteen data, + required Variables$Query$GetCanteen variables, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryGetCanteen), + variables: variables.toJson(), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Query$GetCanteen? readQuery$GetCanteen({ + required Variables$Query$GetCanteen variables, + bool optimistic = true, + }) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryGetCanteen), + variables: variables.toJson(), + ), + optimistic: optimistic, + ); + return result == null ? null : Query$GetCanteen.fromJson(result); + } +} + +graphql_flutter.QueryHookResult useQuery$GetCanteen( + Options$Query$GetCanteen options) => + graphql_flutter.useQuery(options); +graphql.ObservableQuery useWatchQuery$GetCanteen( + WatchOptions$Query$GetCanteen options) => + graphql_flutter.useWatchQuery(options); + +class Query$GetCanteen$Widget extends graphql_flutter.Query { + Query$GetCanteen$Widget({ + widgets.Key? key, + required Options$Query$GetCanteen options, + required graphql_flutter.QueryBuilder builder, + }) : super( + key: key, + options: options, + builder: builder, + ); +} diff --git a/app/lib/view_model/repository/interface/IServerAccess.dart b/app/lib/view_model/repository/interface/IServerAccess.dart index c705eb5d..dc033d04 100644 --- a/app/lib/view_model/repository/interface/IServerAccess.dart +++ b/app/lib/view_model/repository/interface/IServerAccess.dart @@ -62,4 +62,10 @@ abstract class IServerAccess { /// @param reportReason The reason why the image should be reported. /// @return The result of the update. It returns false, if the report failed. Future reportImage(ImageData image, ReportCategory reportReason); + + + /// This method returns the canteen specified or the default canteen if non existant or id null. + /// @param id id of canteen to get, can be null to get default canteen + /// @return canteen with id or default canteen + Future> getCanteenOrDefault(String? id); } \ No newline at end of file diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index 51f9c790..17866132 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -105,7 +105,7 @@ void main() async { var result = await serverAccess.updateAll(); var res = switch (result) { - Success(value: final mealplan) => expect(mealplan.toString(), ""), + Success(value: final mealplan) => true, // TODO Failure(value: final exception) => expect(exception.toString(), ""), }; }); @@ -116,7 +116,7 @@ void main() async { DateTime(2020, 11, 1)); var res = switch (result) { - Success(value: final mealplan) => expect(mealplan.toString(), ""), + Success(value: final mealplan) => true, //TODO Failure(value: final exception) => expect(exception.toString(), ""), }; }); @@ -138,8 +138,29 @@ void main() async { DateTime(2020, 11, 2)); var res = switch (result) { - Success(value: final mealplan) => expect(mealplan.additives, ""), - Failure(value: final exception) => expect(exception, true, reason: "exception while request"), + Success(value: final mealplan) => true, // TODO better testing? + Failure(value: final exception) => + expect(exception, true, reason: "exception while request"), + }; + }); + + test('get canteen', () async { + var result = await serverAccess.getCanteenOrDefault("ad860196-74aa-48c2-a032-e327ec338290"); + + var res = switch (result) { + Success(value: final canteen) => true, // TODO better testing? + Failure(value: final exception) => + expect(exception, true, reason: "exception while request"), + }; + }); + + test('get default canteen', () async { + var result = await serverAccess.getCanteenOrDefault(null); + + var res = switch (result) { + Success(value: final canteen) => true, // TODO better testing? + Failure(value: final exception) => + expect(exception, true, reason: "exception while request"), }; }); } From 806cdeac5d859e2f026a912476c24ca8eead1c7e Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Tue, 11 Jul 2023 22:26:15 +0200 Subject: [PATCH 037/184] first auth tests --- .../model/api_server/GraphQlServerAccess.dart | 13 ++++++---- .../api_server/GraphQlServerAccess_test.dart | 26 +++++++++++++++++-- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index 2f532352..f92f0400 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -30,14 +30,17 @@ import 'requests/mutations.graphql.dart'; class GraphQlServerAccess implements IServerAccess { final String _apiKey = const String.fromEnvironment('API_KEY'); - - final GraphQLClient _client = GraphQLClient( - link: HttpLink(const String.fromEnvironment('API_URL')), - cache: GraphQLCache()); + String currentAuth = ""; + late GraphQLClient _client; final String _clientId; final _dateFormat = DateFormat(dateFormatPattern); - GraphQlServerAccess._(this._clientId); + GraphQlServerAccess._(this._clientId) { + _client = GraphQLClient( + link: AuthLink(getToken: () => currentAuth) + .concat(HttpLink(const String.fromEnvironment('API_URL'))), + cache: GraphQLCache()); + } factory GraphQlServerAccess(String clientId) { return GraphQlServerAccess._(clientId); diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index 17866132..03172b5b 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -10,12 +10,33 @@ import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:intl/intl.dart'; void main() async { final GraphQlServerAccess serverAccess = GraphQlServerAccess("1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); + test('auth', () async { // TODO remove test + String token = ""; + final httpLink = HttpLink(const String.fromEnvironment('API_URL')); + final authLink = AuthLink(getToken: () => token); + + final GraphQLClient _client = + GraphQLClient(link: authLink.concat(httpLink), cache: GraphQLCache()); + + token = + "Mensa MWQ3NWQzODAtY2YwNy00ZWRiLTkwNDYtYTJkOTgxYmMyMTlkOmFiYzoxMjM="; + var result = await _client.query(QueryOptions( + document: gql("""{ + getMyAuth{clientId,apiIdent,hash} + } + """), + )); + expect(result.data?["getMyAuth"]?["clientId"], + "1d75d380-cf07-4edb-9046-a2d981bc219d"); + }); + test('environment endpoint defined', () { expect(const String.fromEnvironment('API_URL').isNotEmpty, true, reason: @@ -143,9 +164,10 @@ void main() async { expect(exception, true, reason: "exception while request"), }; }); - + test('get canteen', () async { - var result = await serverAccess.getCanteenOrDefault("ad860196-74aa-48c2-a032-e327ec338290"); + var result = await serverAccess + .getCanteenOrDefault("ad860196-74aa-48c2-a032-e327ec338290"); var res = switch (result) { Success(value: final canteen) => true, // TODO better testing? From feabc5303efee62b94297712b7d199dcf0a2652c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 12 Jul 2023 10:38:28 +0200 Subject: [PATCH 038/184] add named constructors that can apply changes form another class --- .../repository/data_classes/meal/Meal.dart | 55 +++++++++++++++++++ .../repository/data_classes/meal/Side.dart | 14 +++++ .../data_classes/mealplan/MealPlan.dart | 11 ++++ 3 files changed, 80 insertions(+) diff --git a/app/lib/view_model/repository/data_classes/meal/Meal.dart b/app/lib/view_model/repository/data_classes/meal/Meal.dart index 9706d1d4..8b9c7029 100644 --- a/app/lib/view_model/repository/data_classes/meal/Meal.dart +++ b/app/lib/view_model/repository/data_classes/meal/Meal.dart @@ -76,6 +76,60 @@ class Meal { _images = images, _isFavorite = isFavorite; + /// This constructor creates a meal with the committed values. + /// If any values are not committed these values are replaced with the values of the committed Meal. + /// @param meal The meal that is copied + /// @param id The id of the meal + /// @param name The name of the meal + /// @param foodType The food type of the meal + /// @param price The price of the meal + /// @param allergens The allergens of the meal + /// @param additives The additives of the meal + /// @param sides The sides of the meal + /// @param individualRating The individual rating of the meal + /// @param numberOfRatings The number of ratings of the meal + /// @param averageRating The average rating of the meal + /// @param lastServed The date when the meal was last served + /// @param nextServed The date when the meal will be served next + /// @param relativeFrequency The relative frequency of the meal + /// @param images The images of the meal + /// @param isFavorite The favorite status of the meal + /// @return A meal with the committed values + Meal.copy({ + required Meal meal, + String? id, + String? name, + FoodType? foodType, + Price? price, + List? allergens, + List? additives, + List? sides, + int? individualRating, + int? numberOfRatings, + double? averageRating, + DateTime? lastServed, + DateTime? nextServed, + Frequency? relativeFrequency, + List? images, + bool? isFavorite, + }) + : _id = id ?? meal.id, + _name = name ?? meal.name, + _foodType = foodType ?? meal.foodType, + _price = price ?? meal.price, + _allergens = allergens ?? meal.allergens, + _additives = additives ?? meal.additives, + _sides = sides ?? meal.sides, + _individualRating = individualRating ?? meal.individualRating, + _numberOfRatings = numberOfRatings ?? meal.numberOfRatings, + _averageRating = averageRating ?? meal.averageRating, + _lastServed = lastServed ?? meal.lastServed, + _nextServed = nextServed ?? meal.nextServed, + _relativeFrequency = relativeFrequency ?? meal.relativeFrequency, + _images = images ?? meal.images, + _isFavorite = isFavorite ?? meal.isFavorite; + + void setFavorite() { _isFavorite = true; } @@ -142,4 +196,5 @@ class Meal { List? get images => _images; bool get isFavorite => _isFavorite ?? false; + } diff --git a/app/lib/view_model/repository/data_classes/meal/Side.dart b/app/lib/view_model/repository/data_classes/meal/Side.dart index f634a3db..bc8beb33 100644 --- a/app/lib/view_model/repository/data_classes/meal/Side.dart +++ b/app/lib/view_model/repository/data_classes/meal/Side.dart @@ -25,6 +25,20 @@ class Side { _allergens = allergens, _additives = additives; + Side.copy({ + required Side side, + String? id, + String? name, + FoodType? foodType, + Price? price, + List? allergens, + List? additives, + }) : _id = id ?? side.id, + _name = name ?? side.name, + _foodType = foodType ?? side.foodType, + _price = price ?? side.price, + _allergens = allergens ?? side.allergens, + _additives = additives ?? side.additives; String get id => _id; diff --git a/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart b/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart index 1f774274..5ff6e647 100644 --- a/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart +++ b/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart @@ -18,6 +18,17 @@ class MealPlan { _isClosed = isClosed, _meals = meals; + MealPlan.copy({ + required MealPlan mealPlan, + DateTime? date, + Line? line, + bool? isClosed, + List? meals, + }) : _date = date ?? mealPlan.date, + _line = line ?? mealPlan.line, + _isClosed = isClosed ?? mealPlan.isClosed, + _meals = meals ?? mealPlan.meals; + DateTime get date => _date; Line get line => _line; From 308357397b530d9d0b742ab989c41cc6310ef367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 12 Jul 2023 10:38:48 +0200 Subject: [PATCH 039/184] fromating --- .../view_model/repository/error_handling/MealPlanException.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/view_model/repository/error_handling/MealPlanException.dart b/app/lib/view_model/repository/error_handling/MealPlanException.dart index 4aec92a2..c1c4121d 100644 --- a/app/lib/view_model/repository/error_handling/MealPlanException.dart +++ b/app/lib/view_model/repository/error_handling/MealPlanException.dart @@ -45,4 +45,4 @@ class NoDataException extends MealPlanException { /// This constructor creates a new NoDataException with the given message. /// @param message The message of the exception. NoDataException(this.message); -} \ No newline at end of file +} From fc3dfcb95566bcb8574cb62961921c49623235d0 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Wed, 12 Jul 2023 10:49:29 +0200 Subject: [PATCH 040/184] started adding NoDataException --- .../model/api_server/GraphQlServerAccess.dart | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index e4764330..124f2905 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:app/model/api_server/requests/querys.graphql.dart'; import 'package:app/model/api_server/requests/schema.graphql.dart'; import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; @@ -22,7 +20,6 @@ import 'package:app/view_model/repository/error_handling/MealPlanException.dart' import 'package:app/view_model/repository/error_handling/NoMealException.dart'; import 'package:app/view_model/repository/error_handling/Result.dart'; -import 'package:flutter/cupertino.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:intl/intl.dart'; @@ -147,9 +144,16 @@ class GraphQlServerAccess implements IServerAccess { final mealPlan = _convertMealPlan(result.parsedData?.getCanteens ?? [], date); - completeList.addAll(mealPlan); + switch (mealPlan) { + case Success(value: final mealPlan): + { + completeList.addAll(mealPlan); + } + case Failure(exception: _): + {} + } } - return Success(completeList); // TODO when return error? + return Success(completeList); } @override @@ -162,12 +166,11 @@ class GraphQlServerAccess implements IServerAccess { final mealData = result.parsedData?.getMeal; final exception = result.exception; if (exception != null) { - return Failure(exception); + return Failure(NoConnectionException(exception.toString())); } if (mealData == null) { - return Failure(NoMealException(// Todo correct exception - "Could not request meal from api: ${result.exception}")); + return Failure(NoMealException("Could not request meal from api")); } return Success(_convertMeal(mealData)); @@ -188,14 +191,13 @@ class GraphQlServerAccess implements IServerAccess { final mealPlan = _convertMealPlan( [result.parsedData?.getCanteen].nonNulls.toList(), date); - return Success(mealPlan); + return mealPlan; } static const defaultUuid = "00000000-0000-0000-0000-000000000000"; @override Future getDefaultCanteen() async { - final result = await _client .query$GetDefaultCanteen(Options$Query$GetDefaultCanteen()); @@ -215,30 +217,32 @@ class GraphQlServerAccess implements IServerAccess { // --------------- utility helper methods --------------- -List _convertMealPlan( - List mealPlan, DateTime date) { - return mealPlan +Result, MealPlanException> _convertMealPlan( + List mealPlans, DateTime date) { + return Success(mealPlans .expand( - (e) => e.lines + (mealPlan) => mealPlan.lines .asMap() - .map((idx, e) => MapEntry( + .map((idx, line) => MapEntry( idx, MealPlan( date: date, line: Line( - id: e.id, - name: e.name, - canteen: _convertCanteen(e.canteen), + id: line.id, + name: line.name, + canteen: _convertCanteen(line.canteen), position: idx), // mensa closed when data available but no meals in list - isClosed: e.meals?.isEmpty ?? false, - meals: e.meals?.map((e) => _convertMeal(e)).toList() ?? [], + isClosed: line.meals?.isEmpty ?? false, + // TODO return noDataException if one lines meals are null + meals: + line.meals?.map((e) => _convertMeal(e)).toList() ?? [], ), )) .values .toList(), ) - .toList(); + .toList()); } Meal _convertMeal(Fragment$mealInfo meal) { From 93d0b1f248033c6323f8776f0b919d47af4b1204 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Wed, 12 Jul 2023 12:29:29 +0200 Subject: [PATCH 041/184] imped meal frequency with new api --- .../model/api_server/GraphQlServerAccess.dart | 15 +++-- .../model/api_server/requests/querys.graphql | 3 +- .../api_server/requests/querys.graphql.dart | 64 +++++++++++++------ .../model/api_server/requests/schema.graphql | 7 +- 4 files changed, 63 insertions(+), 26 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index 124f2905..afbf5f7b 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -258,15 +258,22 @@ Meal _convertMeal(Fragment$mealInfo meal) { numberOfRatings: meal.ratings.ratingsCount, lastServed: _convertDate(meal.statistics.lastServed), nextServed: _convertDate(meal.statistics.nextServed), - relativeFrequency: _specifyFrequency(meal.statistics.relativeFrequency), + relativeFrequency: _specifyFrequency(meal.statistics), images: meal.images.map((e) => _convertImage(e)).toList(), sides: meal.sides.map((e) => _convertSide(e)).toList(), ); } -Frequency _specifyFrequency(double frequency) { - // TODO - return Frequency.normal; +const int _rareMealLimit = 2; + +Frequency _specifyFrequency(Fragment$mealInfo$statistics statistics) { + if (statistics.$new) { + return Frequency.newMeal; + } else if (statistics.frequency <= _rareMealLimit) { + return Frequency.rare; + } else { + return Frequency.normal; + } } DateTime? _convertDate(String? date) { diff --git a/app/lib/model/api_server/requests/querys.graphql b/app/lib/model/api_server/requests/querys.graphql index dbc79d56..6d9321f5 100644 --- a/app/lib/model/api_server/requests/querys.graphql +++ b/app/lib/model/api_server/requests/querys.graphql @@ -52,7 +52,8 @@ fragment mealInfo on Meal { statistics { lastServed nextServed - relativeFrequency + frequency + new } ratings { averageRating diff --git a/app/lib/model/api_server/requests/querys.graphql.dart b/app/lib/model/api_server/requests/querys.graphql.dart index ae08f2e5..3afd4334 100644 --- a/app/lib/model/api_server/requests/querys.graphql.dart +++ b/app/lib/model/api_server/requests/querys.graphql.dart @@ -1304,7 +1304,14 @@ const fragmentDefinitionmealInfo = FragmentDefinitionNode( selectionSet: null, ), FieldNode( - name: NameNode(value: 'relativeFrequency'), + name: NameNode(value: 'frequency'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'new'), alias: null, arguments: [], directives: [], @@ -1541,19 +1548,22 @@ class Fragment$mealInfo$statistics { Fragment$mealInfo$statistics({ this.lastServed, this.nextServed, - required this.relativeFrequency, + required this.frequency, + required this.$new, this.$__typename = 'MealStatistics', }); factory Fragment$mealInfo$statistics.fromJson(Map json) { final l$lastServed = json['lastServed']; final l$nextServed = json['nextServed']; - final l$relativeFrequency = json['relativeFrequency']; + final l$frequency = json['frequency']; + final l$$new = json['new']; final l$$__typename = json['__typename']; return Fragment$mealInfo$statistics( lastServed: (l$lastServed as String?), nextServed: (l$nextServed as String?), - relativeFrequency: (l$relativeFrequency as num).toDouble(), + frequency: (l$frequency as int), + $new: (l$$new as bool), $__typename: (l$$__typename as String), ); } @@ -1562,7 +1572,9 @@ class Fragment$mealInfo$statistics { final String? nextServed; - final double relativeFrequency; + final int frequency; + + final bool $new; final String $__typename; @@ -1572,8 +1584,10 @@ class Fragment$mealInfo$statistics { _resultData['lastServed'] = l$lastServed; final l$nextServed = nextServed; _resultData['nextServed'] = l$nextServed; - final l$relativeFrequency = relativeFrequency; - _resultData['relativeFrequency'] = l$relativeFrequency; + final l$frequency = frequency; + _resultData['frequency'] = l$frequency; + final l$$new = $new; + _resultData['new'] = l$$new; final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -1583,12 +1597,14 @@ class Fragment$mealInfo$statistics { int get hashCode { final l$lastServed = lastServed; final l$nextServed = nextServed; - final l$relativeFrequency = relativeFrequency; + final l$frequency = frequency; + final l$$new = $new; final l$$__typename = $__typename; return Object.hashAll([ l$lastServed, l$nextServed, - l$relativeFrequency, + l$frequency, + l$$new, l$$__typename, ]); } @@ -1612,9 +1628,14 @@ class Fragment$mealInfo$statistics { if (l$nextServed != lOther$nextServed) { return false; } - final l$relativeFrequency = relativeFrequency; - final lOther$relativeFrequency = other.relativeFrequency; - if (l$relativeFrequency != lOther$relativeFrequency) { + final l$frequency = frequency; + final lOther$frequency = other.frequency; + if (l$frequency != lOther$frequency) { + return false; + } + final l$$new = $new; + final lOther$$new = other.$new; + if (l$$new != lOther$$new) { return false; } final l$$__typename = $__typename; @@ -1647,7 +1668,8 @@ abstract class CopyWith$Fragment$mealInfo$statistics { TRes call({ String? lastServed, String? nextServed, - double? relativeFrequency, + int? frequency, + bool? $new, String? $__typename, }); } @@ -1668,7 +1690,8 @@ class _CopyWithImpl$Fragment$mealInfo$statistics TRes call({ Object? lastServed = _undefined, Object? nextServed = _undefined, - Object? relativeFrequency = _undefined, + Object? frequency = _undefined, + Object? $new = _undefined, Object? $__typename = _undefined, }) => _then(Fragment$mealInfo$statistics( @@ -1678,10 +1701,12 @@ class _CopyWithImpl$Fragment$mealInfo$statistics nextServed: nextServed == _undefined ? _instance.nextServed : (nextServed as String?), - relativeFrequency: - relativeFrequency == _undefined || relativeFrequency == null - ? _instance.relativeFrequency - : (relativeFrequency as double), + frequency: frequency == _undefined || frequency == null + ? _instance.frequency + : (frequency as int), + $new: $new == _undefined || $new == null + ? _instance.$new + : ($new as bool), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String), @@ -1697,7 +1722,8 @@ class _CopyWithStubImpl$Fragment$mealInfo$statistics call({ String? lastServed, String? nextServed, - double? relativeFrequency, + int? frequency, + bool? $new, String? $__typename, }) => _res; diff --git a/app/lib/model/api_server/requests/schema.graphql b/app/lib/model/api_server/requests/schema.graphql index 0a284803..b7b2a2c2 100644 --- a/app/lib/model/api_server/requests/schema.graphql +++ b/app/lib/model/api_server/requests/schema.graphql @@ -239,8 +239,11 @@ type MealStatistics { # The date of the next time the meal will be served. nextServed: NaiveDate - # The relative frequency with which the meal is offered. - relativeFrequency: Float! + # Count how often meal was served in last three months. + frequency: Int! + + # Whether this meal is new and was never served before. + new: Boolean! } # This enum lists all the types a meal can be of. From 77e33c916fdb2f27e7a1975b43d04d6008551468 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Wed, 12 Jul 2023 12:38:03 +0200 Subject: [PATCH 042/184] added exception when mealplan (line) has no data --- app/lib/model/api_server/GraphQlServerAccess.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index afbf5f7b..7041bb29 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -219,6 +219,12 @@ class GraphQlServerAccess implements IServerAccess { Result, MealPlanException> _convertMealPlan( List mealPlans, DateTime date) { + if (mealPlans + .expand((mealPlan) => mealPlan.lines) + .any((line) => line.meals == null)) { + return Failure(NoDataException("No data for a line.")); + } + return Success(mealPlans .expand( (mealPlan) => mealPlan.lines @@ -233,10 +239,8 @@ Result, MealPlanException> _convertMealPlan( canteen: _convertCanteen(line.canteen), position: idx), // mensa closed when data available but no meals in list - isClosed: line.meals?.isEmpty ?? false, - // TODO return noDataException if one lines meals are null - meals: - line.meals?.map((e) => _convertMeal(e)).toList() ?? [], + isClosed: line.meals!.isEmpty, + meals: line.meals!.map((e) => _convertMeal(e)).toList(), ), )) .values From f68d3d1f325c83dc35c5ca0dddaeaca8de9345d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 12 Jul 2023 12:40:40 +0200 Subject: [PATCH 043/184] change getMeal method --- app/lib/view_model/repository/interface/IServerAccess.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/lib/view_model/repository/interface/IServerAccess.dart b/app/lib/view_model/repository/interface/IServerAccess.dart index 940580ea..343349b9 100644 --- a/app/lib/view_model/repository/interface/IServerAccess.dart +++ b/app/lib/view_model/repository/interface/IServerAccess.dart @@ -1,4 +1,5 @@ import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; import '../data_classes/meal/ImageData.dart'; @@ -22,7 +23,7 @@ abstract class IServerAccess { /// This method returns the meal with the committed id. /// @param id The id of the meal /// @return The meal with the committed id or an error - Future> getMealFromId(String id); + Future> getMeal(Meal meal, Line line, DateTime date); /// This method updates the rating of the committed meal on the server. /// @param rating The new rating of the meal From 6ddc57b0c90295ddac13460f508898125120dd8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 12 Jul 2023 12:41:36 +0200 Subject: [PATCH 044/184] add methods for canteen, date and preferences --- .../view_model/logic/meal/IMealAccess.dart | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/lib/view_model/logic/meal/IMealAccess.dart b/app/lib/view_model/logic/meal/IMealAccess.dart index 4b4cdcf5..8b745919 100644 --- a/app/lib/view_model/logic/meal/IMealAccess.dart +++ b/app/lib/view_model/logic/meal/IMealAccess.dart @@ -14,7 +14,7 @@ abstract class IMealAccess { /// @param date The date of the mealplan /// @param canteen The canteen of the mealplan /// @return The mealplan of the committed date of the committed canteen or an error - Future, MealPlanException>> getMealPlan(DateTime date, Canteen canteen); + Future, MealPlanException>> getMealPlan(); /// This method returns the meal with the committed id form the database. /// If the requested data is not stored there, the data is requested from the server. @@ -28,7 +28,7 @@ abstract class IMealAccess { /// @param canteen The canteen of the mealplan /// @param context The context of the app used for displaying temporal messages. /// @return The result of the update - Future refreshMealplan(DateTime date, Canteen canteen, BuildContext context); + Future refreshMealplan(BuildContext context); /// This method updates the rating of the committed meal on the server. /// If the update is successful, a temporal success message is displayed. @@ -39,16 +39,31 @@ abstract class IMealAccess { /// @return The result of the update Future updateMealRating(int rating, Meal meal, BuildContext context); + /// This method returns the currently selected filter preferences. + /// @return The selected filter preferences. + Future getFilterPreferences(); + + /// This method resets the filter preferences. + Future resetFilterPreferences(); + /// This method changes the FilterPreferences of the app. /// @param filterPreferences The new FilterPreferences /// @return The result of the update Future changeFilterPreferences(FilterPreferences filterPreferences); + /// This method returns the currently selected Canteen. + /// @return The currently selected Canteen. + Future getCanteen(); + /// This method changes the last used canteen that is stored. /// @param canteen The new canteen /// @return The result of the update Future changeCanteen(Canteen canteen); + /// This method returns the currently displayed date. + /// @return The current displayed date. + Future getDate(); + /// This method changes the date of the mealplan that is displayed. /// @param date The new date /// @return The result of the update From 99d021706b61234a9b64bda24002a56563833f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 12 Jul 2023 12:55:44 +0200 Subject: [PATCH 045/184] add number of Occurances --- app/lib/view_model/repository/data_classes/meal/Meal.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/lib/view_model/repository/data_classes/meal/Meal.dart b/app/lib/view_model/repository/data_classes/meal/Meal.dart index 8b9c7029..b8e6edd9 100644 --- a/app/lib/view_model/repository/data_classes/meal/Meal.dart +++ b/app/lib/view_model/repository/data_classes/meal/Meal.dart @@ -24,6 +24,7 @@ class Meal { final DateTime? _nextServed; final Frequency? _relativeFrequency; final List? _images; + final int? _numberOfOccurance; bool? _isFavorite; /// This constructor creates a meal with the committed values. @@ -58,6 +59,7 @@ class Meal { DateTime? nextServed, Frequency? relativeFrequency, List? images, + int? numberOfOccurance, bool? isFavorite, }) : _id = id, @@ -74,6 +76,7 @@ class Meal { _nextServed = nextServed, _relativeFrequency = relativeFrequency, _images = images, + _numberOfOccurance = numberOfOccurance, _isFavorite = isFavorite; /// This constructor creates a meal with the committed values. @@ -111,6 +114,7 @@ class Meal { DateTime? nextServed, Frequency? relativeFrequency, List? images, + int? numberOfOccurance, bool? isFavorite, }) : _id = id ?? meal.id, @@ -127,9 +131,12 @@ class Meal { _nextServed = nextServed ?? meal.nextServed, _relativeFrequency = relativeFrequency ?? meal.relativeFrequency, _images = images ?? meal.images, + _numberOfOccurance = numberOfOccurance ?? meal._numberOfOccurance, _isFavorite = isFavorite ?? meal.isFavorite; + int? get numberOfOccurance => _numberOfOccurance; + void setFavorite() { _isFavorite = true; } From 9784b2b2ef48d159949703e4e92b7c431888acd3 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Wed, 12 Jul 2023 13:51:28 +0200 Subject: [PATCH 046/184] auth doc for hashing --- doc/ApiAuth.md | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/doc/ApiAuth.md b/doc/ApiAuth.md index a4095c27..227a6ca0 100644 --- a/doc/ApiAuth.md +++ b/doc/ApiAuth.md @@ -23,6 +23,44 @@ where `` is a [base 64](https://en.wikipedia.org/wiki/Base6 This string consists of three parts separated by `:`. | placeholder | description | | ---------------------- | ------------------------------------------------------------------------------------------------------------- | -| `` | Randomly generated [uuid](https://en.wikipedia.org/wiki/Universally_unique_identifier) identifying the client | +| `` | Randomly generated [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) identifying the client | | `` | First `10` symbols of an api key. It can be requested from TODO. | -| `` | Hash over TODO | \ No newline at end of file +| `` | Hash see below | + + +## Generating the hash +A [SHA-512](https://en.wikipedia.org/wiki/SHA-2) hash has to be generated over the following parameters (in that order): + +1. Name of mutation query as used in GraphQL (eg. `addUpvote`) as [UTF-8](https://en.wikipedia.org/wiki/UTF-8) +1. Client identifier as the the representation of the uuid (not string) +1. Complete API key as UTF-8 +1. Remaining mutation parameters, see below + +The resulting hash then gets again encoded as base 64 and can be inserted in the `` field. + +### Mutation parameters +After the api key, (_almost_*) all parameters have to get inserted in the order of their appearance in the graphql schema so that a mutation can not be changed in a meaningful way while keeping the same hash. + +Data format: +- **UUIDs** are encoded as their byte representation +- **strings** are encoded as UTF-8 +- **integers** are encoded as 32 bit unsigned integer in [_little endian_](https://en.wikipedia.org/wiki/Endianness) byte orderin +- **enums** are encoded as as UTF-8 String as they are named in the graphql schema (eg. `OFFENSIVE`) + +The following table gives a overview over the parameters which have to be included: + +| mutation | parameters | +| ---------------- | ------------------------------------------------------------------------------------------- | +| `addUpvote` | `imageId` as UUID | +| `addDownvote` | `imageId` as UUID | +| `removeUpvote` | `imageId` as UUID | +| `removeDownvote` | `imageId` as UUID | +| `addImage` | `mealId` as UUID, `imageUrl` as UTF-8 | +| `setRating` | `mealId` as UUID, `rating` as 32 bit little endian unsigned integer | +| `reportImage` | `imageId` as UUID, `reason` as UTF-8 string, named like in graphql schema (eg. `OFFENSIVE`) | + + + +| ❗Note | +| :-------------------------------------------------------------------------------------------------------- | +| As a result, only one mutation can be made in a single graphql query as each mutation needs its own hash. | \ No newline at end of file From db969a623cf6f916b36bd31d4a67c8a4967398e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 9 Jul 2023 19:55:50 +0200 Subject: [PATCH 047/184] initialise model mocks for testing --- app/test/model/mocks/ApiMock.dart | 4 ++++ app/test/model/mocks/DatabaseMock.dart | 4 ++++ app/test/model/mocks/LocalStorageMock.dart | 4 ++++ 3 files changed, 12 insertions(+) create mode 100644 app/test/model/mocks/ApiMock.dart create mode 100644 app/test/model/mocks/DatabaseMock.dart create mode 100644 app/test/model/mocks/LocalStorageMock.dart diff --git a/app/test/model/mocks/ApiMock.dart b/app/test/model/mocks/ApiMock.dart new file mode 100644 index 00000000..950f8487 --- /dev/null +++ b/app/test/model/mocks/ApiMock.dart @@ -0,0 +1,4 @@ +import 'package:app/view_model/repository/interface/IServerAccess.dart'; +import 'package:mocktail/mocktail.dart'; + +class ApiMock extends Mock implements IServerAccess {} \ No newline at end of file diff --git a/app/test/model/mocks/DatabaseMock.dart b/app/test/model/mocks/DatabaseMock.dart new file mode 100644 index 00000000..d7cefe25 --- /dev/null +++ b/app/test/model/mocks/DatabaseMock.dart @@ -0,0 +1,4 @@ +import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; +import 'package:mocktail/mocktail.dart'; + +class DatabaseMock extends Mock implements IDatabaseAccess {} \ No newline at end of file diff --git a/app/test/model/mocks/LocalStorageMock.dart b/app/test/model/mocks/LocalStorageMock.dart new file mode 100644 index 00000000..0c51e0d3 --- /dev/null +++ b/app/test/model/mocks/LocalStorageMock.dart @@ -0,0 +1,4 @@ +import 'package:app/view_model/repository/interface/ILocalStorage.dart'; +import 'package:mocktail/mocktail.dart'; + +class LocalStorageMock extends Mock implements ILocalStorage {} \ No newline at end of file From fb072b11ce9583beef9c45d38fe0b45a8811bfa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 9 Jul 2023 19:56:12 +0200 Subject: [PATCH 048/184] initialise FavoriteMealAccess.dart --- .../logic/favorite/FavoriteMealAccess.dart | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 app/lib/view_model/logic/favorite/FavoriteMealAccess.dart diff --git a/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart new file mode 100644 index 00000000..6146a5f4 --- /dev/null +++ b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart @@ -0,0 +1,52 @@ + +import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; +import 'package:flutter/material.dart'; + +class FavoriteMealAccess extends ChangeNotifier implements IFavoriteMealAccess { + final IDatabaseAccess _database; + + late List _favorites; + + FavoriteMealAccess(this._database) { + _init(); + } + + Future _init() async { + _favorites = await _database.getFavorites(); + } + + @override + Future addFavoriteMeal(Meal meal) async { + if (await isFavoriteMeal(meal)) { + return; + } + + await _database.addFavorite(meal); + _favorites.add(meal); + notifyListeners(); + } + + @override + Future> getFavoriteMeals() async { + return Future.value(_favorites); + } + + @override + Future isFavoriteMeal(Meal meal) async { + return Future.value(_favorites.map((favorite) => favorite.id).contains(meal.id)); + } + + @override + Future removeFavoriteMeal(Meal meal) async { + if (await isFavoriteMeal(meal) == false) { + return; + } + + await _database.deleteFavorite(meal); + _favorites.removeWhere((element) => element.id == meal.id); + notifyListeners(); + } + +} \ No newline at end of file From 9ec318619258828cb5ccdc10c168fec28811cd8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 9 Jul 2023 19:56:28 +0200 Subject: [PATCH 049/184] write test for FavoriteMealAccess.dart --- .../view-model/FavoriteMealAccessTest.dart | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 app/test/view-model/FavoriteMealAccessTest.dart diff --git a/app/test/view-model/FavoriteMealAccessTest.dart b/app/test/view-model/FavoriteMealAccessTest.dart new file mode 100644 index 00000000..e60dcd0f --- /dev/null +++ b/app/test/view-model/FavoriteMealAccessTest.dart @@ -0,0 +1,77 @@ +import 'package:app/view_model/logic/favorite/FavoriteMealAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/meal/Price.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../model/mocks/DatabaseMock.dart'; + +void main () { + final database = DatabaseMock(); + + late FavoriteMealAccess favorites; + + final meal1 = Meal(id: "1", + name: "vegan Meal", + foodType: FoodType.vegan, + price: Price(student: 200, employee: 300, pupil: 400, guest: 500)); + final meal2 = Meal(id: "42", + name: "vegetarian Meal", + foodType: FoodType.vegetarian, + price: Price(student: 200, employee: 300, pupil: 400, guest: 500)); + final meal3 = Meal(id: "12", + name: "fishi Meal", + foodType: FoodType.fish, + price: Price(student: 200, employee: 300, pupil: 400, guest: 500)); + var meals = [meal1, meal2]; + + setUp(() { + when(() => database.getFavorites()).thenAnswer((_) async => meals); + favorites = FavoriteMealAccess(database); + }); + + test("Test initialisation", () async { + expect(await favorites.getFavoriteMeals(), meals); + }); + + group("Favorite check", () { + test("is Favorite", () async { + expect(await favorites.isFavoriteMeal(meal1), true); + }); + + test("is no favorite", () async { + expect(await favorites.isFavoriteMeal(meal3), false); + }); + }); + + group("Test adding and removing meals", () { + test("add meal already in meals", () async { + await favorites.addFavoriteMeal(meal1); + verifyNever(() => database.addFavorite(meal1)); + expect(await favorites.getFavoriteMeals(), meals); + }); + + test("remove meal not in meals", () async { + await favorites.removeFavoriteMeal(meal3); + verifyNever(() => database.deleteFavorite(meal3)); + expect(await favorites.getFavoriteMeals(), meals); + }); + + test("add meal to meals (not in meals)", () async { + when(() => database.addFavorite(meal3)).thenAnswer((_) async {}); + await favorites.addFavoriteMeal(meal3); + verify(() => database.addFavorite(meal3)).called(1); + meals.add(meal3); + expect(await favorites.getFavoriteMeals(), meals); + }); + + test("remove meal from meals (that is in meals)", () async { + when(() => database.deleteFavorite(meal1)).thenAnswer((_) async {}); + await favorites.removeFavoriteMeal(meal1); + verify(() => database.deleteFavorite(meal1)).called(1); + meals.remove(meal1); + expect(await favorites.getFavoriteMeals(), meals); + }); + }); +} \ No newline at end of file From 7a5faa69c101258817716640f45bda16f51fb455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 9 Jul 2023 20:12:21 +0200 Subject: [PATCH 050/184] delete canteen and filterPreferences from PreferenceAccess and the Interface --- .../logic/preference/PreferenceAccess.dart | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 app/lib/view_model/logic/preference/PreferenceAccess.dart diff --git a/app/lib/view_model/logic/preference/PreferenceAccess.dart b/app/lib/view_model/logic/preference/PreferenceAccess.dart new file mode 100644 index 00000000..2d5f2cca --- /dev/null +++ b/app/lib/view_model/logic/preference/PreferenceAccess.dart @@ -0,0 +1,76 @@ +import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; +import 'package:app/view_model/repository/data_classes/settings/ColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; +import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; +import 'package:app/view_model/repository/interface/ILocalStorage.dart'; + +import 'package:flutter/foundation.dart'; + +class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { + late ILocalStorage access; + + late String _clientIdentifier; + late ColorScheme _colorScheme; + late PriceCategory _priceCategory; + late MealPlanFormat _mealPlanFormat; + + PreferenceAccess(this.access) { + _init(); + } + + Future _init() async { + _clientIdentifier = await access.getClientIdentifier() ?? ""; + _colorScheme = await access.getColorScheme() ?? ColorScheme.system; + _priceCategory = await access.getPriceCategory() ?? PriceCategory.student; + _mealPlanFormat = await access.getMealPlanFormat() ?? MealPlanFormat.grid; + } + + @override + Future getClientIdentifier() { + return Future.value(_clientIdentifier); + } + + @override + Future getColorScheme() async { + return Future.value(_colorScheme); + } + + @override + Future getMealPlanFormat() { + return Future.value(_mealPlanFormat); + } + + @override + Future getPriceCategory() { + return Future.value(_priceCategory); + } + + @override + Future setClientIdentifier(String identifier) async { + _clientIdentifier = identifier; + await setClientIdentifier(identifier); + notifyListeners(); + } + + @override + Future setColorScheme(ColorScheme scheme) async { + _colorScheme = scheme; + await setColorScheme(scheme); + notifyListeners(); + } + + @override + Future setMealPlanFormat(MealPlanFormat format) async { + _mealPlanFormat = format; + await setMealPlanFormat(format); + notifyListeners(); + } + + @override + Future setPriceCategory(PriceCategory category) async { + _priceCategory = category; + await setPriceCategory(category); + notifyListeners(); + } + +} \ No newline at end of file From 866a41f140334845a987cdad5c79726cec23cd55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 9 Jul 2023 21:24:11 +0200 Subject: [PATCH 051/184] test and corrections PreferenceAccess.dart --- .../logic/preference/PreferenceAccess.dart | 39 ++++--- app/test/view-model/PreferenceAccessTest.dart | 106 ++++++++++++++++++ 2 files changed, 131 insertions(+), 14 deletions(-) create mode 100644 app/test/view-model/PreferenceAccessTest.dart diff --git a/app/lib/view_model/logic/preference/PreferenceAccess.dart b/app/lib/view_model/logic/preference/PreferenceAccess.dart index 2d5f2cca..f85a93dd 100644 --- a/app/lib/view_model/logic/preference/PreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/PreferenceAccess.dart @@ -7,69 +7,80 @@ import 'package:app/view_model/repository/interface/ILocalStorage.dart'; import 'package:flutter/foundation.dart'; class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { - late ILocalStorage access; + late ILocalStorage _access; late String _clientIdentifier; late ColorScheme _colorScheme; late PriceCategory _priceCategory; late MealPlanFormat _mealPlanFormat; - PreferenceAccess(this.access) { - _init(); + // waits until _init() is finished initializing + late Future _doneInitialization; + + PreferenceAccess(this._access) { + _doneInitialization = _init(); } Future _init() async { - _clientIdentifier = await access.getClientIdentifier() ?? ""; - _colorScheme = await access.getColorScheme() ?? ColorScheme.system; - _priceCategory = await access.getPriceCategory() ?? PriceCategory.student; - _mealPlanFormat = await access.getMealPlanFormat() ?? MealPlanFormat.grid; + _clientIdentifier = await _access.getClientIdentifier() ?? ""; + _colorScheme = await _access.getColorScheme() ?? ColorScheme.system; + _priceCategory = await _access.getPriceCategory() ?? PriceCategory.student; + _mealPlanFormat = await _access.getMealPlanFormat() ?? MealPlanFormat.grid; } @override - Future getClientIdentifier() { + Future getClientIdentifier() async { + await _doneInitialization; return Future.value(_clientIdentifier); } @override Future getColorScheme() async { + await _doneInitialization; return Future.value(_colorScheme); } @override - Future getMealPlanFormat() { + Future getMealPlanFormat() async { + await _doneInitialization; return Future.value(_mealPlanFormat); } @override - Future getPriceCategory() { + Future getPriceCategory() async { + await _doneInitialization; return Future.value(_priceCategory); } @override Future setClientIdentifier(String identifier) async { + await _doneInitialization; _clientIdentifier = identifier; - await setClientIdentifier(identifier); + await _access.setClientIdentifier(identifier); notifyListeners(); } @override Future setColorScheme(ColorScheme scheme) async { + await _doneInitialization; _colorScheme = scheme; - await setColorScheme(scheme); + await _access.setColorScheme(scheme); notifyListeners(); } @override Future setMealPlanFormat(MealPlanFormat format) async { + await _doneInitialization; _mealPlanFormat = format; - await setMealPlanFormat(format); + await _access.setMealPlanFormat(format); notifyListeners(); } @override Future setPriceCategory(PriceCategory category) async { + await _doneInitialization; _priceCategory = category; - await setPriceCategory(category); + await _access.setPriceCategory(category); notifyListeners(); } diff --git a/app/test/view-model/PreferenceAccessTest.dart b/app/test/view-model/PreferenceAccessTest.dart new file mode 100644 index 00000000..7d2eb3a7 --- /dev/null +++ b/app/test/view-model/PreferenceAccessTest.dart @@ -0,0 +1,106 @@ +import 'package:app/view_model/logic/preference/PreferenceAccess.dart'; +import 'package:app/view_model/repository/data_classes/settings/ColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; +import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../model/mocks/LocalStorageMock.dart'; + +void main () { + final localStorage = LocalStorageMock(); + + late PreferenceAccess preferences; + late PreferenceAccess preferencesPredefined; + + setUp(() async { + when(() => localStorage.getClientIdentifier()).thenAnswer((_) => Future.value(null)); + when(() => localStorage.getColorScheme()).thenAnswer((_) => Future.value(null)); + when(() => localStorage.getPriceCategory()).thenAnswer((_) => Future.value(null)); + when(() => localStorage.getMealPlanFormat()).thenAnswer((_) => Future.value(null)); + + preferences = PreferenceAccess(localStorage); + }); + + group("initialization", () { + test("client identifier", () async { + expect(await preferences.getClientIdentifier(), ""); + }); + + test("color scheme", () async { + expect(await preferences.getColorScheme(), ColorScheme.system); + }); + + test("meal plan format", () async { + expect(await preferences.getMealPlanFormat(), MealPlanFormat.grid); + }); + + test("price category", () async { + expect(await preferences.getPriceCategory(), PriceCategory.student); + }); + }); + + group("test setters", () { + test("set ClientIdentifier", () async { + const string = "42"; + when(() => localStorage.setClientIdentifier(string)).thenAnswer((_) async {}); + + await preferences.setClientIdentifier(string); + verify(() => localStorage.setClientIdentifier(string)).called(1); + expect(await preferences.getClientIdentifier(), string); + }); + + test("set Color Scheme", () async { + const scheme = ColorScheme.light; + when(() => localStorage.setColorScheme(scheme)).thenAnswer((_) async {}); + + await preferences.setColorScheme(scheme); + verify(() => localStorage.setColorScheme(scheme)).called(1); + expect(await preferences.getColorScheme(), scheme); + }); + + test("set Meal Plan Format", () async { + const format = MealPlanFormat.list; + when(() => localStorage.setMealPlanFormat(format)).thenAnswer((_) async {}); + + await preferences.setMealPlanFormat(format); + verify(() => localStorage.setMealPlanFormat(format)).called(1); + expect(await preferences.getMealPlanFormat(), format); + }); + + test("set Price Category", () async { + const price = PriceCategory.employee; + when(() => localStorage.setPriceCategory(price)).thenAnswer((_) async {}); + + await preferences.setPriceCategory(price); + verify(() => localStorage.setPriceCategory(price)).called(1); + expect(await preferences.getPriceCategory(), price); + }); + }); + + group("initialization with non standard values", () { + when(() => localStorage.getClientIdentifier()).thenAnswer((_) => Future.value("42")); + when(() => localStorage.getColorScheme()).thenAnswer((_) => Future.value(ColorScheme.light)); + when(() => localStorage.getPriceCategory()).thenAnswer((_) => Future.value(PriceCategory.employee)); + when(() => localStorage.getMealPlanFormat()).thenAnswer((_) => Future.value(MealPlanFormat.list)); + + preferencesPredefined = PreferenceAccess(localStorage); + + test("client identifier", () async { + expect(await preferencesPredefined.getClientIdentifier(), "42"); + }); + + test("color scheme", () async { + expect(await preferencesPredefined.getColorScheme(), ColorScheme.light); + }); + + test("meal plan format", () async { + expect(await preferencesPredefined.getMealPlanFormat(), MealPlanFormat.list); + }); + + test("price category", () async { + expect(await preferencesPredefined.getPriceCategory(), PriceCategory.employee); + }); + }); + +} \ No newline at end of file From 9cdb157c4019bd53de7de057aa0f3997a8ed996e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 9 Jul 2023 21:25:31 +0200 Subject: [PATCH 052/184] corrections FavoriteMealAccess.dart --- .../logic/favorite/FavoriteMealAccess.dart | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart index 6146a5f4..1c705417 100644 --- a/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart +++ b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart @@ -9,16 +9,21 @@ class FavoriteMealAccess extends ChangeNotifier implements IFavoriteMealAccess { late List _favorites; + // waits until _init() is finished initializing + late Future _doneInitialization; + FavoriteMealAccess(this._database) { - _init(); + _doneInitialization = _init(); } - + Future _init() async { _favorites = await _database.getFavorites(); } @override Future addFavoriteMeal(Meal meal) async { + await _doneInitialization; + if (await isFavoriteMeal(meal)) { return; } @@ -30,16 +35,20 @@ class FavoriteMealAccess extends ChangeNotifier implements IFavoriteMealAccess { @override Future> getFavoriteMeals() async { + await _doneInitialization; return Future.value(_favorites); } @override Future isFavoriteMeal(Meal meal) async { + await _doneInitialization; return Future.value(_favorites.map((favorite) => favorite.id).contains(meal.id)); } @override Future removeFavoriteMeal(Meal meal) async { + await _doneInitialization; + if (await isFavoriteMeal(meal) == false) { return; } From 8a2178d0a4baa0acd11b4a61847e742b7d9f96db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 10 Jul 2023 14:12:38 +0200 Subject: [PATCH 053/184] first drafts --- .../logic/meal/CombinedMealPlanAccess.dart | 187 ++++++++++++++++++ app/test/view-model/MealPlanAccessTest.dart | 14 ++ 2 files changed, 201 insertions(+) create mode 100644 app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart create mode 100644 app/test/view-model/MealPlanAccessTest.dart diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart new file mode 100644 index 00000000..45da3096 --- /dev/null +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -0,0 +1,187 @@ + +import 'package:app/view_model/logic/meal/IMealAccess.dart'; +import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; +import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; +import 'package:app/view_model/repository/error_handling/Result.dart'; +import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; +import 'package:app/view_model/repository/interface/ILocalStorage.dart'; +import 'package:app/view_model/repository/interface/IServerAccess.dart'; +import 'package:flutter/material.dart'; + +class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { + final ILocalStorage _preferences; + final IServerAccess _api; + final IDatabaseAccess _database; + + late Canteen _activeCanteen; + late DateTime _displayedDate; + late List _mealplans = []; + late List _filteredMealplan; + late FilterPreferences _filter; + + CombinedMealPlanAccess( + this._preferences, + this._api, + this._database + ) { + _init(); + } + + + Future _init() async { + _displayedDate = DateTime.timestamp(); + _filter = await _preferences.getFilterPreferences() ?? FilterPreferences(); + + // get mealplans form server + List mealplans = switch (await _api.updateAll()) { + Success(value: final mealplan) => mealplan, + Failure(exception: final exception) => [] + }; + + // update all if connection to server is successful + if (mealplans.isNotEmpty) { + _database.updateAll(mealplans); + } + + // get canteen from string + // get canteen id from local storage + final canteenString = await _preferences.getCanteen(); + Canteen? canteen; + // get default canteen from server if canteen id not saved in local storage + if (canteenString == null) { + canteen = await _api.getDefaultCanteen(); + + // save canteen id in local storage + _preferences.setCanteen(canteen!.id); + } else { + // get canteen from database + // updates from server are already stored or there is no connection + canteen = await _database.getCanteenById(canteenString); + } + + // if canteen can not be set, no mealplans can be displayed + if (canteen == null) { + _mealplans = []; + _filteredMealplan = []; + return; + } + + _activeCanteen = canteen; + + // get mealplans from database + // new if update was successful + // old if update was not successful and old data is stored in the database + // no mealplan if update was not successful and old data is not stored in database + _mealplans = switch (await _database.getMealPlan(_displayedDate, canteen)) { + Success(value: final mealplan) => mealplan, + Failure(exception: final exception) => [] + }; + + // filter mealplans + _filterMealPlans(); + } + + @override + Future changeCanteen(Canteen canteen) async { + _activeCanteen = canteen; + _preferences.setCanteen(canteen.id); + + // TODO get new MealPlan + + notifyListeners(); + } + + @override + Future changeDate(DateTime date) async { + _displayedDate = date; + + // TODO get new MealPlan + + notifyListeners(); + } + + @override + Future changeFilterPreferences(FilterPreferences filterPreferences) { + // TODO: implement changeFilterPreferences + throw UnimplementedError(); + } + + @override + Future> getMealFromId(String id) { + // TODO: implement getMealFromId + throw UnimplementedError(); + } + + @override + Future, MealPlanException>> getMealPlan(DateTime date, Canteen canteen) async { + if (date != _displayedDate || canteen != _activeCanteen) { + await _setNewMealPlan(canteen, date); + } + + // no connection to server and no data + if (_mealplans.isEmpty) { + return Future.value(Failure(NoConnectionException("no connection"))); + } + + // everything is filtered + if (_filteredMealplan.isEmpty) { + return Future.value(Failure(FilteredMealException("all filtered"))); + } + + // canteen is closed + if (_mealplans.first.isClosed) { + return Future.value(Failure(ClosedCanteenException("canteen closed"))); + } + + // no data for date + if (_mealplans.first.meals.isEmpty) { + return Future.value(Failure(NoDataException("no data to date"))); + } + + // success + return Future.value(Success(_mealplans)); + } + + @override + Future refreshMealplan(DateTime date, Canteen canteen, BuildContext context) { + // TODO: implement refreshMealplan + throw UnimplementedError(); + } + + @override + Future updateMealRating(int rating, Meal meal, BuildContext context) { + // TODO: implement updateMealRating + throw UnimplementedError(); + } + + void _filterMealPlans() { + if (_mealplans.isEmpty) { + _filteredMealplan = []; + return; + } + + // TODO implement + throw UnimplementedError(); + } + + /// This method stores the mealplan of the committed canteen and date in the _mealplan attribute and filters the mealplan. + /// Therefore it checks first the database and if there is no data, it requests it from the server. + Future _setNewMealPlan(Canteen canteen, DateTime date) async { + final mealplan = switch (await _database.getMealPlan(date, canteen)) { + Success(value: final mealplans) => mealplans, + Failure(exception: final exception) => null + }; + + _mealplans = mealplan ?? switch (await _api.updateCanteen(canteen, date)) { + Success(value: final mealplans) => mealplans, + Failure(exception: final exception) => [] + }; + + _filterMealPlans(); + notifyListeners(); + } + +} \ No newline at end of file diff --git a/app/test/view-model/MealPlanAccessTest.dart b/app/test/view-model/MealPlanAccessTest.dart new file mode 100644 index 00000000..ce2295a9 --- /dev/null +++ b/app/test/view-model/MealPlanAccessTest.dart @@ -0,0 +1,14 @@ +import 'package:mocktail/mocktail.dart'; + +import '../model/mocks/ApiMock.dart'; +import '../model/mocks/DatabaseMock.dart'; +import '../model/mocks/LocalStorageMock.dart'; + +void main () { + final localStorage = LocalStorageMock(); + final api = ApiMock(); + final database = DatabaseMock(); + + + +} \ No newline at end of file From 0c248d65616b209ca4ddd2e8b28d18f5e64fba18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 10 Jul 2023 16:12:02 +0200 Subject: [PATCH 054/184] delete Canteen and FilterPreferences form IPreferenceAccess --- .../logic/preference/IPreferenceAccess.dart | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/app/lib/view_model/logic/preference/IPreferenceAccess.dart b/app/lib/view_model/logic/preference/IPreferenceAccess.dart index 5dc96ab6..35d18ac3 100644 --- a/app/lib/view_model/logic/preference/IPreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/IPreferenceAccess.dart @@ -1,6 +1,3 @@ - -import '../../repository/data_classes/filter/FilterPreferences.dart'; -import '../../repository/data_classes/mealplan/Canteen.dart'; import '../../repository/data_classes/settings/MensaColorScheme.dart'; import '../../repository/data_classes/settings/MealPlanFormat.dart'; import '../../repository/data_classes/settings/PriceCategory.dart'; @@ -16,28 +13,6 @@ abstract class IPreferenceAccess { /// @return The result of the update. Future setClientIdentifier(String identifier); - /// The saved FilterPreferences is returned. - /// @return The saved FilterPreferences. - Future getFilterPreferences(); - - /// The committed FilterPreferences is set. - /// @param filter The new FilterPreferences. - /// @return The result of the update. - Future setFilterPreferences(FilterPreferences filter); - - /// The FilterPreferences are reset to the default values. - /// @return The result of the update. - Future resetFilterPreferences(); - - /// The saved Canteen is returned. - /// @return The saved Canteen. - Future getCanteen(); - - /// The committed Canteen is set. - /// @param canteen The new Canteen. - /// @return The result of the update. - Future setCanteen(Canteen canteen); - /// The saved ColorScheme is returned. /// @return The saved ColorScheme. Future getColorScheme(); From ee7f2d161e28e27de6fee30a6a07cbc5de8fe694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 10 Jul 2023 16:14:24 +0200 Subject: [PATCH 055/184] add mocktail package for using mock classes --- app/pubspec.lock | 216 +++++++++++++++++++++++++++++++++++++++++++++++ app/pubspec.yaml | 1 + 2 files changed, 217 insertions(+) diff --git a/app/pubspec.lock b/app/pubspec.lock index 4df52b69..897c7fea 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + url: "https://pub.dev" + source: hosted + version: "61.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + url: "https://pub.dev" + source: hosted + version: "5.13.0" args: dependency: transitive description: @@ -49,6 +65,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.1" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + url: "https://pub.dev" + source: hosted + version: "1.6.3" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -125,6 +165,22 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" http: dependency: transitive description: @@ -133,6 +189,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.13.6" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" http_parser: dependency: transitive description: @@ -149,6 +213,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" js: dependency: transitive description: @@ -197,6 +269,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + mocktail: + dependency: "direct main" + description: + name: mocktail + sha256: "80a996cd9a69284b3dc521ce185ffe9150cde69767c2d3a0720147d93c0cef53" + url: "https://pub.dev" + source: hosted + version: "0.3.0" nested: dependency: transitive description: @@ -205,6 +293,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -293,6 +397,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" process: dependency: transitive description: @@ -309,6 +421,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" shared_preferences: dependency: "direct main" description: @@ -365,11 +485,59 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" source_span: dependency: transitive description: @@ -410,6 +578,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + test: + dependency: transitive + description: + name: test + sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + url: "https://pub.dev" + source: hosted + version: "1.24.1" test_api: dependency: transitive description: @@ -418,6 +594,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" + test_core: + dependency: transitive + description: + name: test_core + sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + url: "https://pub.dev" + source: hosted + version: "0.5.1" toml: dependency: transitive description: @@ -466,6 +650,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b8c67f5fa3897b122cf60fe9ff314f7b0ef71eab25c5f8b771480bc338f48823 + url: "https://pub.dev" + source: hosted + version: "11.7.2" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + url: "https://pub.dev" + source: hosted + version: "1.2.0" win32: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 20dddd17..b131648c 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -47,6 +47,7 @@ dependencies: shared_preferences: ^2.2.0 provider: ^6.0.5 + mocktail: ^0.3.0 dev_dependencies: flutter_test: From 6b5cfded83dcb16607d3bf02dafc866864f4b404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 10 Jul 2023 16:14:52 +0200 Subject: [PATCH 056/184] change ColorScheme to MensaColorScheme --- .../view_model/logic/preference/PreferenceAccess.dart | 10 +++++----- app/test/view-model/PreferenceAccessTest.dart | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/lib/view_model/logic/preference/PreferenceAccess.dart b/app/lib/view_model/logic/preference/PreferenceAccess.dart index f85a93dd..b464e7db 100644 --- a/app/lib/view_model/logic/preference/PreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/PreferenceAccess.dart @@ -1,5 +1,5 @@ import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; -import 'package:app/view_model/repository/data_classes/settings/ColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:app/view_model/repository/interface/ILocalStorage.dart'; @@ -10,7 +10,7 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { late ILocalStorage _access; late String _clientIdentifier; - late ColorScheme _colorScheme; + late MensaColorScheme _colorScheme; late PriceCategory _priceCategory; late MealPlanFormat _mealPlanFormat; @@ -23,7 +23,7 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { Future _init() async { _clientIdentifier = await _access.getClientIdentifier() ?? ""; - _colorScheme = await _access.getColorScheme() ?? ColorScheme.system; + _colorScheme = await _access.getColorScheme() ?? MensaColorScheme.system; _priceCategory = await _access.getPriceCategory() ?? PriceCategory.student; _mealPlanFormat = await _access.getMealPlanFormat() ?? MealPlanFormat.grid; } @@ -35,7 +35,7 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { } @override - Future getColorScheme() async { + Future getColorScheme() async { await _doneInitialization; return Future.value(_colorScheme); } @@ -61,7 +61,7 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { } @override - Future setColorScheme(ColorScheme scheme) async { + Future setColorScheme(MensaColorScheme scheme) async { await _doneInitialization; _colorScheme = scheme; await _access.setColorScheme(scheme); diff --git a/app/test/view-model/PreferenceAccessTest.dart b/app/test/view-model/PreferenceAccessTest.dart index 7d2eb3a7..70163a83 100644 --- a/app/test/view-model/PreferenceAccessTest.dart +++ b/app/test/view-model/PreferenceAccessTest.dart @@ -1,5 +1,5 @@ import 'package:app/view_model/logic/preference/PreferenceAccess.dart'; -import 'package:app/view_model/repository/data_classes/settings/ColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -28,7 +28,7 @@ void main () { }); test("color scheme", () async { - expect(await preferences.getColorScheme(), ColorScheme.system); + expect(await preferences.getColorScheme(), MensaColorScheme.system); }); test("meal plan format", () async { @@ -51,7 +51,7 @@ void main () { }); test("set Color Scheme", () async { - const scheme = ColorScheme.light; + const scheme = MensaColorScheme.light; when(() => localStorage.setColorScheme(scheme)).thenAnswer((_) async {}); await preferences.setColorScheme(scheme); @@ -80,7 +80,7 @@ void main () { group("initialization with non standard values", () { when(() => localStorage.getClientIdentifier()).thenAnswer((_) => Future.value("42")); - when(() => localStorage.getColorScheme()).thenAnswer((_) => Future.value(ColorScheme.light)); + when(() => localStorage.getColorScheme()).thenAnswer((_) => Future.value(MensaColorScheme.light)); when(() => localStorage.getPriceCategory()).thenAnswer((_) => Future.value(PriceCategory.employee)); when(() => localStorage.getMealPlanFormat()).thenAnswer((_) => Future.value(MealPlanFormat.list)); @@ -91,7 +91,7 @@ void main () { }); test("color scheme", () async { - expect(await preferencesPredefined.getColorScheme(), ColorScheme.light); + expect(await preferencesPredefined.getColorScheme(), MensaColorScheme.light); }); test("meal plan format", () async { From bc7caf8c45994e41f65a8bf5a89ae9778b56cf67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 12 Jul 2023 10:37:32 +0200 Subject: [PATCH 057/184] implementations --- .../logic/meal/CombinedMealPlanAccess.dart | 362 ++++++++++++++---- 1 file changed, 296 insertions(+), 66 deletions(-) diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 45da3096..7954c751 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -1,16 +1,22 @@ - import 'package:app/view_model/logic/meal/IMealAccess.dart'; import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; +import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; +import 'package:app/view_model/repository/error_handling/NoMealException.dart'; import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; import 'package:app/view_model/repository/interface/ILocalStorage.dart'; import 'package:app/view_model/repository/interface/IServerAccess.dart'; import 'package:flutter/material.dart'; +import '../../repository/data_classes/meal/Side.dart'; + + +// todo wann ist das Zeug wirklich zu? einfach rauslöschen? class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { final ILocalStorage _preferences; final IServerAccess _api; @@ -18,32 +24,28 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { late Canteen _activeCanteen; late DateTime _displayedDate; - late List _mealplans = []; - late List _filteredMealplan; + late List _mealPlans = []; + late List _filteredMealPlan; late FilterPreferences _filter; + late bool _noDataYet = false; - CombinedMealPlanAccess( - this._preferences, - this._api, - this._database - ) { + CombinedMealPlanAccess(this._preferences, this._api, this._database) { _init(); } - Future _init() async { _displayedDate = DateTime.timestamp(); _filter = await _preferences.getFilterPreferences() ?? FilterPreferences(); - // get mealplans form server - List mealplans = switch (await _api.updateAll()) { + // get meal plans form server + List mealPlans = switch (await _api.updateAll()) { Success(value: final mealplan) => mealplan, - Failure(exception: final exception) => [] + Failure() => [] }; // update all if connection to server is successful - if (mealplans.isNotEmpty) { - _database.updateAll(mealplans); + if (mealPlans.isNotEmpty) { + _database.updateAll(mealPlans); } // get canteen from string @@ -62,126 +64,354 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { canteen = await _database.getCanteenById(canteenString); } - // if canteen can not be set, no mealplans can be displayed + // if canteen can not be set, no meal plans can be displayed if (canteen == null) { - _mealplans = []; - _filteredMealplan = []; + _mealPlans = []; + _filteredMealPlan = []; return; } _activeCanteen = canteen; - // get mealplans from database + // get meal plans from database // new if update was successful // old if update was not successful and old data is stored in the database - // no mealplan if update was not successful and old data is not stored in database - _mealplans = switch (await _database.getMealPlan(_displayedDate, canteen)) { + // no meal-plan if update was not successful and old data is not stored in database + _mealPlans = switch (await _database.getMealPlan(_displayedDate, canteen)) { Success(value: final mealplan) => mealplan, - Failure(exception: final exception) => [] + Failure() => [] }; - // filter mealplans + // filter meal plans _filterMealPlans(); } + // todo get date + // todo get canteen + // todo get filter preferences + // todo reset filter preferences + @override Future changeCanteen(Canteen canteen) async { _activeCanteen = canteen; - _preferences.setCanteen(canteen.id); - - // TODO get new MealPlan - notifyListeners(); + // requests and stores the new meal plan + // filters meal plan + // notifies listener + _setNewMealPlan(); } @override Future changeDate(DateTime date) async { _displayedDate = date; - // TODO get new MealPlan - - notifyListeners(); + // requests and stores the new meal plan + // filters meal plan + // notifies listener + _setNewMealPlan(); } @override - Future changeFilterPreferences(FilterPreferences filterPreferences) { - // TODO: implement changeFilterPreferences - throw UnimplementedError(); + Future changeFilterPreferences( + FilterPreferences filterPreferences) async { + _filter = filterPreferences; + _filterMealPlans(); + notifyListeners(); } @override - Future> getMealFromId(String id) { - // TODO: implement getMealFromId - throw UnimplementedError(); - } + Future> getMealFromId(String id) async { + final meal = switch (await _api.getMealFromId(id)) { + Success(value: final value) => value, + Failure() => null + }; - @override - Future, MealPlanException>> getMealPlan(DateTime date, Canteen canteen) async { - if (date != _displayedDate || canteen != _activeCanteen) { - await _setNewMealPlan(canteen, date); + if (meal != null) { + return Future.value(Success(meal)); + } + + // get data form database if it is stored there + final mealDatabase = await _database.getMealFavorite(id); + + switch (mealDatabase) { + case Success(): + return mealDatabase; + case Failure(): + return Future.value(Failure(NoMealException("meal not found"))); } + } + @override + Future, MealPlanException>> getMealPlan() async { // no connection to server and no data - if (_mealplans.isEmpty) { + if (_mealPlans.isEmpty) { return Future.value(Failure(NoConnectionException("no connection"))); } // everything is filtered - if (_filteredMealplan.isEmpty) { + if (_filteredMealPlan.isEmpty) { return Future.value(Failure(FilteredMealException("all filtered"))); } // canteen is closed - if (_mealplans.first.isClosed) { + if (_mealPlans.first.isClosed) { return Future.value(Failure(ClosedCanteenException("canteen closed"))); } // no data for date - if (_mealplans.first.meals.isEmpty) { + if (_noDataYet) { return Future.value(Failure(NoDataException("no data to date"))); } // success - return Future.value(Success(_mealplans)); + return Future.value(Success(_filteredMealPlan)); } @override - Future refreshMealplan(DateTime date, Canteen canteen, BuildContext context) { - // TODO: implement refreshMealplan - throw UnimplementedError(); + Future refreshMealplan(BuildContext context) async { + final mealPlan = await _getMealPlanFromServer(); + + if (_mealPlans.isEmpty) { + // show snack-bar + // TODO get good text + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text("Error Refresh"))); + + return Future.value(); + } + + _mealPlans = mealPlan; + _filterMealPlans(); + + notifyListeners(); } @override - Future updateMealRating(int rating, Meal meal, BuildContext context) { - // TODO: implement updateMealRating - throw UnimplementedError(); + Future updateMealRating( + int rating, Meal meal, BuildContext context) async { + final result = await _api.updateMealRating(rating, meal); + final messenger = ScaffoldMessenger.of(context); + + if (!result) { + // todo display error in snack-bar + messenger.showSnackBar(SnackBar(content: Text("Error Rating"))); + return Future.value(); + } + + _changeRatingOfMeal(meal, rating); + + // todo display snack-bar success + messenger.showSnackBar(SnackBar(content: Text("Success Rating"))); + + notifyListeners(); } - void _filterMealPlans() { - if (_mealplans.isEmpty) { - _filteredMealplan = []; + void _changeRatingOfMeal(Meal changedMeal, int rating) { + for (final mealPlan in _mealPlans) { + // check if right meal plan + final result = + mealPlan.meals.map((meal) => meal.id).contains(changedMeal.id); + + if (result) { + // remove outdated meal, add new meal + mealPlan.meals.removeWhere((element) => element.id == changedMeal.id); + mealPlan.meals + .add(Meal.copy(meal: changedMeal, individualRating: rating)); + return; + } + } + } + + Future _filterMealPlans() async { + // any kind of failure so no data is present + if (_mealPlans.isEmpty || _noDataYet || _mealPlans.first.isClosed) { + _filteredMealPlan = []; return; } - // TODO implement - throw UnimplementedError(); + List newFilteredMealPlan = []; + + for (final mealPlan in _mealPlans) { + MealPlan filteredMealPlan = MealPlan.copy(mealPlan: mealPlan, meals: []); + + for (final meal in mealPlan.meals) { + // check if meal would be displayed + if (await _filterMeal(meal)) { + // add meal if displayed + Meal filteredMeal = Meal.copy(meal: meal, sides: []); + filteredMealPlan.meals.add(filteredMeal); + + // filter sides of copied meal + _filterSides(filteredMeal, meal.sides); + } + } + + if (mealPlan.meals.isEmpty) { + newFilteredMealPlan.remove(mealPlan); + } + } + + _filteredMealPlan = newFilteredMealPlan; + } + + Future _filterSides(Meal meal, List? sides) async { + for (final side in sides!) { + if (await _filterSide(side)) { + meal.sides!.add(side); + } + } + } + + Future _filterSide(Side side) async { + // check categories + if (!_filter.categories.contains(side.foodType)) { + return false; + } + + // check allergens + for (final allergen in side.allergens) { + if (!_filter.allergens.contains(allergen)) { + return false; + } + } + + // check price + if (side.price.getPrice( + await _preferences.getPriceCategory() ?? PriceCategory.student) > + _filter.price) { + return false; + } + + return true; + } + + Future _filterMeal(Meal meal) async { + // check categories + if (!_filter.categories.contains(meal.foodType)) { + return false; + } + + // check allergens + if (meal.allergens == null) { + return false; + } + + final allergens = meal.allergens ?? []; + for (final allergen in allergens) { + if (!_filter.allergens.contains(allergen)) { + return false; + } + } + + // check price + if (meal.price.getPrice( + await _preferences.getPriceCategory() ?? PriceCategory.student) > + _filter.price) { + return false; + } + + // check rating + if (meal.averageRating == null || meal.averageRating! < _filter.rating) { + return false; + } + + // check frequency + if (meal.relativeFrequency == null || + _filter.frequency.contains(meal.relativeFrequency)) { + return false; + } + + // check onlyFavorite + if (_filter.onlyFavorite && + !(await _database.getFavorites()).map((e) => e.id).contains(meal.id)) { + return false; + } + + return true; } - /// This method stores the mealplan of the committed canteen and date in the _mealplan attribute and filters the mealplan. + /// This method stores the meal plan of the committed canteen and date in the _mealPlan attribute and filters the meal plan. + /// It also notifies the listeners /// Therefore it checks first the database and if there is no data, it requests it from the server. - Future _setNewMealPlan(Canteen canteen, DateTime date) async { - final mealplan = switch (await _database.getMealPlan(date, canteen)) { + Future _setNewMealPlan() async { + _noDataYet = false; + _mealPlans = await _getMealPlanFromDatabaseAndServer(); + + _filterMealPlans(); + notifyListeners(); + } + + Future> _getMealPlanFromDatabaseAndServer() async { + final mealPlan = + switch (await _database.getMealPlan(_displayedDate, _activeCanteen)) { Success(value: final mealplans) => mealplans, - Failure(exception: final exception) => null + Failure(exception: final exception) => + _handleMealPlanErrorFromDatabase(exception) }; - _mealplans = mealplan ?? switch (await _api.updateCanteen(canteen, date)) { + if (mealPlan != null) { + return mealPlan; + } + + return await _getMealPlanFromServer(); + } + + List? _handleMealPlanErrorFromDatabase( + MealPlanException exception) { + switch (exception) { + case NoConnectionException(): + return []; + case FilteredMealException(): + return []; + case ClosedCanteenException(): + return [ + MealPlan( + date: _displayedDate, + line: Line( + id: "0", name: "name", canteen: _activeCanteen, position: 0), + isClosed: true, + meals: []) + ]; + case NoDataException(): + return null; + } + } + + Future> _getMealPlanFromServer() async { + _noDataYet = false; + return switch (await _api.updateCanteen(_activeCanteen, _displayedDate)) { Success(value: final mealplans) => mealplans, - Failure(exception: final exception) => [] + Failure(exception: final exception) => + _convertMealPlanExceptionToMealPlan(exception) }; - - _filterMealPlans(); - notifyListeners(); } -} \ No newline at end of file + List _convertMealPlanExceptionToMealPlan( + MealPlanException exception) { + switch (exception) { + case NoConnectionException(): + return []; + case FilteredMealException(): + return []; + case ClosedCanteenException(): + return [ + MealPlan( + date: _displayedDate, + line: Line( + id: "0", name: "name", canteen: _activeCanteen, position: 0), + isClosed: true, + meals: []) + ]; + case NoDataException(): + _noDataYet = true; + return [ + MealPlan( + date: _displayedDate, + line: Line( + id: "0", name: "name", canteen: _activeCanteen, position: 0), + isClosed: false, + meals: []) + ]; + } + } +} From 5200aca3350dd06e31b2995ce2a65a877c78895b Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Wed, 12 Jul 2023 14:38:34 +0200 Subject: [PATCH 058/184] first auth implementation --- .../model/api_server/GraphQlServerAccess.dart | 59 +++++++++++++++++-- app/pubspec.lock | 6 +- app/pubspec.yaml | 3 + .../api_server/GraphQlServerAccess_test.dart | 21 ++++++- 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index 7041bb29..e8129752 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:app/model/api_server/requests/querys.graphql.dart'; import 'package:app/model/api_server/requests/schema.graphql.dart'; import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; @@ -20,24 +23,29 @@ import 'package:app/view_model/repository/error_handling/MealPlanException.dart' import 'package:app/view_model/repository/error_handling/NoMealException.dart'; import 'package:app/view_model/repository/error_handling/Result.dart'; +import 'package:crypto/crypto.dart'; +import 'package:flutter/cupertino.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:intl/intl.dart'; +import 'package:convert/convert.dart'; +import 'package:uuid/uuid.dart'; import '../../view_model/repository/interface/IServerAccess.dart'; import 'requests/mutations.graphql.dart'; class GraphQlServerAccess implements IServerAccess { final String _apiKey = const String.fromEnvironment('API_KEY'); - String currentAuth = ""; - late GraphQLClient _client; + late String _currentAuth; + late final GraphQLClient _client; final String _clientId; final _dateFormat = DateFormat(dateFormatPattern); GraphQlServerAccess._(this._clientId) { _client = GraphQLClient( - link: AuthLink(getToken: () => currentAuth) + link: AuthLink(getToken: () => _currentAuth) .concat(HttpLink(const String.fromEnvironment('API_URL'))), cache: GraphQLCache()); + _authenticate(""); } factory GraphQlServerAccess(String clientId) { @@ -48,7 +56,10 @@ class GraphQlServerAccess implements IServerAccess { @override Future deleteDownvote(ImageData image) async { - // TODO auth + var requestName = "removeDownvote"; + var hash = _generateHashOfParameters(requestName, _serializeUuid(image.id)); + _authenticate(hash); + final result = await _client.mutate$RemoveDownvote( Options$Mutation$RemoveDownvote( variables: Variables$Mutation$RemoveDownvote(imageId: image.id))); @@ -213,6 +224,46 @@ class GraphQlServerAccess implements IServerAccess { } return _convertCanteen(canteen); } + + // --------------- auth --------------- + + void _authenticate(String hash) { + var authString = "$_clientId:${_apiKey.substring(0, 10)}:$hash"; + var bytes = utf8.encode(authString); + var base64 = base64Encode(bytes); + _currentAuth = "Mensa $base64"; + } + + String _generateHashOfParameters(String mutationName, List parameterData) { + var hash = sha512.convert([ + ..._serializeString(mutationName), + ..._serializeUuid(_clientId), + ..._serializeString(_apiKey), + ...parameterData + ]); + + return base64.encode(hash.bytes); + } + + List _serializeString(String string) { + return utf8.encode(string); + } + + List _serializeInt(int value) { + return (ByteData(4)..setUint32(0, value, Endian.little)) + .buffer + .asUint8List() + .toList(); + } + + List _serializeReportReason(Enum$ReportReason reason) { + return _serializeString(reason.toString().split('.').last); + } + + List _serializeUuid(String uuid) { + return Uuid.parse(uuid); + } + } // --------------- utility helper methods --------------- diff --git a/app/pubspec.lock b/app/pubspec.lock index 7a8666bb..93291bf6 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -162,7 +162,7 @@ packages: source: hosted version: "1.2.4" convert: - dependency: transitive + dependency: "direct main" description: name: convert sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" @@ -170,7 +170,7 @@ packages: source: hosted version: "3.1.1" crypto: - dependency: transitive + dependency: "direct main" description: name: crypto sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab @@ -843,7 +843,7 @@ packages: source: hosted version: "1.3.2" uuid: - dependency: transitive + dependency: "direct main" description: name: uuid sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 26e8c973..8405b10d 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -48,6 +48,9 @@ dependencies: provider: ^6.0.5 graphql_flutter: ^5.1.1 + crypto: ^3.0.3 + convert: ^3.1.1 + uuid: ^3.0.7 dev_dependencies: flutter_test: diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index 293762ec..91979f8e 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:developer'; import 'package:app/model/api_server/GraphQlServerAccess.dart'; @@ -17,7 +18,8 @@ void main() async { final GraphQlServerAccess serverAccess = GraphQlServerAccess("1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); - test('auth', () async { // TODO remove test + test('auth at server', () async { + // TODO remove test String token = ""; final httpLink = HttpLink(const String.fromEnvironment('API_URL')); final authLink = AuthLink(getToken: () => token); @@ -166,9 +168,22 @@ void main() async { }); test('get canteen', () async { - var canteen = await serverAccess - .getDefaultCanteen(); + var canteen = await serverAccess.getDefaultCanteen(); expect(canteen != null, true); }); + + // TODO remove + test('transform auth', () async { + var clientId = "1d75d380-cf07-4edb-9046-a2d981bc219d"; + var apiKey = "abc"; + var hash = "123"; + + var authString = "${clientId}:${apiKey.substring(0, 3)}:$hash"; + var bytes = utf8.encode(authString); + var base64 = base64Encode(bytes); + + expect( + base64, "MWQ3NWQzODAtY2YwNy00ZWRiLTkwNDYtYTJkOTgxYmMyMTlkOmFiYzoxMjM="); + }); } From 04f8daaf9e74d3a13ea101c833369db74f2faf69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 12 Jul 2023 15:25:10 +0200 Subject: [PATCH 059/184] documentation --- app/lib/view_model/repository/data_classes/meal/Meal.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/view_model/repository/data_classes/meal/Meal.dart b/app/lib/view_model/repository/data_classes/meal/Meal.dart index b8e6edd9..76ae119e 100644 --- a/app/lib/view_model/repository/data_classes/meal/Meal.dart +++ b/app/lib/view_model/repository/data_classes/meal/Meal.dart @@ -24,7 +24,7 @@ class Meal { final DateTime? _nextServed; final Frequency? _relativeFrequency; final List? _images; - final int? _numberOfOccurance; + final int? _numberOfOccurance; // is not stored in database bool? _isFavorite; /// This constructor creates a meal with the committed values. From 1d68e3b207939519832db7aede9e52af73d2cb18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 12 Jul 2023 15:43:09 +0200 Subject: [PATCH 060/184] change of signature --- app/lib/view_model/logic/meal/IMealAccess.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/view_model/logic/meal/IMealAccess.dart b/app/lib/view_model/logic/meal/IMealAccess.dart index 8b745919..2492421c 100644 --- a/app/lib/view_model/logic/meal/IMealAccess.dart +++ b/app/lib/view_model/logic/meal/IMealAccess.dart @@ -20,7 +20,7 @@ abstract class IMealAccess { /// If the requested data is not stored there, the data is requested from the server. /// @param id The id of the meal /// @return The meal with the committed id or an error - Future> getMealFromId(String id); + Future> getWholeFavorite(String id); /// This method updates all meal plans of the committed date of the committed canteen. /// If the connection to the server fails, an temporal error message is displayed. From a566ce2a2f63c6c0da4f274249cd4680a03f7617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 12 Jul 2023 15:44:54 +0200 Subject: [PATCH 061/184] add methods for favorite access --- .../repository/interface/IDatabaseAccess.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/lib/view_model/repository/interface/IDatabaseAccess.dart b/app/lib/view_model/repository/interface/IDatabaseAccess.dart index 36cc04c7..7ff60a19 100644 --- a/app/lib/view_model/repository/interface/IDatabaseAccess.dart +++ b/app/lib/view_model/repository/interface/IDatabaseAccess.dart @@ -1,3 +1,4 @@ +import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; import '../data_classes/meal/Meal.dart'; @@ -23,6 +24,16 @@ abstract class IDatabaseAccess { /// @return The favorite meal with the committed id or an error Future> getMealFavorite(String id); + /// This method returns a favorite meals line. + /// @param the meal whose line is requested + /// @return the line that once offered the meal + Future getFavoriteMealsLine(Meal meal); + + /// this method returns a favorite meals date. + /// @param the meal whose date is requested + /// @return the date that once had offered the meal + Future getFavoriteMealsDate(Meal meal); + /// This method adds a favorite. If the favorite does already exists, it does nothing. /// @param meal The meal that should be added as favorite /// @return The result of the update From 6fadab142831247845ccf4b147236d58eeb097fc Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Wed, 12 Jul 2023 19:12:15 +0200 Subject: [PATCH 062/184] added auth to all mutations --- .../model/api_server/GraphQlServerAccess.dart | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index e8129752..34004d37 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -56,20 +56,25 @@ class GraphQlServerAccess implements IServerAccess { @override Future deleteDownvote(ImageData image) async { - var requestName = "removeDownvote"; - var hash = _generateHashOfParameters(requestName, _serializeUuid(image.id)); + const requestName = "removeDownvote"; + final hash = + _generateHashOfParameters(requestName, _serializeUuid(image.id)); _authenticate(hash); final result = await _client.mutate$RemoveDownvote( Options$Mutation$RemoveDownvote( variables: Variables$Mutation$RemoveDownvote(imageId: image.id))); - final parsedData = result.parsedData; - return parsedData?.removeDownvote ?? false; + + return result.parsedData?.removeDownvote ?? false; } @override Future deleteUpvote(ImageData image) async { - // TODO auth + const requestName = "removeUpvote"; + final hash = + _generateHashOfParameters(requestName, _serializeUuid(image.id)); + _authenticate(hash); + final result = await _client.mutate$RemoveUpvote( Options$Mutation$RemoveUpvote( variables: Variables$Mutation$RemoveUpvote(imageId: image.id))); @@ -79,7 +84,11 @@ class GraphQlServerAccess implements IServerAccess { @override Future downvoteImage(ImageData image) async { - // TODO auth + const requestName = "addDownvote"; + final hash = + _generateHashOfParameters(requestName, _serializeUuid(image.id)); + _authenticate(hash); + final result = await _client.mutate$AddDownvote( Options$Mutation$AddDownvote( variables: Variables$Mutation$AddDownvote(imageId: image.id))); @@ -89,7 +98,11 @@ class GraphQlServerAccess implements IServerAccess { @override Future upvoteImage(ImageData image) async { - // TODO auth + const requestName = "addUpvote"; + final hash = + _generateHashOfParameters(requestName, _serializeUuid(image.id)); + _authenticate(hash); + final result = await _client.mutate$AddUpvote(Options$Mutation$AddUpvote( variables: Variables$Mutation$AddUpvote(imageId: image.id))); @@ -98,7 +111,11 @@ class GraphQlServerAccess implements IServerAccess { @override Future linkImage(String url, Meal meal) async { - // TODO: auth + const requestName = "addImage"; + final hash = _generateHashOfParameters( + requestName, [..._serializeUuid(meal.id), ..._serializeString(url)]); + _authenticate(hash); + final result = await _client.mutate$LinkImage(Options$Mutation$LinkImage( variables: Variables$Mutation$LinkImage(imageUrl: url, mealId: meal.id))); @@ -108,19 +125,30 @@ class GraphQlServerAccess implements IServerAccess { @override Future reportImage(ImageData image, ReportCategory reportReason) async { - // TODO: auth + final convertedReason = _convertToReportReason(reportReason); + + const requestName = "reportImage"; + final hash = _generateHashOfParameters(requestName, [ + ..._serializeUuid(image.id), + ..._serializeReportReason(convertedReason) + ]); + _authenticate(hash); + final result = await _client.mutate$ReportImage( Options$Mutation$ReportImage( variables: Variables$Mutation$ReportImage( - imageId: image.id, - reason: _convertToReportReason(reportReason)))); + imageId: image.id, reason: convertedReason))); return result.parsedData?.reportImage ?? false; } @override Future updateMealRating(int rating, Meal meal) async { - // TODO: auth + const requestName = "setRating"; + final hash = _generateHashOfParameters( + requestName, [..._serializeUuid(meal.id), ..._serializeInt(rating)]); + _authenticate(hash); + final result = await _client.mutate$UpdateRating( Options$Mutation$UpdateRating( variables: Variables$Mutation$UpdateRating( @@ -234,7 +262,8 @@ class GraphQlServerAccess implements IServerAccess { _currentAuth = "Mensa $base64"; } - String _generateHashOfParameters(String mutationName, List parameterData) { + String _generateHashOfParameters( + String mutationName, List parameterData) { var hash = sha512.convert([ ..._serializeString(mutationName), ..._serializeUuid(_clientId), @@ -263,7 +292,6 @@ class GraphQlServerAccess implements IServerAccess { List _serializeUuid(String uuid) { return Uuid.parse(uuid); } - } // --------------- utility helper methods --------------- From 90cda872b6bae3ecb119d0faa42e0a18c65d4050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 16 Jul 2023 14:47:36 +0200 Subject: [PATCH 063/184] rebase origin --- app/lib/view_model/logic/favorite/FavoriteMealAccess.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart index f29b9f60..1c705417 100644 --- a/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart +++ b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart @@ -16,10 +16,6 @@ class FavoriteMealAccess extends ChangeNotifier implements IFavoriteMealAccess { _doneInitialization = _init(); } - FavoriteMealAccess(this._database) { - _init(); - } - Future _init() async { _favorites = await _database.getFavorites(); } From f57c906dea4f8835596deea6002d98ef53607770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 16 Jul 2023 15:02:14 +0200 Subject: [PATCH 064/184] add setter for individual rating --- .../repository/data_classes/meal/ImageData.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/lib/view_model/repository/data_classes/meal/ImageData.dart b/app/lib/view_model/repository/data_classes/meal/ImageData.dart index 24c923be..85e64edc 100644 --- a/app/lib/view_model/repository/data_classes/meal/ImageData.dart +++ b/app/lib/view_model/repository/data_classes/meal/ImageData.dart @@ -3,11 +3,10 @@ class ImageData { final String _id; final String _url; final double _imageRank; - final int? _individualRating; final int _positiveRating; final int _negativeRating; - + int? _individualRating; /// This constructor creates an image with the committed values. /// @param id The id of the image @@ -35,7 +34,7 @@ class ImageData { int get positiveRating => _positiveRating; - int? get individualRating => _individualRating; + int get individualRating => _individualRating ?? 0; double get imageRank => _imageRank; @@ -43,6 +42,11 @@ class ImageData { String get id => _id; + + set individualRating(int value) { + _individualRating = value; + } + Map toMap() { return { 'imageID': _id, From 79b34db85ce09289bb46e208be8462ea0de9fea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 16 Jul 2023 15:11:44 +0200 Subject: [PATCH 065/184] change Image to ImageData --- app/lib/view_model/logic/image/IImageAccess.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/lib/view_model/logic/image/IImageAccess.dart b/app/lib/view_model/logic/image/IImageAccess.dart index 7e33bef1..9004e38f 100644 --- a/app/lib/view_model/logic/image/IImageAccess.dart +++ b/app/lib/view_model/logic/image/IImageAccess.dart @@ -1,4 +1,5 @@ +import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; import 'package:flutter/cupertino.dart'; @@ -21,7 +22,7 @@ abstract class IImageAccess { /// @param image The image that should be upvoted /// @param context The context of the app used for displaying temporal messages. /// @return The result of the update - Future upvoteImage(Image image, BuildContext context); + Future upvoteImage(ImageData image, BuildContext context); /// This method adds a downvote to the committed image on the server. /// If the update is successful, a temporal success message is displayed. @@ -29,7 +30,7 @@ abstract class IImageAccess { /// @param image The image that should be downvoted /// @param context The context of the app used for displaying temporal messages. /// @return The result of the update - Future downvoteImage(Image image, BuildContext context); + Future downvoteImage(ImageData image, BuildContext context); /// This method deletes an upvote from the committed image on the server. /// If the update is successful, a temporal success message is displayed. @@ -37,7 +38,7 @@ abstract class IImageAccess { /// @param image The image that should be unupvoted /// @param context The context of the app used for displaying temporal messages. /// @return The result of the update - Future deleteUpvote(Image image, BuildContext context); + Future deleteUpvote(ImageData image, BuildContext context); /// This method deletes a downvote from the committed image on the server. /// If the update is successful, a temporal success message is displayed. @@ -45,7 +46,7 @@ abstract class IImageAccess { /// @param image The image that should be undownvoted /// @param context The context of the app used for displaying temporal messages. /// @return The result of the update - Future deleteDownvote(Image image, BuildContext context); + Future deleteDownvote(ImageData image, BuildContext context); /// This method reports the committed image on the server. /// If the update is successful, a temporal success message is displayed. @@ -54,5 +55,5 @@ abstract class IImageAccess { /// @param reportReason The reason why the image is reported /// @param context The context of the app used for displaying temporal messages. /// @return The result of the update - Future reportImage(Image image, ReportCategory reportReason, BuildContext context); + Future reportImage(ImageData image, ReportCategory reportReason, BuildContext context); } \ No newline at end of file From 9d9344e423b86a98ffb4cd0c293f3a9a4b7bc84d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 16 Jul 2023 15:16:56 +0200 Subject: [PATCH 066/184] implementation --- .../view_model/logic/image/ImageAccess.dart | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/app/lib/view_model/logic/image/ImageAccess.dart b/app/lib/view_model/logic/image/ImageAccess.dart index e69de29b..a82c5c67 100644 --- a/app/lib/view_model/logic/image/ImageAccess.dart +++ b/app/lib/view_model/logic/image/ImageAccess.dart @@ -0,0 +1,95 @@ +import 'package:app/view_model/logic/image/IImageAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; +import 'package:app/view_model/repository/interface/IServerAccess.dart'; +import 'package:flutter/material.dart'; + +class ImageAccess extends ChangeNotifier implements IImageAccess { + final IServerAccess _api; + + ImageAccess(this._api); + + @override + Future deleteDownvote(ImageData image, BuildContext context) async { + final result = await _api.deleteDownvote(image); + + if (!result) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text("error"))); + return; + } + + notifyListeners(); + } + + @override + Future deleteUpvote(ImageData image, BuildContext context) async { + final result = await _api.deleteUpvote(image); + + if (!result) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text("error"))); + return; + } + + notifyListeners(); + } + + @override + Future downvoteImage(ImageData image, BuildContext context) async { + final result = await _api.downvoteImage(image); + + if (!result) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text("error"))); + return; + } + + notifyListeners(); + } + + @override + Future linkImage(String url, Meal meal, BuildContext context) async { + final result = await _api.linkImage(url, meal); + + if (!result) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text("error"))); + return; + } + + // todo aktualisieren? + + notifyListeners(); + } + + @override + Future reportImage(ImageData image, ReportCategory reportReason, + BuildContext context) async { + final result = await _api.reportImage(image, reportReason); + + if (!result) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text("error"))); + return; + } + + // todo wie wird es nicht mehr angezeigt + + notifyListeners(); + } + + @override + Future upvoteImage(ImageData image, BuildContext context) async { + final result = await _api.upvoteImage(image); + + if (!result) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text("error"))); + return; + } + + notifyListeners(); + } +} From 98b663860591dbaaedfe3666a4a6b1c3f77729fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 16 Jul 2023 18:03:42 +0200 Subject: [PATCH 067/184] documentation --- .../data_classes/filter/Frequency.dart | 3 ++ .../data_classes/meal/FoodType.dart | 2 +- .../data_classes/meal/ImageData.dart | 29 +++++++++++++++---- .../repository/data_classes/meal/Meal.dart | 6 ++++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/app/lib/view_model/repository/data_classes/filter/Frequency.dart b/app/lib/view_model/repository/data_classes/filter/Frequency.dart index 65fe80a5..a3c0fc4a 100644 --- a/app/lib/view_model/repository/data_classes/filter/Frequency.dart +++ b/app/lib/view_model/repository/data_classes/filter/Frequency.dart @@ -1,6 +1,9 @@ /// Enum for the frequency of a meal enum Frequency { + /// A meal is offered for the first time newMeal, + /// A meal is not offered often. rare, + /// The meal is not new and is not rare. normal } \ No newline at end of file diff --git a/app/lib/view_model/repository/data_classes/meal/FoodType.dart b/app/lib/view_model/repository/data_classes/meal/FoodType.dart index 3ecad008..f8e4b109 100644 --- a/app/lib/view_model/repository/data_classes/meal/FoodType.dart +++ b/app/lib/view_model/repository/data_classes/meal/FoodType.dart @@ -14,6 +14,6 @@ enum FoodType { vegetarian, /// the food is vegan vegan, - /// the food type is unspcified + /// the food type is unspecified unknown, } \ No newline at end of file diff --git a/app/lib/view_model/repository/data_classes/meal/ImageData.dart b/app/lib/view_model/repository/data_classes/meal/ImageData.dart index 85e64edc..12a1a944 100644 --- a/app/lib/view_model/repository/data_classes/meal/ImageData.dart +++ b/app/lib/view_model/repository/data_classes/meal/ImageData.dart @@ -1,3 +1,4 @@ +/// This class represents an image of a meal. class ImageData { final String _id; @@ -6,7 +7,7 @@ class ImageData { final int _positiveRating; final int _negativeRating; - int? _individualRating; + int _individualRating; /// This constructor creates an image with the committed values. /// @param id The id of the image @@ -26,27 +27,45 @@ class ImageData { }) : _id = id, _url = url, _imageRank = imageRank, - _individualRating = individualRating, + _individualRating = individualRating ?? 0, _positiveRating = positiveRating, _negativeRating = negativeRating; + /// The method returns the number of negative ratings. int get negativeRating => _negativeRating; + /// The method returns the number of positive ratings. int get positiveRating => _positiveRating; - int get individualRating => _individualRating ?? 0; + /// The method returns the individual rating of the image. + /// It is 0 if there is no rating, 1 for an upvote and -1 for a downvote. + int get individualRating => _individualRating; + /// The method returns the image rank calculated by the server. double get imageRank => _imageRank; + /// The method returns the url to the image at the image hoster. String get url => _url; + /// The method returns the id of the image. String get id => _id; + /// The method changes the value of [_individualRating] to the value of an upvote. + void upvote() { + _individualRating = 1; + } + + /// The method changes the value of [_individualRating] to the value of a downvote. + void downvote() { + _individualRating = -1; + } - set individualRating(int value) { - _individualRating = value; + /// The method changes the value of [_individualRating] to the value of no vote. + void deleteRating() { + _individualRating = 0; } + /// The method returns the information that are stored in a map. Map toMap() { return { 'imageID': _id, diff --git a/app/lib/view_model/repository/data_classes/meal/Meal.dart b/app/lib/view_model/repository/data_classes/meal/Meal.dart index 76ae119e..4eb0fe12 100644 --- a/app/lib/view_model/repository/data_classes/meal/Meal.dart +++ b/app/lib/view_model/repository/data_classes/meal/Meal.dart @@ -135,16 +135,20 @@ class Meal { _isFavorite = isFavorite ?? meal.isFavorite; + /// This method returns the number of occurences in three months. int? get numberOfOccurance => _numberOfOccurance; + /// This method adds the meal to the favorites. void setFavorite() { _isFavorite = true; } + /// This method deletes the meal from the favorites. void deleteFavorite() { _isFavorite = false; } + /// This method returns the information of the meal that are stored in the database. Map toMap() { return { 'mealID': _id, @@ -160,6 +164,7 @@ class Meal { }; } + /// This method returns the additives as a map. List> additiveToMap() { return _additives!.map((additive) => { 'mealID': _id, @@ -167,6 +172,7 @@ class Meal { }).toList(); } + /// This method returns the allerens as a map. List> allergenToMap() { return _allergens!.map((allergen) => { 'mealID': _id, From 571240d8c2877b9f7f127f83cea914e6932ca6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 17 Jul 2023 15:43:24 +0200 Subject: [PATCH 068/184] return string that is displayed and do not display it directly --- .../view_model/logic/image/IImageAccess.dart | 47 +++++++++---------- .../view_model/logic/meal/IMealAccess.dart | 15 +++--- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/app/lib/view_model/logic/image/IImageAccess.dart b/app/lib/view_model/logic/image/IImageAccess.dart index 9004e38f..65ca2ce4 100644 --- a/app/lib/view_model/logic/image/IImageAccess.dart +++ b/app/lib/view_model/logic/image/IImageAccess.dart @@ -1,59 +1,56 @@ import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; -import 'package:flutter/cupertino.dart'; import '../../repository/data_classes/meal/Meal.dart'; /// This class is the interface for the access to the image data. abstract class IImageAccess { /// This method links the committed url to the committed meal on the server. - /// If the update is successful, a temporal success message is displayed. - /// If the connection to the server fails, an temporal error message is displayed. + /// It returns a string that should be displayed in a temporal message. /// @param url The url of the image /// @param meal The meal that is shown on the image of the url /// @param context The context of the app used for displaying temporal messages. - /// @return The result of the update - Future linkImage(String url, Meal meal, BuildContext context); + /// @return The string that should be displayed in a temporal message + Future linkImage(String url, Meal meal); /// This method adds an upvote to the committed image on the server. - /// If the update is successful, a temporal success message is displayed. - /// If the connection to the server fails, an temporal error message is displayed. + /// It returns a non empty string that should be displayed in a temporal message, + /// or a null, saying that nothing should be printed. /// @param image The image that should be upvoted /// @param context The context of the app used for displaying temporal messages. - /// @return The result of the update - Future upvoteImage(ImageData image, BuildContext context); + /// @return The string that should be displayed in a temporal message or null + Future upvoteImage(ImageData image); /// This method adds a downvote to the committed image on the server. - /// If the update is successful, a temporal success message is displayed. - /// If the connection to the server fails, an temporal error message is displayed. + /// It returns a non empty string that should be displayed in a temporal message, + /// or a null, saying that nothing should be printed. /// @param image The image that should be downvoted /// @param context The context of the app used for displaying temporal messages. - /// @return The result of the update - Future downvoteImage(ImageData image, BuildContext context); + /// @return The string that should be displayed in a temporal message or null + Future downvoteImage(ImageData image); /// This method deletes an upvote from the committed image on the server. - /// If the update is successful, a temporal success message is displayed. - /// If the connection to the server fails, an temporal error message is displayed. + /// It returns a non empty string that should be displayed in a temporal message, + /// or a null, saying that nothing should be printed. /// @param image The image that should be unupvoted /// @param context The context of the app used for displaying temporal messages. - /// @return The result of the update - Future deleteUpvote(ImageData image, BuildContext context); + /// @return The string that should be displayed in a temporal message or null + Future deleteUpvote(ImageData image); /// This method deletes a downvote from the committed image on the server. - /// If the update is successful, a temporal success message is displayed. - /// If the connection to the server fails, an temporal error message is displayed. + /// It returns a non empty string that should be displayed in a temporal message, + /// or a null, saying that nothing should be printed. /// @param image The image that should be undownvoted /// @param context The context of the app used for displaying temporal messages. - /// @return The result of the update - Future deleteDownvote(ImageData image, BuildContext context); + /// @return The string that should be displayed in a temporal message or null + Future deleteDownvote(ImageData image); /// This method reports the committed image on the server. - /// If the update is successful, a temporal success message is displayed. - /// If the connection to the server fails, an temporal error message is displayed. + /// It returns a string that should be displayed in a temporal message. /// @param image The image that should be reported /// @param reportReason The reason why the image is reported /// @param context The context of the app used for displaying temporal messages. - /// @return The result of the update - Future reportImage(ImageData image, ReportCategory reportReason, BuildContext context); + /// @return The string that should be displayed in a temporal message + Future reportImage(ImageData image, ReportCategory reportReason); } \ No newline at end of file diff --git a/app/lib/view_model/logic/meal/IMealAccess.dart b/app/lib/view_model/logic/meal/IMealAccess.dart index 8b745919..a36277ac 100644 --- a/app/lib/view_model/logic/meal/IMealAccess.dart +++ b/app/lib/view_model/logic/meal/IMealAccess.dart @@ -1,7 +1,6 @@ import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; -import 'package:flutter/cupertino.dart'; import '../../repository/data_classes/meal/Meal.dart'; import '../../repository/data_classes/mealplan/Canteen.dart'; @@ -23,21 +22,21 @@ abstract class IMealAccess { Future> getMealFromId(String id); /// This method updates all meal plans of the committed date of the committed canteen. - /// If the connection to the server fails, an temporal error message is displayed. + /// It returns a string that should be displayed in a temporal message or null. /// @param date The date of the mealplan /// @param canteen The canteen of the mealplan /// @param context The context of the app used for displaying temporal messages. - /// @return The result of the update - Future refreshMealplan(BuildContext context); + /// @return It returns a string that should be displayed in a temporal message or null. + Future refreshMealplan(); /// This method updates the rating of the committed meal on the server. - /// If the update is successful, a temporal success message is displayed. - /// If the connection to the server fails, an temporal error message is displayed. + /// It returns a non empty string that should be displayed in a temporal message, + /// or a null, saying that nothing should be printed. /// @param rating The new rating of the meal /// @param meal The meal that should be updated /// @param context The context of the app used for displaying temporal messages. - /// @return The result of the update - Future updateMealRating(int rating, Meal meal, BuildContext context); + /// @return It returns a string that should be displayed in a temporal message. + Future updateMealRating(int rating, Meal meal); /// This method returns the currently selected filter preferences. /// @return The selected filter preferences. From 9fd92368c3185541ec3a8be1a7e9837f73781357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 17 Jul 2023 15:43:41 +0200 Subject: [PATCH 069/184] formating --- app/lib/view_model/repository/interface/IServerAccess.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/lib/view_model/repository/interface/IServerAccess.dart b/app/lib/view_model/repository/interface/IServerAccess.dart index 343349b9..c322f5c6 100644 --- a/app/lib/view_model/repository/interface/IServerAccess.dart +++ b/app/lib/view_model/repository/interface/IServerAccess.dart @@ -18,7 +18,8 @@ abstract class IServerAccess { /// @param date The date of the mealplan /// @param canteen The canteen of the mealplan /// @return The mealplan of the committed date of the committed canteen or an error - Future, MealPlanException>> updateCanteen(Canteen canteen, DateTime date); + Future, MealPlanException>> updateCanteen( + Canteen canteen, DateTime date); /// This method returns the meal with the committed id. /// @param id The id of the meal @@ -66,4 +67,4 @@ abstract class IServerAccess { /// This method requests the default canteen from the server. /// @return The default canteen or null if no connection could be established. Future getDefaultCanteen(); -} \ No newline at end of file +} From ec017c3a0c508a5a1c9221fc0ebc6b267b5cab01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 17 Jul 2023 15:52:08 +0200 Subject: [PATCH 070/184] return string instead of viewing temporal messages directly --- .../view_model/logic/image/ImageAccess.dart | 48 +++++++++---------- .../logic/meal/CombinedMealPlanAccess.dart | 22 +++------ 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/app/lib/view_model/logic/image/ImageAccess.dart b/app/lib/view_model/logic/image/ImageAccess.dart index a82c5c67..94fba7d2 100644 --- a/app/lib/view_model/logic/image/ImageAccess.dart +++ b/app/lib/view_model/logic/image/ImageAccess.dart @@ -5,91 +5,89 @@ import 'package:app/view_model/repository/data_classes/settings/ReportCategory.d import 'package:app/view_model/repository/interface/IServerAccess.dart'; import 'package:flutter/material.dart'; +// todo string for error and success class ImageAccess extends ChangeNotifier implements IImageAccess { final IServerAccess _api; ImageAccess(this._api); @override - Future deleteDownvote(ImageData image, BuildContext context) async { + Future deleteDownvote(ImageData image) async { final result = await _api.deleteDownvote(image); if (!result) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text("error"))); - return; + return "error"; } + image.deleteRating(); notifyListeners(); + return null; } @override - Future deleteUpvote(ImageData image, BuildContext context) async { + Future deleteUpvote(ImageData image) async { final result = await _api.deleteUpvote(image); if (!result) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text("error"))); - return; + return "error"; } + image.deleteRating(); notifyListeners(); + return null; } @override - Future downvoteImage(ImageData image, BuildContext context) async { + Future downvoteImage(ImageData image) async { final result = await _api.downvoteImage(image); if (!result) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text("error"))); - return; + return "error"; } + image.downvote(); notifyListeners(); + return null; } @override - Future linkImage(String url, Meal meal, BuildContext context) async { + Future linkImage(String url, Meal meal) async { final result = await _api.linkImage(url, meal); if (!result) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text("error"))); - return; + return "error"; } // todo aktualisieren? notifyListeners(); + return "success"; } @override - Future reportImage(ImageData image, ReportCategory reportReason, - BuildContext context) async { + Future reportImage(ImageData image, ReportCategory reportReason) async { final result = await _api.reportImage(image, reportReason); if (!result) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text("error"))); - return; + return "error"; } // todo wie wird es nicht mehr angezeigt notifyListeners(); + return "success"; } @override - Future upvoteImage(ImageData image, BuildContext context) async { + Future upvoteImage(ImageData image) async { final result = await _api.upvoteImage(image); if (!result) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text("error"))); - return; + return "error"; } + image.upvote(); notifyListeners(); + return null; } } diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 7954c751..51c8b7af 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -17,6 +17,7 @@ import '../../repository/data_classes/meal/Side.dart'; // todo wann ist das Zeug wirklich zu? einfach rauslöschen? +// todo string for snack-bar class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { final ILocalStorage _preferences; final IServerAccess _api; @@ -168,42 +169,33 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { } @override - Future refreshMealplan(BuildContext context) async { + Future refreshMealplan() async { final mealPlan = await _getMealPlanFromServer(); if (_mealPlans.isEmpty) { // show snack-bar // TODO get good text - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text("Error Refresh"))); - - return Future.value(); + return "error"; } _mealPlans = mealPlan; _filterMealPlans(); notifyListeners(); + return null; } @override - Future updateMealRating( - int rating, Meal meal, BuildContext context) async { + Future updateMealRating(int rating, Meal meal) async { final result = await _api.updateMealRating(rating, meal); - final messenger = ScaffoldMessenger.of(context); if (!result) { - // todo display error in snack-bar - messenger.showSnackBar(SnackBar(content: Text("Error Rating"))); - return Future.value(); + return "error"; } _changeRatingOfMeal(meal, rating); - - // todo display snack-bar success - messenger.showSnackBar(SnackBar(content: Text("Success Rating"))); - notifyListeners(); + return "success"; } void _changeRatingOfMeal(Meal changedMeal, int rating) { From ccbb5754ae4b227080f9229869ced848d44737f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 17 Jul 2023 15:56:43 +0200 Subject: [PATCH 071/184] test ImageAccess --- app/test/view-model/ImageAccessTest.dart | 74 ++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 app/test/view-model/ImageAccessTest.dart diff --git a/app/test/view-model/ImageAccessTest.dart b/app/test/view-model/ImageAccessTest.dart new file mode 100644 index 00000000..34ccc013 --- /dev/null +++ b/app/test/view-model/ImageAccessTest.dart @@ -0,0 +1,74 @@ +import 'package:app/view_model/logic/image/ImageAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../model/mocks/ApiMock.dart'; + +void main() { + final api = ApiMock(); + + ImageAccess images = ImageAccess(api); + + ImageData image = ImageData(id: "42", + url: "url", + imageRank: 42, + positiveRating: 42, + negativeRating: 42); + + group("upvote", () { + test("failed upvote", () async { + when(() => api.upvoteImage(image)).thenAnswer((_) async => false); + await images.upvoteImage(image); + expect(image.individualRating, 0); + }); + + test("successful upvote", () async { + when(() => api.upvoteImage(image)).thenAnswer((_) async => true); + await images.upvoteImage(image); + expect(image.individualRating, 1); + }); + }); + + group("delete upvote", () { + test("failed request", () async { + when(() => api.deleteUpvote(image)).thenAnswer((_) async => false); + await images.deleteUpvote(image); + expect(image.individualRating, 1); + }); + + test("successful request", () async { + when(() => api.deleteUpvote(image)).thenAnswer((_) async => true); + await images.deleteUpvote(image); + expect(image.individualRating, 0); + }); + }); + + group("downvote", () { + test("failed request", () async { + when(() => api.downvoteImage(image)).thenAnswer((_) async => false); + await images.downvoteImage(image); + expect(image.individualRating, 0); + }); + + test("successful request", () async { + when(() => api.downvoteImage(image)).thenAnswer((_) async => true); + await images.downvoteImage(image); + expect(image.individualRating, -1); + }); + }); + + group("delete downvote", () { + test("failed request", () async { + when(() => api.deleteDownvote(image)).thenAnswer((_) async => false); + await images.deleteDownvote(image); + expect(image.individualRating, -1); + }); + + test("successful request", () async { + when(() => api.deleteDownvote(image)).thenAnswer((_) async => true); + await images.deleteDownvote(image); + expect(image.individualRating, 0); + }); + }); +} \ No newline at end of file From 2404aeb630e2a27c112e8255fc42b7a0b0bbe242 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Mon, 17 Jul 2023 17:18:53 +0200 Subject: [PATCH 072/184] Implemented MensaFilterIconCheckboxGroup --- .../icons/allergens/AllergenAlmondsIcon.dart | 4 +- .../icons/allergens/AllergenBarleyIcon.dart | 4 +- .../allergens/AllergenBrazilNutsIcon.dart | 4 +- .../icons/allergens/AllergenCashewsIcon.dart | 4 +- .../icons/allergens/AllergenCeleryIcon.dart | 4 +- .../allergens/AllergenCrustaceansIcon.dart | 4 +- .../icons/allergens/AllergenEggsIcon.dart | 4 +- .../icons/allergens/AllergenFishIcon.dart | 4 +- .../icons/allergens/AllergenGelatineIcon.dart | 4 +- .../allergens/AllergenHazelnutsIcon.dart | 4 +- .../core/icons/allergens/AllergenIcon.dart | 209 ++++++++++++++++-- .../icons/allergens/AllergenKamutIcon.dart | 4 +- .../icons/allergens/AllergenLoafIcon.dart | 4 +- .../icons/allergens/AllergenLupinIcon.dart | 4 +- .../allergens/AllergenMacadamiaIcon.dart | 4 +- .../icons/allergens/AllergenMilkIcon.dart | 4 +- .../icons/allergens/AllergenMolluscsIcon.dart | 4 +- .../icons/allergens/AllergenMustardIcon.dart | 4 +- .../icons/allergens/AllergenOatsIcon.dart | 4 +- .../icons/allergens/AllergenPeanutsIcon.dart | 4 +- .../icons/allergens/AllergenPecansIcon.dart | 4 +- .../allergens/AllergenPistachiosIcon.dart | 4 +- .../core/icons/allergens/AllergenRyeIcon.dart | 4 +- .../icons/allergens/AllergenSesameIcon.dart | 4 +- .../icons/allergens/AllergenSoyaIcon.dart | 4 +- .../icons/allergens/AllergenSpeltIcon.dart | 4 +- .../icons/allergens/AllergenSulphiteIcon.dart | 4 +- .../icons/allergens/AllergenWalnutsIcon.dart | 4 +- .../icons/allergens/AllergenWheatIcon.dart | 4 +- .../core/icons/allergens/IAllergenIcon.dart | 24 ++ .../view/filter/MensaFilterIconCheckbox.dart | 57 +++++ .../filter/MensaFilterIconCheckboxGroup.dart | 36 +++ .../data_classes/meal/Allergen.dart | 4 +- 33 files changed, 366 insertions(+), 76 deletions(-) create mode 100644 app/lib/view/core/icons/allergens/IAllergenIcon.dart create mode 100644 app/lib/view/filter/MensaFilterIconCheckbox.dart create mode 100644 app/lib/view/filter/MensaFilterIconCheckboxGroup.dart diff --git a/app/lib/view/core/icons/allergens/AllergenAlmondsIcon.dart b/app/lib/view/core/icons/allergens/AllergenAlmondsIcon.dart index 6df32c3d..fb77f21e 100644 --- a/app/lib/view/core/icons/allergens/AllergenAlmondsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenAlmondsIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Almonds -class AllergenAlmondsIcon extends AllergenIcon { +class AllergenAlmondsIcon extends IAllergenIcon { const AllergenAlmondsIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenBarleyIcon.dart b/app/lib/view/core/icons/allergens/AllergenBarleyIcon.dart index 9617c633..c3ccbfef 100644 --- a/app/lib/view/core/icons/allergens/AllergenBarleyIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenBarleyIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Barley -class AllergenBarleyIcon extends AllergenIcon { +class AllergenBarleyIcon extends IAllergenIcon { const AllergenBarleyIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenBrazilNutsIcon.dart b/app/lib/view/core/icons/allergens/AllergenBrazilNutsIcon.dart index 76329060..c3acabf3 100644 --- a/app/lib/view/core/icons/allergens/AllergenBrazilNutsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenBrazilNutsIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Brazil Nuts -class AllergenBrazilNutsIcon extends AllergenIcon { +class AllergenBrazilNutsIcon extends IAllergenIcon { const AllergenBrazilNutsIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenCashewsIcon.dart b/app/lib/view/core/icons/allergens/AllergenCashewsIcon.dart index 04a896db..5748c3cd 100644 --- a/app/lib/view/core/icons/allergens/AllergenCashewsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenCashewsIcon.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for cashews. -class AllergenCashewsIcon extends AllergenIcon { +class AllergenCashewsIcon extends IAllergenIcon { const AllergenCashewsIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenCeleryIcon.dart b/app/lib/view/core/icons/allergens/AllergenCeleryIcon.dart index 5da18869..df39936f 100644 --- a/app/lib/view/core/icons/allergens/AllergenCeleryIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenCeleryIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Cashews -class AllergenCeleryIcon extends AllergenIcon { +class AllergenCeleryIcon extends IAllergenIcon { const AllergenCeleryIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenCrustaceansIcon.dart b/app/lib/view/core/icons/allergens/AllergenCrustaceansIcon.dart index 6329ab2f..667fb66f 100644 --- a/app/lib/view/core/icons/allergens/AllergenCrustaceansIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenCrustaceansIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Crustaceans -class AllergenCrustaceansIcon extends AllergenIcon { +class AllergenCrustaceansIcon extends IAllergenIcon { const AllergenCrustaceansIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenEggsIcon.dart b/app/lib/view/core/icons/allergens/AllergenEggsIcon.dart index 1b064a65..8fdec6d2 100644 --- a/app/lib/view/core/icons/allergens/AllergenEggsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenEggsIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Eggs -class AllergenEggsIcon extends AllergenIcon { +class AllergenEggsIcon extends IAllergenIcon { const AllergenEggsIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenFishIcon.dart b/app/lib/view/core/icons/allergens/AllergenFishIcon.dart index 374bcdf4..d0b736f8 100644 --- a/app/lib/view/core/icons/allergens/AllergenFishIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenFishIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Fish -class AllergenFishIcon extends AllergenIcon { +class AllergenFishIcon extends IAllergenIcon { const AllergenFishIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenGelatineIcon.dart b/app/lib/view/core/icons/allergens/AllergenGelatineIcon.dart index f27250a5..987abaa2 100644 --- a/app/lib/view/core/icons/allergens/AllergenGelatineIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenGelatineIcon.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Gelatine -class AllergenGelatineIcon extends AllergenIcon { +class AllergenGelatineIcon extends IAllergenIcon { const AllergenGelatineIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenHazelnutsIcon.dart b/app/lib/view/core/icons/allergens/AllergenHazelnutsIcon.dart index b1437d1b..5a420007 100644 --- a/app/lib/view/core/icons/allergens/AllergenHazelnutsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenHazelnutsIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Hazelnuts -class AllergenHazelnutsIcon extends AllergenIcon { +class AllergenHazelnutsIcon extends IAllergenIcon { const AllergenHazelnutsIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenIcon.dart b/app/lib/view/core/icons/allergens/AllergenIcon.dart index 46b75004..4af54ec5 100644 --- a/app/lib/view/core/icons/allergens/AllergenIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenIcon.dart @@ -1,24 +1,195 @@ +import 'package:app/view/core/icons/allergens/AllergenAlmondsIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenBarleyIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenBrazilNutsIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenCashewsIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenCeleryIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenCrustaceansIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenEggsIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenFishIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenGelatineIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenHazelnutsIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenKamutIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenLoafIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenLupinIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenMacadamiaIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenMilkIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenMolluscsIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenMustardIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenOatsIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenPeanutsIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenPecansIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenPistachiosIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenRyeIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenSesameIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenSoyaIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenSpeltIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenSulphiteIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenWalnutsIcon.dart'; +import 'package:app/view/core/icons/allergens/AllergenWheatIcon.dart'; +import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; import 'package:flutter/material.dart'; -/// Abstract class for all allergen icons. -abstract class AllergenIcon extends StatelessWidget { - final double _width; - final double _height; - final Color _color; +import 'IAllergenIcon.dart'; - /// Creates an allergen icon. - /// @param key The key to use for this widget. - /// @param width The width of the icon. - /// @param height The height of the icon. - /// @param color The color of the icon. - const AllergenIcon({super.key, double width = 24, double height = 24, Color color = Colors.black}): _width = width, _height = height, _color = color; +class AllergenIcon extends IAllergenIcon { + final Allergen _allergen; + final Color? _color; - /// Returns the color of the icon. - Color get color => _color; + const AllergenIcon( + {super.key, + required Allergen allergen, + super.width, + super.height, + Color? color}) + : _allergen = allergen, + _color = color; - /// Returns the height of the icon. - double get height => _height; - - /// Returns the width of the icon. - double get width => _width; -} \ No newline at end of file + @override + Widget build(BuildContext context) { + switch (_allergen) { + case Allergen.ca: + return AllergenCashewsIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface, + ); + case Allergen.di: + return AllergenSpeltIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.ei: + return AllergenEggsIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.er: + return AllergenPeanutsIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.fi: + return AllergenFishIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.ge: + return AllergenBarleyIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.ha: + return AllergenHazelnutsIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.hf: + return AllergenOatsIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.ka: + return AllergenKamutIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.kr: + return AllergenCrustaceansIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.lu: + return AllergenLupinIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.ma: + return AllergenAlmondsIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.ml: + return AllergenMilkIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.pa: + return AllergenBrazilNutsIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.pe: + return AllergenPecansIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.pi: + return AllergenPistachiosIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.qu: + return AllergenMacadamiaIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.ro: + return AllergenRyeIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.sa: + return AllergenSesameIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.se: + return AllergenCeleryIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.sf: + return AllergenSulphiteIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.sn: + return AllergenMustardIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.so: + return AllergenSoyaIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.wa: + return AllergenWalnutsIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.we: + return AllergenWheatIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.wt: + return AllergenMolluscsIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.la: + return AllergenLoafIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + case Allergen.gt: + return AllergenGelatineIcon( + width: width, + height: height, + color: _color ?? Theme.of(context).colorScheme.onSurface); + default: + return SizedBox(width: width, height: height); + } + } +} diff --git a/app/lib/view/core/icons/allergens/AllergenKamutIcon.dart b/app/lib/view/core/icons/allergens/AllergenKamutIcon.dart index 11ba2a0a..5f1dcf94 100644 --- a/app/lib/view/core/icons/allergens/AllergenKamutIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenKamutIcon.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Kamut -class AllergenKamutIcon extends AllergenIcon { +class AllergenKamutIcon extends IAllergenIcon { const AllergenKamutIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenLoafIcon.dart b/app/lib/view/core/icons/allergens/AllergenLoafIcon.dart index 26b3b2b9..8a58d55d 100644 --- a/app/lib/view/core/icons/allergens/AllergenLoafIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenLoafIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Loaf -class AllergenLoafIcon extends AllergenIcon { +class AllergenLoafIcon extends IAllergenIcon { const AllergenLoafIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenLupinIcon.dart b/app/lib/view/core/icons/allergens/AllergenLupinIcon.dart index bae51706..559d91b3 100644 --- a/app/lib/view/core/icons/allergens/AllergenLupinIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenLupinIcon.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Lupin -class AllergenLupinIcon extends AllergenIcon { +class AllergenLupinIcon extends IAllergenIcon { const AllergenLupinIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenMacadamiaIcon.dart b/app/lib/view/core/icons/allergens/AllergenMacadamiaIcon.dart index 6fb85a7a..5ed60e87 100644 --- a/app/lib/view/core/icons/allergens/AllergenMacadamiaIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenMacadamiaIcon.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Macadamia -class AllergenMacadamiaIcon extends AllergenIcon { +class AllergenMacadamiaIcon extends IAllergenIcon { const AllergenMacadamiaIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenMilkIcon.dart b/app/lib/view/core/icons/allergens/AllergenMilkIcon.dart index e2368eca..3ca3fe1c 100644 --- a/app/lib/view/core/icons/allergens/AllergenMilkIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenMilkIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Milk -class AllergenMilkIcon extends AllergenIcon { +class AllergenMilkIcon extends IAllergenIcon { const AllergenMilkIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenMolluscsIcon.dart b/app/lib/view/core/icons/allergens/AllergenMolluscsIcon.dart index a4ec1aea..3973b439 100644 --- a/app/lib/view/core/icons/allergens/AllergenMolluscsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenMolluscsIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Molluscs -class AllergenMolluscsIcon extends AllergenIcon { +class AllergenMolluscsIcon extends IAllergenIcon { const AllergenMolluscsIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenMustardIcon.dart b/app/lib/view/core/icons/allergens/AllergenMustardIcon.dart index d0c3b9b7..da12e7bf 100644 --- a/app/lib/view/core/icons/allergens/AllergenMustardIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenMustardIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Mustard -class AllergenMustardIcon extends AllergenIcon { +class AllergenMustardIcon extends IAllergenIcon { const AllergenMustardIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenOatsIcon.dart b/app/lib/view/core/icons/allergens/AllergenOatsIcon.dart index e19ae5f9..04070d13 100644 --- a/app/lib/view/core/icons/allergens/AllergenOatsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenOatsIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Mustard -class AllergenOatsIcon extends AllergenIcon { +class AllergenOatsIcon extends IAllergenIcon { const AllergenOatsIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenPeanutsIcon.dart b/app/lib/view/core/icons/allergens/AllergenPeanutsIcon.dart index 452eaf1f..0a004c6b 100644 --- a/app/lib/view/core/icons/allergens/AllergenPeanutsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenPeanutsIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Peanuts -class AllergenPeanutsIcon extends AllergenIcon { +class AllergenPeanutsIcon extends IAllergenIcon { const AllergenPeanutsIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenPecansIcon.dart b/app/lib/view/core/icons/allergens/AllergenPecansIcon.dart index d3e42cfb..10edee41 100644 --- a/app/lib/view/core/icons/allergens/AllergenPecansIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenPecansIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Peanuts -class AllergenPecansIcon extends AllergenIcon { +class AllergenPecansIcon extends IAllergenIcon { const AllergenPecansIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenPistachiosIcon.dart b/app/lib/view/core/icons/allergens/AllergenPistachiosIcon.dart index 26903db2..60b9fa24 100644 --- a/app/lib/view/core/icons/allergens/AllergenPistachiosIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenPistachiosIcon.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Pistachios -class AllergenPistachiosIcon extends AllergenIcon { +class AllergenPistachiosIcon extends IAllergenIcon { const AllergenPistachiosIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenRyeIcon.dart b/app/lib/view/core/icons/allergens/AllergenRyeIcon.dart index 1cf59b88..a4afa2dc 100644 --- a/app/lib/view/core/icons/allergens/AllergenRyeIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenRyeIcon.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Rye -class AllergenRyeIcon extends AllergenIcon { +class AllergenRyeIcon extends IAllergenIcon { const AllergenRyeIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenSesameIcon.dart b/app/lib/view/core/icons/allergens/AllergenSesameIcon.dart index 683a43ff..bab0af44 100644 --- a/app/lib/view/core/icons/allergens/AllergenSesameIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenSesameIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Sesame -class AllergenSesameIcon extends AllergenIcon { +class AllergenSesameIcon extends IAllergenIcon { const AllergenSesameIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenSoyaIcon.dart b/app/lib/view/core/icons/allergens/AllergenSoyaIcon.dart index 81d0cdf4..cc9b4f0b 100644 --- a/app/lib/view/core/icons/allergens/AllergenSoyaIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenSoyaIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Soya -class AllergenSoyaIcon extends AllergenIcon { +class AllergenSoyaIcon extends IAllergenIcon { const AllergenSoyaIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenSpeltIcon.dart b/app/lib/view/core/icons/allergens/AllergenSpeltIcon.dart index 0a0fa322..08036626 100644 --- a/app/lib/view/core/icons/allergens/AllergenSpeltIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenSpeltIcon.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Spelt -class AllergenSpeltIcon extends AllergenIcon { +class AllergenSpeltIcon extends IAllergenIcon { const AllergenSpeltIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenSulphiteIcon.dart b/app/lib/view/core/icons/allergens/AllergenSulphiteIcon.dart index 8388fa03..0c037b15 100644 --- a/app/lib/view/core/icons/allergens/AllergenSulphiteIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenSulphiteIcon.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Sulphite -class AllergenSulphiteIcon extends AllergenIcon { +class AllergenSulphiteIcon extends IAllergenIcon { const AllergenSulphiteIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenWalnutsIcon.dart b/app/lib/view/core/icons/allergens/AllergenWalnutsIcon.dart index 97e1aaf7..03ffdf21 100644 --- a/app/lib/view/core/icons/allergens/AllergenWalnutsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenWalnutsIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Walnuts -class AllergenWalnutsIcon extends AllergenIcon { +class AllergenWalnutsIcon extends IAllergenIcon { const AllergenWalnutsIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/AllergenWheatIcon.dart b/app/lib/view/core/icons/allergens/AllergenWheatIcon.dart index 1847ca75..fd93d798 100644 --- a/app/lib/view/core/icons/allergens/AllergenWheatIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenWheatIcon.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'AllergenIcon.dart'; +import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Wheat -class AllergenWheatIcon extends AllergenIcon { +class AllergenWheatIcon extends IAllergenIcon { const AllergenWheatIcon( {super.key, super.width, super.height, super.color}); diff --git a/app/lib/view/core/icons/allergens/IAllergenIcon.dart b/app/lib/view/core/icons/allergens/IAllergenIcon.dart new file mode 100644 index 00000000..6ff6ebe3 --- /dev/null +++ b/app/lib/view/core/icons/allergens/IAllergenIcon.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +/// Abstract class for all allergen icons. +abstract class IAllergenIcon extends StatelessWidget { + final double _width; + final double _height; + final Color _color; + + /// Creates an allergen icon. + /// @param key The key to use for this widget. + /// @param width The width of the icon. + /// @param height The height of the icon. + /// @param color The color of the icon. + const IAllergenIcon({super.key, double width = 24, double height = 24, Color color = Colors.black}): _width = width, _height = height, _color = color; + + /// Returns the color of the icon. + Color get color => _color; + + /// Returns the height of the icon. + double get height => _height; + + /// Returns the width of the icon. + double get width => _width; +} \ No newline at end of file diff --git a/app/lib/view/filter/MensaFilterIconCheckbox.dart b/app/lib/view/filter/MensaFilterIconCheckbox.dart new file mode 100644 index 00000000..078c4556 --- /dev/null +++ b/app/lib/view/filter/MensaFilterIconCheckbox.dart @@ -0,0 +1,57 @@ +import 'package:app/view/core/icons/allergens/IAllergenIcon.dart'; +import 'package:flutter/material.dart'; + +class MensaFilterIconCheckbox { + final IAllergenIcon _icon; + final String _text; + final T value; + + const MensaFilterIconCheckbox( + {required IAllergenIcon icon, required String text, required this.value}) + : _icon = icon, + _text = text; + + Widget build(BuildContext context, bool active, Function() onTap) { + return Container( + width: 80, + height: 80, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.0), + border: Border.all( + width: 2, + color: active + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.surface), + color: Theme.of(context).colorScheme.background, + ), + child: Material( + borderRadius: BorderRadius.circular(4.0), + color: Colors.transparent, + child: InkWell( + borderRadius: BorderRadius.circular(4.0), + onTap: onTap, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + SizedBox( + height: 8, + ), + _icon, + SizedBox(height: 8), + Row(children: [ + Expanded( + child: Padding( + padding: EdgeInsets.all(1), + child: Text( + _text, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 9), + ))) + ]), + ]), + )), + ); + } +} diff --git a/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart b/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart new file mode 100644 index 00000000..10ad24b9 --- /dev/null +++ b/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart @@ -0,0 +1,36 @@ +import 'package:app/view/filter/MensaFilterIconCheckbox.dart'; +import 'package:flutter/cupertino.dart'; + +class MensaFilterIconCheckboxGroup extends StatelessWidget { + final List> _items; + final List _selectedValues; + final Function(List) _onChanged; + + const MensaFilterIconCheckboxGroup( + {super.key, + required List> items, + required List selectedValues, + required Function(List) onChanged}) + : _items = items, + _selectedValues = selectedValues, + _onChanged = onChanged; + + @override + Widget build(BuildContext context) { + return Wrap( + runAlignment: WrapAlignment.center, + //alignment: WrapAlignment.spaceEvenly, + spacing: 8, + runSpacing: 8, + children: _items + .map((e) => e.build(context, _selectedValues.contains(e.value), () { + if (_selectedValues.contains(e.value)) { + _selectedValues.remove(e.value); + } else { + _selectedValues.add(e.value); + } + _onChanged(_selectedValues); + })) + .toList()); + } +} diff --git a/app/lib/view_model/repository/data_classes/meal/Allergen.dart b/app/lib/view_model/repository/data_classes/meal/Allergen.dart index ec83833e..d0ff120f 100644 --- a/app/lib/view_model/repository/data_classes/meal/Allergen.dart +++ b/app/lib/view_model/repository/data_classes/meal/Allergen.dart @@ -24,5 +24,7 @@ enum Allergen { so, wa, we, - wt + wt, + la, + gt, } \ No newline at end of file From 4bd9fa5afeb20ecbfe1561e36aaf28264f9f6071 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Mon, 17 Jul 2023 18:06:08 +0200 Subject: [PATCH 073/184] implemented translations --- app/assets/locales/de/common.json | 2 +- app/assets/locales/en/common.json | 2 +- app/lib/main.dart | 46 ++++++++++++++++++++++++------- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/app/assets/locales/de/common.json b/app/assets/locales/de/common.json index 0e0dcd23..5771c35d 100644 --- a/app/assets/locales/de/common.json +++ b/app/assets/locales/de/common.json @@ -1,3 +1,3 @@ { - + "demo": "Flutter Demo Startseite" } \ No newline at end of file diff --git a/app/assets/locales/en/common.json b/app/assets/locales/en/common.json index 0e0dcd23..901ba021 100644 --- a/app/assets/locales/en/common.json +++ b/app/assets/locales/en/common.json @@ -1,3 +1,3 @@ { - + "demo": "Flutter Demo Home Page" } \ No newline at end of file diff --git a/app/lib/main.dart b/app/lib/main.dart index 9bee45fe..6fce345d 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,17 +1,47 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; void main() { - runApp(const MyApp()); + final FlutterI18nDelegate delegate = FlutterI18nDelegate( + translationLoader: NamespaceFileTranslationLoader( + namespaces: ["common"], + useCountryCode: false, + basePath: 'assets/locales', + fallbackDir: 'de'), + missingTranslationHandler: (key, locale) { + if (kDebugMode) { + print("--- Missing Key: $key, languageCode: ${locale!.languageCode}"); + } + }, + ); + WidgetsFlutterBinding.ensureInitialized(); + + runApp(MyApp( + delegate: delegate, + )); } class MyApp extends StatelessWidget { - const MyApp({super.key}); + final FlutterI18nDelegate _delegate; + + const MyApp({super.key, required FlutterI18nDelegate delegate}) + : _delegate = delegate; // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', + title: 'Mensa App', + localizationsDelegates: [ + _delegate, + ...GlobalMaterialLocalizations.delegates, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: const [ + Locale('de'), + ], theme: ThemeData( brightness: Brightness.light, colorScheme: const ColorScheme( @@ -43,13 +73,13 @@ class MyApp extends StatelessWidget { surfaceTint: Color(0xFF202020), onSurface: Color(0xFFFFFFFF)), ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), + home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); + const MyHomePage({super.key}); // This widget is the home page of your application. It is stateful, meaning // that it has a State object (defined below) that contains fields that affect @@ -60,8 +90,6 @@ class MyHomePage extends StatefulWidget { // used by the build method of the State. Fields in a Widget subclass are // always marked "final". - final String title; - @override State createState() => _MyHomePageState(); } @@ -110,9 +138,7 @@ class _MyHomePageState extends State { // horizontal). mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text( - 'You have pushed the button this many times:', - ), + I18nText("common.demo"), Text( '$_counter', style: Theme.of(context).textTheme.headlineMedium, From 0a420fef2a33db39eca666735c49cde5107e9fb9 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Mon, 17 Jul 2023 18:40:01 +0200 Subject: [PATCH 074/184] made test use demo server --- app/README.md | 11 +--- .../model/api_server/GraphQlServerAccess.dart | 15 ++--- app/lib/model/api_server/config.dart | 3 + app/secret.example.json | 4 -- .../api_server/GraphQlServerAccess_test.dart | 62 +++---------------- 5 files changed, 19 insertions(+), 76 deletions(-) create mode 100644 app/lib/model/api_server/config.dart delete mode 100644 app/secret.example.json diff --git a/app/README.md b/app/README.md index 63e294a3..ec3f3fe3 100644 --- a/app/README.md +++ b/app/README.md @@ -11,14 +11,5 @@ Studierendenwerk Karlsruhe [^1]. To generate the dart wrappers from `*.graphql` files run `dart run build_runner build`. -### Tests +## Tests -### Graphql - -To run graphql test, a api endpoint must be specified in an `secret.json` file, an example of which -can be found in `secret.example.env`. -| ⚠️ **Important** | These secrets must AT NO POINT be check in to version control! | -| -- | -- | - -Then, you need to specify the files location when running -tests: `flutter test --dart-define-from-file=.\secret.json` \ No newline at end of file diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index 34004d37..58ced139 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -24,32 +24,29 @@ import 'package:app/view_model/repository/error_handling/NoMealException.dart'; import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:crypto/crypto.dart'; -import 'package:flutter/cupertino.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:intl/intl.dart'; -import 'package:convert/convert.dart'; import 'package:uuid/uuid.dart'; import '../../view_model/repository/interface/IServerAccess.dart'; import 'requests/mutations.graphql.dart'; class GraphQlServerAccess implements IServerAccess { - final String _apiKey = const String.fromEnvironment('API_KEY'); + final String _apiKey; late String _currentAuth; late final GraphQLClient _client; final String _clientId; final _dateFormat = DateFormat(dateFormatPattern); - GraphQlServerAccess._(this._clientId) { + GraphQlServerAccess._(this._clientId, String server, this._apiKey) { _client = GraphQLClient( - link: AuthLink(getToken: () => _currentAuth) - .concat(HttpLink(const String.fromEnvironment('API_URL'))), + link: AuthLink(getToken: () => _currentAuth).concat(HttpLink(server)), cache: GraphQLCache()); - _authenticate(""); + _authenticate(""); // provide default authentication with client id } - factory GraphQlServerAccess(String clientId) { - return GraphQlServerAccess._(clientId); + factory GraphQlServerAccess(String clientId, String server, String apiKey) { + return GraphQlServerAccess._(clientId, server, apiKey); } // ---------------------- mutations ---------------------- diff --git a/app/lib/model/api_server/config.dart b/app/lib/model/api_server/config.dart new file mode 100644 index 00000000..e5858167 --- /dev/null +++ b/app/lib/model/api_server/config.dart @@ -0,0 +1,3 @@ + +const String testServer = "https://demo.data.mensa-ka.de/"; +const String testApiKey = "YWpzZGg4MnozNzhkMnppZGFzYXNkMiBzYWZzYSBzPGE5MDk4"; \ No newline at end of file diff --git a/app/secret.example.json b/app/secret.example.json deleted file mode 100644 index 3d3d6709..00000000 --- a/app/secret.example.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "API_URL": "https://mensa.yourdomain.com", - "API_KEY": "yourapikey" -} \ No newline at end of file diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index 91979f8e..020b1537 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -1,7 +1,5 @@ -import 'dart:convert'; -import 'dart:developer'; - import 'package:app/model/api_server/GraphQlServerAccess.dart'; +import 'package:app/model/api_server/config.dart'; import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; @@ -11,39 +9,11 @@ import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:intl/intl.dart'; void main() async { - final GraphQlServerAccess serverAccess = - GraphQlServerAccess("1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); - - test('auth at server', () async { - // TODO remove test - String token = ""; - final httpLink = HttpLink(const String.fromEnvironment('API_URL')); - final authLink = AuthLink(getToken: () => token); - - final GraphQLClient _client = - GraphQLClient(link: authLink.concat(httpLink), cache: GraphQLCache()); - - token = - "Mensa MWQ3NWQzODAtY2YwNy00ZWRiLTkwNDYtYTJkOTgxYmMyMTlkOmFiYzoxMjM="; - var result = await _client.query(QueryOptions( - document: gql("""{ - getMyAuth{clientId,apiIdent,hash} - } - """), - )); - expect(result.data?["getMyAuth"]?["clientId"], - "1d75d380-cf07-4edb-9046-a2d981bc219d"); - }); - - test('environment endpoint defined', () { - expect(const String.fromEnvironment('API_URL').isNotEmpty, true, - reason: - "define secret file with `--dart-define-from-file=`, see README"); - }); + final GraphQlServerAccess serverAccess = GraphQlServerAccess( + "1f16dcca-963e-4ceb-a8ca-843a7c9277a5", testServer, testApiKey); test('remove downvote', () async { var deleted = await serverAccess.deleteDownvote(ImageData( @@ -127,8 +97,8 @@ void main() async { test('update all', () async { var result = await serverAccess.updateAll(); - var res = switch (result) { - Success(value: final mealplan) => true, // TODO + var _ = switch (result) { + Success(value: final _) => true, // TODO Failure(exception: final exception) => expect(exception.toString(), ""), }; }); @@ -138,8 +108,8 @@ void main() async { Canteen(id: "bd3c88f9-5dc8-4773-85dc-53305930e7b6", name: "Canteen"), DateTime(2020, 11, 1)); - var res = switch (result) { - Success(value: final mealplan) => true, //TODO + var _ = switch (result) { + Success(value: final _) => true, //TODO Failure(exception: final exception) => expect(exception.toString(), ""), }; }); @@ -160,8 +130,8 @@ void main() async { position: 22), DateTime(2020, 11, 2)); - var res = switch (result) { - Success(value: final mealplan) => true, // TODO better testing? + var _ = switch (result) { + Success(value: final _) => true, // TODO better testing? Failure(exception: final exception) => expect(exception, true, reason: "exception while request"), }; @@ -172,18 +142,4 @@ void main() async { expect(canteen != null, true); }); - - // TODO remove - test('transform auth', () async { - var clientId = "1d75d380-cf07-4edb-9046-a2d981bc219d"; - var apiKey = "abc"; - var hash = "123"; - - var authString = "${clientId}:${apiKey.substring(0, 3)}:$hash"; - var bytes = utf8.encode(authString); - var base64 = base64Encode(bytes); - - expect( - base64, "MWQ3NWQzODAtY2YwNy00ZWRiLTkwNDYtYTJkOTgxYmMyMTlkOmFiYzoxMjM="); - }); } From b51ffabca8bb6ab35fbd2c630991a7a9157da9ce Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Mon, 17 Jul 2023 19:55:49 +0200 Subject: [PATCH 075/184] comments and refactoring --- .../model/api_server/GraphQlServerAccess.dart | 22 ++++++++++++++----- .../api_server/GraphQlServerAccess_test.dart | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index 58ced139..c756dd12 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -31,6 +31,7 @@ import 'package:uuid/uuid.dart'; import '../../view_model/repository/interface/IServerAccess.dart'; import 'requests/mutations.graphql.dart'; +/// This class is responsible for communicating with the server through the graphql api. class GraphQlServerAccess implements IServerAccess { final String _apiKey; late String _currentAuth; @@ -45,8 +46,12 @@ class GraphQlServerAccess implements IServerAccess { _authenticate(""); // provide default authentication with client id } - factory GraphQlServerAccess(String clientId, String server, String apiKey) { - return GraphQlServerAccess._(clientId, server, apiKey); + /// This constructor returns an instance of the server access class. + /// To connect to the server, its address has to be provided as `address`. + /// To authenticate commands to the server, an api key has also to be specified. + /// The client identifier is necessary to request user specific information from the server. + factory GraphQlServerAccess(String address, String apiKey, String clientId) { + return GraphQlServerAccess._(clientId, address, apiKey); } // ---------------------- mutations ---------------------- @@ -251,12 +256,17 @@ class GraphQlServerAccess implements IServerAccess { } // --------------- auth --------------- + static const int apiKeyIdentifierPrefixLength = 10; + static const String authenticationScheme = "Mensa"; void _authenticate(String hash) { - var authString = "$_clientId:${_apiKey.substring(0, 10)}:$hash"; - var bytes = utf8.encode(authString); - var base64 = base64Encode(bytes); - _currentAuth = "Mensa $base64"; + final apiKeyPrefix = _apiKey.length > apiKeyIdentifierPrefixLength + ? _apiKey.substring(0, apiKeyIdentifierPrefixLength) + : _apiKey; + final authString = "$_clientId:$apiKeyPrefix:$hash"; + final bytes = utf8.encode(authString); + final base64 = base64Encode(bytes); + _currentAuth = "$authenticationScheme $base64"; } String _generateHashOfParameters( diff --git a/app/test/model/api_server/GraphQlServerAccess_test.dart b/app/test/model/api_server/GraphQlServerAccess_test.dart index 020b1537..8545122e 100644 --- a/app/test/model/api_server/GraphQlServerAccess_test.dart +++ b/app/test/model/api_server/GraphQlServerAccess_test.dart @@ -13,7 +13,7 @@ import 'package:intl/intl.dart'; void main() async { final GraphQlServerAccess serverAccess = GraphQlServerAccess( - "1f16dcca-963e-4ceb-a8ca-843a7c9277a5", testServer, testApiKey); + testServer, testApiKey, "1f16dcca-963e-4ceb-a8ca-843a7c9277a5"); test('remove downvote', () async { var deleted = await serverAccess.deleteDownvote(ImageData( From e0a3b5041d1976773f39c8201911dc1b45ae779f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 17 Jul 2023 22:41:25 +0200 Subject: [PATCH 076/184] overwrite equals method --- .../filter/FilterPreferences.dart | 29 ++++++++++++++++++- .../repository/data_classes/meal/Meal.dart | 7 +++++ .../data_classes/mealplan/MealPlan.dart | 17 +++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart index 8e312651..f7444b27 100644 --- a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart +++ b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart @@ -1,3 +1,5 @@ +import 'package:flutter/foundation.dart'; + import 'Sorting.dart'; import 'Frequency.dart'; import '../meal/Allergen.dart'; @@ -35,7 +37,7 @@ class FilterPreferences { }) : _categories = categories ?? _getAllFoodTypes(), _allergens = allergens ?? _getAllAllergen(), - _price = price ?? 10, + _price = price ?? 1000, _rating = rating ?? 0, _frequency = frequency ?? _getAllFrequencies(), _onlyFavorite = onlyFavorite ?? false, @@ -198,4 +200,29 @@ class FilterPreferences { } return list; } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is FilterPreferences && + runtimeType == other.runtimeType && + listEquals(_categories, other._categories) && + listEquals(_allergens, other._allergens) && + _price == other._price && + _rating == other._rating && + listEquals(_frequency, other._frequency) && + _onlyFavorite == other._onlyFavorite && + _sortedBy == other._sortedBy && + _ascending == other._ascending; + + @override + int get hashCode => + _categories.hashCode ^ + _allergens.hashCode ^ + _price.hashCode ^ + _rating.hashCode ^ + _frequency.hashCode ^ + _onlyFavorite.hashCode ^ + _sortedBy.hashCode ^ + _ascending.hashCode; } \ No newline at end of file diff --git a/app/lib/view_model/repository/data_classes/meal/Meal.dart b/app/lib/view_model/repository/data_classes/meal/Meal.dart index 4eb0fe12..1007de87 100644 --- a/app/lib/view_model/repository/data_classes/meal/Meal.dart +++ b/app/lib/view_model/repository/data_classes/meal/Meal.dart @@ -210,4 +210,11 @@ class Meal { bool get isFavorite => _isFavorite ?? false; + @override + bool operator ==(Object other) => + identical(this, other) || + other is Meal && runtimeType == other.runtimeType && _id == other._id; + + @override + int get hashCode => _id.hashCode; } diff --git a/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart b/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart index 5ff6e647..31b847aa 100644 --- a/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart +++ b/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart @@ -1,3 +1,5 @@ +import 'package:flutter/foundation.dart'; + import '../meal/Meal.dart'; import 'Line.dart'; @@ -45,4 +47,19 @@ class MealPlan { }; } + @override + bool operator ==(Object other) => + identical(this, other) || + other is MealPlan && + runtimeType == other.runtimeType && + _date.year == other._date.year && + _date.month == other._date.month && + _date.day == other._date.day && + _line == other._line && + _isClosed == other._isClosed && + listEquals(_meals, other._meals); + + @override + int get hashCode => + _date.hashCode ^ _line.hashCode ^ _isClosed.hashCode ^ _meals.hashCode; } \ No newline at end of file From 0fdc0cc193ddda0b2579d3982e4e2d9d19b2d845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 17 Jul 2023 22:42:11 +0200 Subject: [PATCH 077/184] fixes --- .../logic/meal/CombinedMealPlanAccess.dart | 146 ++++++++++------ app/test/view-model/MealPlanAccessTest.dart | 160 +++++++++++++++++- 2 files changed, 256 insertions(+), 50 deletions(-) diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 51c8b7af..d4f7ae05 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -15,7 +15,6 @@ import 'package:flutter/material.dart'; import '../../repository/data_classes/meal/Side.dart'; - // todo wann ist das Zeug wirklich zu? einfach rauslöschen? // todo string for snack-bar class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { @@ -30,8 +29,11 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { late FilterPreferences _filter; late bool _noDataYet = false; + // waits until _init() is finished initializing + late Future _doneInitialization; + CombinedMealPlanAccess(this._preferences, this._api, this._database) { - _init(); + _doneInitialization = _init(); } Future _init() async { @@ -84,66 +86,78 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { }; // filter meal plans - _filterMealPlans(); + await _filterMealPlans(); } - // todo get date - // todo get canteen - // todo get filter preferences - // todo reset filter preferences - @override Future changeCanteen(Canteen canteen) async { + await _doneInitialization; _activeCanteen = canteen; // requests and stores the new meal plan // filters meal plan // notifies listener - _setNewMealPlan(); + await _setNewMealPlan(); } @override Future changeDate(DateTime date) async { + await _doneInitialization; _displayedDate = date; // requests and stores the new meal plan // filters meal plan // notifies listener - _setNewMealPlan(); + await _setNewMealPlan(); } @override Future changeFilterPreferences( FilterPreferences filterPreferences) async { + await _doneInitialization; + _filter = filterPreferences; - _filterMealPlans(); + await _filterMealPlans(); + notifyListeners(); } @override - Future> getMealFromId(String id) async { - final meal = switch (await _api.getMealFromId(id)) { + Future> getWholeFavorite(String id) async { + await _doneInitialization; + + final meal = switch (await _database.getMealFavorite(id)) { Success(value: final value) => value, Failure() => null }; - if (meal != null) { - return Future.value(Success(meal)); + if (meal == null) { + return Failure(NoMealException("no meal")); } - // get data form database if it is stored there - final mealDatabase = await _database.getMealFavorite(id); + final line = await _database.getFavoriteMealsLine(meal); + final date = await _database.getFavoriteMealsDate(meal); - switch (mealDatabase) { - case Success(): - return mealDatabase; - case Failure(): - return Future.value(Failure(NoMealException("meal not found"))); + if (line == null || date == null) { + return Failure(NoMealException("no meal")); } + + final mealFromServer = switch (await _api.getMeal(meal, line, date)) { + Success(value: final value) => value, + Failure() => null + }; + + if (mealFromServer == null) { + return Failure(NoMealException("no meal")); + } + + return Success(mealFromServer); } @override Future, MealPlanException>> getMealPlan() async { + await _doneInitialization; + // no connection to server and no data if (_mealPlans.isEmpty) { return Future.value(Failure(NoConnectionException("no connection"))); @@ -170,16 +184,16 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { @override Future refreshMealplan() async { + await _doneInitialization; + final mealPlan = await _getMealPlanFromServer(); if (_mealPlans.isEmpty) { - // show snack-bar - // TODO get good text return "error"; } _mealPlans = mealPlan; - _filterMealPlans(); + await _filterMealPlans(); notifyListeners(); return null; @@ -187,6 +201,8 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { @override Future updateMealRating(int rating, Meal meal) async { + await _doneInitialization; + final result = await _api.updateMealRating(rating, meal); if (!result) { @@ -198,6 +214,35 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { return "success"; } + @override + Future getCanteen() async { + await _doneInitialization; + + return _activeCanteen; + } + + @override + Future getDate() async { + await _doneInitialization; + + return _displayedDate; + } + + @override + Future getFilterPreferences() async { + await _doneInitialization; + + return _filter; + } + + @override + Future resetFilterPreferences() async { + await _doneInitialization; + + _filter = FilterPreferences(); + await _filterMealPlans(); + } + void _changeRatingOfMeal(Meal changedMeal, int rating) { for (final mealPlan in _mealPlans) { // check if right meal plan @@ -215,13 +260,13 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { } Future _filterMealPlans() async { + _filteredMealPlan = []; // any kind of failure so no data is present if (_mealPlans.isEmpty || _noDataYet || _mealPlans.first.isClosed) { - _filteredMealPlan = []; return; } - List newFilteredMealPlan = []; + List newFilteredMealPlans = []; for (final mealPlan in _mealPlans) { MealPlan filteredMealPlan = MealPlan.copy(mealPlan: mealPlan, meals: []); @@ -234,20 +279,24 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { filteredMealPlan.meals.add(filteredMeal); // filter sides of copied meal - _filterSides(filteredMeal, meal.sides); + await _filterSides(filteredMeal, meal.sides); } } - if (mealPlan.meals.isEmpty) { - newFilteredMealPlan.remove(mealPlan); + if (mealPlan.meals.isNotEmpty) { + newFilteredMealPlans.add(filteredMealPlan); } } - _filteredMealPlan = newFilteredMealPlan; + _filteredMealPlan = newFilteredMealPlans; } Future _filterSides(Meal meal, List? sides) async { - for (final side in sides!) { + if (sides == null) { + return Future.value(); + } + + for (final side in sides) { if (await _filterSide(side)) { meal.sides!.add(side); } @@ -268,9 +317,9 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { } // check price - if (side.price.getPrice( - await _preferences.getPriceCategory() ?? PriceCategory.student) > - _filter.price) { + final price = side.price.getPrice( + await _preferences.getPriceCategory() ?? PriceCategory.student); + if (price > _filter.price) { return false; } @@ -284,32 +333,31 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { } // check allergens - if (meal.allergens == null) { - return false; - } - - final allergens = meal.allergens ?? []; - for (final allergen in allergens) { - if (!_filter.allergens.contains(allergen)) { - return false; + if (meal.allergens != null) { + final allergens = meal.allergens ?? []; + for (final allergen in allergens) { + if (!_filter.allergens.contains(allergen)) { + return false; + } } } + final price = meal.price.getPrice( + await _preferences.getPriceCategory() ?? PriceCategory.student); + // check price - if (meal.price.getPrice( - await _preferences.getPriceCategory() ?? PriceCategory.student) > - _filter.price) { + if (_filter.price < price) { return false; } // check rating - if (meal.averageRating == null || meal.averageRating! < _filter.rating) { + if (meal.averageRating != null && meal.averageRating! < _filter.rating) { return false; } // check frequency if (meal.relativeFrequency == null || - _filter.frequency.contains(meal.relativeFrequency)) { + !_filter.frequency.contains(meal.relativeFrequency)) { return false; } @@ -329,7 +377,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { _noDataYet = false; _mealPlans = await _getMealPlanFromDatabaseAndServer(); - _filterMealPlans(); + await _filterMealPlans(); notifyListeners(); } diff --git a/app/test/view-model/MealPlanAccessTest.dart b/app/test/view-model/MealPlanAccessTest.dart index ce2295a9..33e12319 100644 --- a/app/test/view-model/MealPlanAccessTest.dart +++ b/app/test/view-model/MealPlanAccessTest.dart @@ -1,14 +1,172 @@ +import 'package:app/view_model/logic/meal/CombinedMealPlanAccess.dart'; +import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; +import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; +import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/meal/Price.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; +import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; +import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; +import 'package:app/view_model/repository/error_handling/Result.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import '../model/mocks/ApiMock.dart'; import '../model/mocks/DatabaseMock.dart'; import '../model/mocks/LocalStorageMock.dart'; -void main () { +void main() { final localStorage = LocalStorageMock(); final api = ApiMock(); final database = DatabaseMock(); + late CombinedMealPlanAccess mealPlan; + const String canteenID = "id"; + final Canteen canteen = Canteen(id: canteenID, name: "name"); + final List meals = [ + Meal( + id: "1", + name: "vegan Meal", + foodType: FoodType.vegan, + relativeFrequency: Frequency.newMeal, + price: Price(student: 200, employee: 300, pupil: 400, guest: 500)), + Meal( + id: "42", + name: "vegetarian Meal", + foodType: FoodType.vegetarian, + relativeFrequency: Frequency.newMeal, + price: Price(student: 200, employee: 300, pupil: 400, guest: 500)), + Meal( + id: "12", + name: "fishi Meal", + foodType: FoodType.fish, + relativeFrequency: Frequency.newMeal, + price: Price(student: 200, employee: 300, pupil: 400, guest: 500)), + Meal( + id: "34", + name: "meal with beef", + foodType: FoodType.beef, + relativeFrequency: Frequency.newMeal, + price: Price(student: 100, employee: 120, pupil: 130, guest: 140)), + Meal( + id: "54", + name: "pig", + foodType: FoodType.pork, + relativeFrequency: Frequency.newMeal, + price: Price(student: 123, employee: 456, pupil: 345, guest: 789)), + ]; + + final List lines = [ + Line(id: "1", name: "Linie 1", canteen: canteen, position: 1), + Line(id: "2", name: "Linie 2", canteen: canteen, position: 2), + Line(id: "3", name: "Linie 3", canteen: canteen, position: 3) + ]; + + final List mealplans = [ + MealPlan( + date: DateTime.now(), + line: lines[0], + isClosed: false, + meals: [meals[0], meals[1]]), + MealPlan( + date: DateTime.now(), + line: lines[1], + isClosed: false, + meals: [meals[2]]), + MealPlan( + date: DateTime.now(), + line: lines[2], + isClosed: false, + meals: [meals[3], meals[4]]), + ]; + + final List closedCanteen = [ + MealPlan(date: DateTime.now(), line: lines[1], isClosed: true, meals: []), + MealPlan(date: DateTime.now(), line: lines[2], isClosed: true, meals: []), + ]; + + final List closedLine = [ + MealPlan(date: DateTime.now(), line: lines[1], isClosed: true, meals: []), + MealPlan( + date: DateTime.now(), + line: lines[2], + isClosed: false, + meals: [meals[3], meals[4]]), + ]; + + setUp(() { + when(() => localStorage.getFilterPreferences()) + .thenAnswer((_) async => null); + when(() => localStorage.getCanteen()).thenAnswer((_) async => canteenID); + when(() => localStorage.getPriceCategory()).thenAnswer((_) async => PriceCategory.student); + + when(() => api.updateAll()) + .thenAnswer((_) async => Failure(NoConnectionException("error"))); + + when(() => database.getCanteenById(canteenID)) + .thenAnswer((_) async => canteen); + when(() => database.updateAll(mealplans)).thenAnswer((_) async => {}); + when(() => database.getMealPlan(any(), canteen)) + .thenAnswer((_) async => Success(mealplans)); + when(() => database.getFavorites()).thenAnswer((_) async => []); + + mealPlan = CombinedMealPlanAccess(localStorage, api, database); + }); + + group("initialization", () { + test("simple initialization", () async { + expect(await mealPlan.getCanteen(), canteen); + expect(await mealPlan.getFilterPreferences(), FilterPreferences()); + + final date = await mealPlan.getDate(); + expect(date.year, DateTime.now().year); + expect(date.month, DateTime.now().month); + expect(date.day, DateTime.now().day); + + final returnedMealPlan = switch (await mealPlan.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + for (MealPlan mealplan in mealplans) { + expect(returnedMealPlan.contains(mealplan), true); + } + }); + + // todo initialization with other values + + }); + + group("change meal plan", () { + // todo + // change date + // change canteen + + }); + + group("filter meals", () { + // todo write test cases + // change allergens + // change frequency + // change favorites + // change price + // change rating + // change category + + // filter sides + // reset filters + }); + + group("edge cases", () { + // todo + // just first line is closed, other are open + // all lines closed + // no Data yet + // all filterd + // no connection + }); } \ No newline at end of file From 1c85532dd533eb8d722879a5399b7be5468eec9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 17 Jul 2023 22:42:51 +0200 Subject: [PATCH 078/184] store Price category directly if not already stored in shared preferences --- .../logic/preference/PreferenceAccess.dart | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/lib/view_model/logic/preference/PreferenceAccess.dart b/app/lib/view_model/logic/preference/PreferenceAccess.dart index b464e7db..97c03421 100644 --- a/app/lib/view_model/logic/preference/PreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/PreferenceAccess.dart @@ -6,8 +6,9 @@ import 'package:app/view_model/repository/interface/ILocalStorage.dart'; import 'package:flutter/foundation.dart'; +// todo muss vor Combined Meal Plan Access initialisiert werden class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { - late ILocalStorage _access; + final ILocalStorage _access; late String _clientIdentifier; late MensaColorScheme _colorScheme; @@ -24,8 +25,15 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { Future _init() async { _clientIdentifier = await _access.getClientIdentifier() ?? ""; _colorScheme = await _access.getColorScheme() ?? MensaColorScheme.system; - _priceCategory = await _access.getPriceCategory() ?? PriceCategory.student; _mealPlanFormat = await _access.getMealPlanFormat() ?? MealPlanFormat.grid; + + PriceCategory? category = await _access.getPriceCategory(); + if (category == null) { + category = PriceCategory.student; + _access.setPriceCategory(category); + } + + _priceCategory = category; } @override From 8cb5e5deb8b2a666ff53fd17ba8c188badfb52ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 18 Jul 2023 08:36:24 +0200 Subject: [PATCH 079/184] override equals method --- app/lib/view_model/repository/data_classes/meal/Side.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/lib/view_model/repository/data_classes/meal/Side.dart b/app/lib/view_model/repository/data_classes/meal/Side.dart index bc8beb33..4a37bae0 100644 --- a/app/lib/view_model/repository/data_classes/meal/Side.dart +++ b/app/lib/view_model/repository/data_classes/meal/Side.dart @@ -75,4 +75,12 @@ class Side { List get allergens => _allergens; List get additives => _additives; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Side && runtimeType == other.runtimeType && _id == other._id; + + @override + int get hashCode => _id.hashCode; } From 675ddd47b9b87b237a1ff8c7f4f043712838f80e Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Tue, 18 Jul 2023 08:49:57 +0200 Subject: [PATCH 080/184] documented MensaFilterIconCheckboxGroup --- app/lib/view/filter/MensaFilterIconCheckbox.dart | 5 +++++ app/lib/view/filter/MensaFilterIconCheckboxGroup.dart | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/app/lib/view/filter/MensaFilterIconCheckbox.dart b/app/lib/view/filter/MensaFilterIconCheckbox.dart index 078c4556..4bfc2be1 100644 --- a/app/lib/view/filter/MensaFilterIconCheckbox.dart +++ b/app/lib/view/filter/MensaFilterIconCheckbox.dart @@ -1,11 +1,16 @@ import 'package:app/view/core/icons/allergens/IAllergenIcon.dart'; import 'package:flutter/material.dart'; +/// This widget is used to display a selectable icon with a text. class MensaFilterIconCheckbox { final IAllergenIcon _icon; final String _text; final T value; + /// Creates a MensaFilterIconCheckbox widget. + /// @param icon The icon to display. + /// @param text The text to display. + /// @param value The value of the checkbox. const MensaFilterIconCheckbox( {required IAllergenIcon icon, required String text, required this.value}) : _icon = icon, diff --git a/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart b/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart index 10ad24b9..1aa86a16 100644 --- a/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart +++ b/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart @@ -1,11 +1,17 @@ import 'package:app/view/filter/MensaFilterIconCheckbox.dart'; import 'package:flutter/cupertino.dart'; +/// This widget is used to display a group of MensaFilterIconCheckbox widgets. class MensaFilterIconCheckboxGroup extends StatelessWidget { final List> _items; final List _selectedValues; final Function(List) _onChanged; + /// Creates a MensaFilterIconCheckboxGroup widget. + /// @param key The key to use for this widget. + /// @param items The items to display. + /// @param selectedValues The values that are currently selected. + /// @param onChanged The function to call when the selection changes. const MensaFilterIconCheckboxGroup( {super.key, required List> items, From e7bc7e898ef03e84f986d4ad4f0547d7ec9baa64 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Tue, 18 Jul 2023 10:25:32 +0200 Subject: [PATCH 081/184] implemented RatingsOverview --- app/assets/locales/de/ratings.json | 4 ++ app/lib/view/detail_view/RatingsOverview.dart | 66 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 app/assets/locales/de/ratings.json create mode 100644 app/lib/view/detail_view/RatingsOverview.dart diff --git a/app/assets/locales/de/ratings.json b/app/assets/locales/de/ratings.json new file mode 100644 index 00000000..10ea5760 --- /dev/null +++ b/app/assets/locales/de/ratings.json @@ -0,0 +1,4 @@ +{ + "titleRatings": "Bewertungen", + "labelRatingsCount": "Bewertungen" +} \ No newline at end of file diff --git a/app/lib/view/detail_view/RatingsOverview.dart b/app/lib/view/detail_view/RatingsOverview.dart new file mode 100644 index 00000000..1e95a3ec --- /dev/null +++ b/app/lib/view/detail_view/RatingsOverview.dart @@ -0,0 +1,66 @@ +import 'package:app/view/core/input_components/MensaRatingInput.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:intl/intl.dart'; + +/// Displays a overview over the ratings for a meal. +class RatingsOverview extends StatelessWidget { + final Meal _meal; + final NumberFormat _numberFormat = NumberFormat("#0.0#", "de_DE"); + + /// Creates a new RatingsOverview. + /// @param key The key to identify this widget. + /// @param meal The meal to display the ratings for. + /// @return A new RatingsOverview. + RatingsOverview({super.key, required Meal meal}) : _meal = meal; + + @override + Widget build(BuildContext context) { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(FlutterI18n.translate(context, "ratings.titleRatings"), + textAlign: TextAlign.left, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontSize: 18, + )), + const SizedBox(height: 8), + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(4), + ), + child: Column( + children: [ + Text(_numberFormat.format(_meal.averageRating), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.bold, + fontSize: 28, + )), + const SizedBox(height: 4), + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + MensaRatingInput( + value: _meal.averageRating ?? 0, + onChanged: (p0) => {}, + disabled: true, + color: Theme.of(context).colorScheme.onSurface, + size: 20, + max: 5, + ) + ]), + const SizedBox(height: 8), + Text( + '${_meal.numberOfRatings.toString()} ${FlutterI18n.translate(context, "ratings.labelRatingsCount")}', + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.w300, + fontSize: 12, + )), + ], + ), + ) + ]); + } +} From b7fc2e983edb9a333f2e4635789f3620c4554e70 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Tue, 18 Jul 2023 10:25:45 +0200 Subject: [PATCH 082/184] implemented RatingsOverview --- app/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/main.dart b/app/lib/main.dart index 6fce345d..0e105810 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -6,7 +6,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; void main() { final FlutterI18nDelegate delegate = FlutterI18nDelegate( translationLoader: NamespaceFileTranslationLoader( - namespaces: ["common"], + namespaces: ["common", "ratings"], useCountryCode: false, basePath: 'assets/locales', fallbackDir: 'de'), From 49ae3b040d861b0aad7b0ab0c3aed9dc88865b8d Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Tue, 18 Jul 2023 17:48:20 +0200 Subject: [PATCH 083/184] removed coverage file --- app/coverage/lcov.info | 2728 ---------------------------------------- 1 file changed, 2728 deletions(-) delete mode 100644 app/coverage/lcov.info diff --git a/app/coverage/lcov.info b/app/coverage/lcov.info deleted file mode 100644 index 045172fc..00000000 --- a/app/coverage/lcov.info +++ /dev/null @@ -1,2728 +0,0 @@ -SF:lib\model\api_server\GraphQlServerAccess.dart -DA:40,1 -DA:42,1 -DA:43,1 -DA:48,1 -DA:51,2 -DA:52,1 -DA:53,2 -DA:54,1 -DA:55,1 -DA:58,1 -DA:61,2 -DA:62,1 -DA:63,2 -DA:65,2 -DA:68,1 -DA:71,2 -DA:72,1 -DA:73,2 -DA:75,2 -DA:78,1 -DA:81,3 -DA:82,2 -DA:84,2 -DA:87,1 -DA:90,3 -DA:92,2 -DA:94,2 -DA:97,1 -DA:100,2 -DA:101,1 -DA:102,1 -DA:103,1 -DA:104,1 -DA:106,2 -DA:109,1 -DA:112,2 -DA:113,1 -DA:114,1 -DA:115,1 -DA:117,2 -DA:124,1 -DA:126,1 -DA:128,1 -DA:131,2 -DA:132,2 -DA:133,2 -DA:134,1 -DA:135,1 -DA:136,2 -DA:138,1 -DA:140,0 -DA:144,3 -DA:146,1 -DA:148,1 -DA:151,1 -DA:154,3 -DA:155,1 -DA:156,4 -DA:158,2 -DA:159,1 -DA:161,0 -DA:165,0 -DA:166,0 -DA:169,2 -DA:172,1 -DA:175,2 -DA:176,1 -DA:177,1 -DA:178,3 -DA:180,1 -DA:182,0 -DA:185,1 -DA:186,5 -DA:187,1 -DA:190,1 -DA:194,3 -DA:195,1 -DA:197,1 -DA:199,0 -DA:202,2 -DA:203,2 -DA:205,0 -DA:208,0 -DA:209,0 -DA:212,2 -DA:218,1 -DA:221,1 -DA:222,2 -DA:223,1 -DA:224,3 -DA:226,1 -DA:228,1 -DA:229,1 -DA:230,1 -DA:231,2 -DA:234,2 -DA:235,5 -DA:238,1 -DA:239,1 -DA:241,1 -DA:244,1 -DA:245,1 -DA:246,1 -DA:247,1 -DA:248,2 -DA:249,2 -DA:250,6 -DA:251,6 -DA:252,2 -DA:253,2 -DA:254,2 -DA:255,3 -DA:256,3 -DA:257,3 -DA:258,5 -DA:259,5 -DA:263,1 -DA:268,1 -DA:269,1 -DA:271,1 -DA:277,1 -DA:278,1 -DA:279,1 -DA:280,1 -DA:281,2 -DA:282,2 -DA:283,6 -DA:284,6 -DA:287,1 -DA:288,1 -DA:289,1 -DA:290,1 -DA:291,1 -DA:292,1 -DA:293,1 -DA:294,1 -DA:296,1 -DA:297,0 -DA:301,1 -DA:303,1 -DA:304,1 -DA:305,1 -DA:306,1 -DA:307,1 -DA:308,1 -DA:309,1 -DA:310,1 -DA:311,1 -DA:312,1 -DA:313,1 -DA:314,1 -DA:315,1 -DA:316,1 -DA:317,1 -DA:318,1 -DA:319,1 -DA:320,1 -DA:321,1 -DA:322,1 -DA:323,1 -DA:324,1 -DA:325,1 -DA:326,1 -DA:327,1 -DA:328,1 -DA:329,1 -DA:330,1 -DA:331,0 -DA:335,1 -DA:337,1 -DA:338,1 -DA:339,1 -DA:340,1 -DA:341,1 -DA:342,1 -DA:343,1 -DA:344,1 -DA:346,1 -DA:347,1 -DA:348,1 -DA:349,1 -DA:350,0 -DA:351,0 -DA:352,0 -DA:353,0 -DA:357,1 -DA:359,1 -DA:361,1 -DA:363,1 -DA:365,1 -DA:367,1 -DA:369,1 -DA:371,1 -DA:373,0 -DA:375,0 -DA:380,1 -DA:381,1 -DA:382,1 -DA:383,1 -DA:384,1 -DA:385,1 -DA:388,1 -DA:389,3 -DA:392,1 -DA:394,1 -DA:396,1 -DA:398,1 -DA:400,1 -DA:402,1 -DA:404,0 -LF:210 -LH:192 -end_of_record -SF:lib\view_model\repository\data_classes\meal\ImageData.dart -DA:20,1 -DA:34,0 -DA:36,0 -DA:38,0 -DA:40,0 -DA:42,0 -DA:44,2 -DA:46,0 -DA:47,0 -DA:48,0 -DA:49,0 -LF:11 -LH:2 -end_of_record -SF:lib\view_model\repository\data_classes\meal\Meal.dart -DA:46,1 -DA:79,0 -DA:80,0 -DA:83,0 -DA:84,0 -DA:87,0 -DA:88,0 -DA:89,0 -DA:90,0 -DA:91,0 -DA:92,0 -DA:93,0 -DA:94,0 -DA:95,0 -DA:96,0 -DA:97,0 -DA:98,0 -DA:102,0 -DA:103,0 -DA:104,0 -DA:106,0 -DA:109,0 -DA:110,0 -DA:111,0 -DA:113,0 -DA:116,2 -DA:118,0 -DA:120,0 -DA:122,0 -DA:124,0 -DA:126,0 -DA:128,0 -DA:130,0 -DA:132,0 -DA:134,0 -DA:136,0 -DA:138,0 -DA:140,0 -DA:142,0 -DA:144,0 -LF:40 -LH:2 -end_of_record -SF:lib\view_model\repository\data_classes\meal\Price.dart -DA:10,1 -DA:20,0 -DA:22,0 -DA:23,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:27,0 -DA:28,0 -DA:29,0 -DA:33,0 -DA:34,0 -DA:35,0 -DA:36,0 -DA:37,0 -DA:38,0 -LF:16 -LH:1 -end_of_record -SF:lib\view_model\repository\data_classes\mealplan\Canteen.dart -DA:6,1 -DA:12,2 -DA:14,2 -DA:16,0 -DA:17,0 -DA:18,0 -DA:19,0 -LF:7 -LH:3 -end_of_record -SF:lib\view_model\repository\data_classes\mealplan\Line.dart -DA:9,1 -DA:19,0 -DA:20,0 -DA:21,0 -DA:22,0 -DA:23,0 -DA:24,0 -DA:28,2 -DA:30,0 -DA:32,0 -DA:34,0 -LF:11 -LH:2 -end_of_record -SF:lib\view_model\repository\error_handling\Result.dart -DA:7,1 -DA:14,1 -DA:24,0 -LF:3 -LH:2 -end_of_record -SF:lib\model\api_server\requests\querys.graphql.dart -DA:9,1 -DA:15,1 -DA:16,1 -DA:17,1 -DA:18,1 -DA:19,1 -DA:32,0 -DA:33,0 -DA:34,0 -DA:35,0 -DA:36,0 -DA:37,0 -DA:38,0 -DA:39,0 -DA:43,0 -DA:45,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:55,0 -DA:60,0 -DA:63,0 -DA:64,0 -DA:65,0 -DA:68,0 -DA:69,0 -DA:70,0 -DA:73,0 -DA:74,0 -DA:75,0 -DA:83,0 -DA:84,0 -DA:86,0 -DA:108,0 -DA:119,0 -DA:124,0 -DA:125,0 -DA:126,0 -DA:127,0 -DA:129,0 -DA:130,0 -DA:137,0 -DA:141,0 -DA:146,0 -DA:186,0 -DA:191,0 -DA:192,0 -DA:199,0 -DA:202,0 -DA:206,0 -DA:207,0 -DA:216,0 -DA:221,0 -DA:222,0 -DA:226,0 -DA:228,0 -DA:229,0 -DA:230,0 -DA:231,0 -DA:232,0 -DA:237,0 -DA:238,0 -DA:239,0 -DA:240,0 -DA:241,0 -DA:245,0 -DA:246,0 -DA:248,0 -DA:250,0 -DA:255,0 -DA:256,0 -DA:259,0 -DA:260,0 -DA:261,0 -DA:267,0 -DA:269,0 -DA:270,0 -DA:288,0 -DA:299,0 -DA:300,0 -DA:301,0 -DA:302,0 -DA:308,0 -DA:312,0 -DA:316,1 -DA:321,1 -DA:322,1 -DA:323,1 -DA:324,1 -DA:326,2 -DA:327,1 -DA:328,1 -DA:337,0 -DA:338,0 -DA:339,0 -DA:340,0 -DA:341,0 -DA:342,0 -DA:346,0 -DA:348,0 -DA:349,0 -DA:350,0 -DA:351,0 -DA:356,0 -DA:361,0 -DA:364,0 -DA:365,0 -DA:366,0 -DA:369,0 -DA:370,0 -DA:371,0 -DA:372,0 -DA:376,0 -DA:377,0 -DA:378,0 -DA:386,0 -DA:387,0 -DA:389,0 -DA:415,0 -DA:426,0 -DA:430,0 -DA:431,0 -DA:432,0 -DA:434,0 -DA:435,0 -DA:438,0 -DA:444,0 -DA:446,0 -DA:448,0 -DA:449,0 -DA:454,0 -DA:458,0 -DA:462,0 -DA:463,0 -DA:564,0 -DA:570,0 -DA:571,0 -DA:577,0 -DA:579,0 -DA:582,0 -DA:587,0 -DA:588,0 -DA:594,0 -DA:598,0 -DA:603,1 -DA:611,1 -DA:612,1 -DA:613,1 -DA:614,1 -DA:615,1 -DA:616,1 -DA:617,1 -DA:620,1 -DA:622,3 -DA:623,1 -DA:638,0 -DA:639,0 -DA:640,0 -DA:641,0 -DA:642,0 -DA:643,0 -DA:644,0 -DA:645,0 -DA:646,0 -DA:647,0 -DA:648,0 -DA:649,0 -DA:653,0 -DA:655,0 -DA:656,0 -DA:657,0 -DA:658,0 -DA:659,0 -DA:660,0 -DA:664,0 -DA:669,0 -DA:674,0 -DA:675,0 -DA:678,0 -DA:679,0 -DA:680,0 -DA:683,0 -DA:684,0 -DA:685,0 -DA:688,0 -DA:689,0 -DA:690,0 -DA:693,0 -DA:694,0 -DA:696,0 -DA:699,0 -DA:700,0 -DA:701,0 -DA:702,0 -DA:706,0 -DA:709,0 -DA:710,0 -DA:711,0 -DA:719,0 -DA:720,0 -DA:722,0 -DA:751,0 -DA:762,0 -DA:769,0 -DA:770,0 -DA:771,0 -DA:772,0 -DA:774,0 -DA:775,0 -DA:777,0 -DA:778,0 -DA:780,0 -DA:781,0 -DA:784,0 -DA:785,0 -DA:786,0 -DA:789,0 -DA:793,0 -DA:794,0 -DA:796,0 -DA:797,0 -DA:802,0 -DA:806,0 -DA:813,0 -DA:814,0 -DA:815,0 -DA:816,0 -DA:820,1 -DA:834,1 -DA:835,1 -DA:836,1 -DA:837,1 -DA:838,1 -DA:839,1 -DA:840,1 -DA:841,1 -DA:842,1 -DA:843,1 -DA:844,1 -DA:845,1 -DA:846,1 -DA:849,1 -DA:850,1 -DA:852,3 -DA:853,1 -DA:855,3 -DA:856,1 -DA:857,1 -DA:859,1 -DA:862,2 -DA:863,1 -DA:864,1 -DA:866,2 -DA:867,1 -DA:868,1 -DA:895,0 -DA:896,0 -DA:897,0 -DA:898,0 -DA:899,0 -DA:900,0 -DA:901,0 -DA:902,0 -DA:903,0 -DA:904,0 -DA:905,0 -DA:906,0 -DA:907,0 -DA:908,0 -DA:909,0 -DA:910,0 -DA:911,0 -DA:912,0 -DA:913,0 -DA:914,0 -DA:915,0 -DA:916,0 -DA:917,0 -DA:918,0 -DA:919,0 -DA:920,0 -DA:924,0 -DA:926,0 -DA:927,0 -DA:928,0 -DA:929,0 -DA:930,0 -DA:931,0 -DA:932,0 -DA:933,0 -DA:934,0 -DA:935,0 -DA:936,0 -DA:937,0 -DA:942,0 -DA:943,0 -DA:946,0 -DA:947,0 -DA:952,0 -DA:957,0 -DA:960,0 -DA:961,0 -DA:962,0 -DA:965,0 -DA:966,0 -DA:967,0 -DA:970,0 -DA:971,0 -DA:972,0 -DA:975,0 -DA:976,0 -DA:977,0 -DA:980,0 -DA:981,0 -DA:982,0 -DA:985,0 -DA:986,0 -DA:987,0 -DA:988,0 -DA:992,0 -DA:993,0 -DA:994,0 -DA:997,0 -DA:998,0 -DA:999,0 -DA:1000,0 -DA:1004,0 -DA:1005,0 -DA:1006,0 -DA:1009,0 -DA:1010,0 -DA:1011,0 -DA:1014,0 -DA:1015,0 -DA:1016,0 -DA:1019,0 -DA:1020,0 -DA:1021,0 -DA:1022,0 -DA:1026,0 -DA:1027,0 -DA:1028,0 -DA:1031,0 -DA:1032,0 -DA:1033,0 -DA:1034,0 -DA:1038,0 -DA:1039,0 -DA:1040,0 -DA:1048,0 -DA:1049,0 -DA:1051,0 -DA:1094,0 -DA:1105,0 -DA:1118,0 -DA:1119,0 -DA:1120,0 -DA:1121,0 -DA:1123,0 -DA:1124,0 -DA:1126,0 -DA:1127,0 -DA:1129,0 -DA:1130,0 -DA:1132,0 -DA:1133,0 -DA:1135,0 -DA:1136,0 -DA:1138,0 -DA:1139,0 -DA:1141,0 -DA:1142,0 -DA:1144,0 -DA:1145,0 -DA:1147,0 -DA:1148,0 -DA:1151,0 -DA:1152,0 -DA:1153,0 -DA:1156,0 -DA:1157,0 -DA:1158,0 -DA:1159,0 -DA:1162,0 -DA:1163,0 -DA:1164,0 -DA:1165,0 -DA:1168,0 -DA:1174,0 -DA:1176,0 -DA:1178,0 -DA:1179,0 -DA:1180,0 -DA:1186,0 -DA:1188,0 -DA:1190,0 -DA:1191,0 -DA:1196,0 -DA:1200,0 -DA:1213,0 -DA:1214,0 -DA:1215,0 -DA:1216,0 -DA:1217,0 -DA:1218,0 -DA:1219,0 -DA:1220,0 -DA:1506,0 -DA:1511,0 -DA:1512,0 -DA:1519,0 -DA:1522,0 -DA:1526,0 -DA:1527,0 -DA:1536,0 -DA:1541,1 -DA:1548,1 -DA:1549,1 -DA:1550,1 -DA:1551,1 -DA:1552,1 -DA:1553,1 -DA:1556,1 -DA:1569,0 -DA:1570,0 -DA:1571,0 -DA:1572,0 -DA:1573,0 -DA:1574,0 -DA:1575,0 -DA:1576,0 -DA:1577,0 -DA:1578,0 -DA:1582,0 -DA:1584,0 -DA:1585,0 -DA:1586,0 -DA:1587,0 -DA:1588,0 -DA:1596,0 -DA:1601,0 -DA:1602,0 -DA:1605,0 -DA:1606,0 -DA:1607,0 -DA:1610,0 -DA:1611,0 -DA:1612,0 -DA:1615,0 -DA:1616,0 -DA:1617,0 -DA:1620,0 -DA:1621,0 -DA:1622,0 -DA:1631,0 -DA:1632,0 -DA:1634,0 -DA:1657,0 -DA:1668,0 -DA:1674,0 -DA:1675,0 -DA:1676,0 -DA:1678,0 -DA:1679,0 -DA:1682,0 -DA:1683,0 -DA:1685,0 -DA:1686,0 -DA:1693,0 -DA:1697,0 -DA:1703,0 -DA:1707,1 -DA:1714,1 -DA:1715,1 -DA:1716,1 -DA:1717,1 -DA:1718,1 -DA:1719,1 -DA:1720,1 -DA:1735,0 -DA:1736,0 -DA:1737,0 -DA:1738,0 -DA:1739,0 -DA:1740,0 -DA:1741,0 -DA:1742,0 -DA:1743,0 -DA:1744,0 -DA:1748,0 -DA:1750,0 -DA:1751,0 -DA:1752,0 -DA:1753,0 -DA:1754,0 -DA:1762,0 -DA:1767,0 -DA:1768,0 -DA:1771,0 -DA:1772,0 -DA:1773,0 -DA:1776,0 -DA:1777,0 -DA:1778,0 -DA:1781,0 -DA:1782,0 -DA:1783,0 -DA:1786,0 -DA:1787,0 -DA:1788,0 -DA:1797,0 -DA:1798,0 -DA:1800,0 -DA:1823,0 -DA:1834,0 -DA:1840,0 -DA:1841,0 -DA:1842,0 -DA:1844,0 -DA:1845,0 -DA:1847,0 -DA:1848,0 -DA:1850,0 -DA:1851,0 -DA:1858,0 -DA:1862,0 -DA:1868,0 -DA:1872,1 -DA:1883,1 -DA:1884,1 -DA:1885,1 -DA:1886,1 -DA:1887,1 -DA:1888,1 -DA:1889,1 -DA:1890,1 -DA:1891,1 -DA:1892,1 -DA:1895,1 -DA:1920,0 -DA:1921,0 -DA:1922,0 -DA:1923,0 -DA:1924,0 -DA:1925,0 -DA:1926,0 -DA:1927,0 -DA:1928,0 -DA:1929,0 -DA:1930,0 -DA:1931,0 -DA:1932,0 -DA:1933,0 -DA:1934,0 -DA:1935,0 -DA:1936,0 -DA:1937,0 -DA:1941,0 -DA:1943,0 -DA:1944,0 -DA:1945,0 -DA:1946,0 -DA:1947,0 -DA:1948,0 -DA:1949,0 -DA:1950,0 -DA:1951,0 -DA:1963,0 -DA:1968,0 -DA:1969,0 -DA:1972,0 -DA:1973,0 -DA:1974,0 -DA:1977,0 -DA:1978,0 -DA:1979,0 -DA:1982,0 -DA:1983,0 -DA:1984,0 -DA:1987,0 -DA:1988,0 -DA:1989,0 -DA:1992,0 -DA:1993,0 -DA:1994,0 -DA:1997,0 -DA:1998,0 -DA:1999,0 -DA:2002,0 -DA:2003,0 -DA:2004,0 -DA:2007,0 -DA:2008,0 -DA:2009,0 -DA:2018,0 -DA:2019,0 -DA:2021,0 -DA:2048,0 -DA:2059,0 -DA:2069,0 -DA:2070,0 -DA:2071,0 -DA:2072,0 -DA:2073,0 -DA:2076,0 -DA:2077,0 -DA:2079,0 -DA:2080,0 -DA:2082,0 -DA:2083,0 -DA:2085,0 -DA:2086,0 -DA:2088,0 -DA:2089,0 -DA:2096,0 -DA:2100,0 -DA:2110,0 -DA:2114,1 -DA:2124,1 -DA:2125,1 -DA:2126,1 -DA:2127,1 -DA:2128,1 -DA:2129,1 -DA:2130,1 -DA:2131,1 -DA:2132,1 -DA:2136,3 -DA:2137,1 -DA:2139,3 -DA:2140,1 -DA:2141,1 -DA:2142,1 -DA:2161,0 -DA:2162,0 -DA:2163,0 -DA:2164,0 -DA:2165,0 -DA:2166,0 -DA:2167,0 -DA:2168,0 -DA:2169,0 -DA:2170,0 -DA:2171,0 -DA:2172,0 -DA:2173,0 -DA:2174,0 -DA:2175,0 -DA:2176,0 -DA:2177,0 -DA:2178,0 -DA:2182,0 -DA:2184,0 -DA:2185,0 -DA:2186,0 -DA:2187,0 -DA:2188,0 -DA:2189,0 -DA:2190,0 -DA:2191,0 -DA:2194,0 -DA:2195,0 -DA:2202,0 -DA:2207,0 -DA:2208,0 -DA:2211,0 -DA:2212,0 -DA:2213,0 -DA:2216,0 -DA:2217,0 -DA:2218,0 -DA:2221,0 -DA:2222,0 -DA:2223,0 -DA:2226,0 -DA:2227,0 -DA:2228,0 -DA:2229,0 -DA:2233,0 -DA:2234,0 -DA:2235,0 -DA:2238,0 -DA:2239,0 -DA:2240,0 -DA:2241,0 -DA:2245,0 -DA:2246,0 -DA:2247,0 -DA:2250,0 -DA:2251,0 -DA:2252,0 -DA:2255,0 -DA:2256,0 -DA:2257,0 -DA:2265,0 -DA:2266,0 -DA:2268,0 -DA:2295,0 -DA:2306,0 -DA:2315,0 -DA:2316,0 -DA:2317,0 -DA:2318,0 -DA:2320,0 -DA:2321,0 -DA:2323,0 -DA:2324,0 -DA:2326,0 -DA:2327,0 -DA:2329,0 -DA:2330,0 -DA:2332,0 -DA:2333,0 -DA:2336,0 -DA:2337,0 -DA:2338,0 -DA:2344,0 -DA:2348,0 -DA:2357,0 -DA:2358,0 -DA:2362,1 -DA:2370,1 -DA:2371,1 -DA:2372,1 -DA:2373,1 -DA:2374,1 -DA:2375,1 -DA:2376,1 -DA:2395,0 -DA:2396,0 -DA:2397,0 -DA:2398,0 -DA:2399,0 -DA:2400,0 -DA:2401,0 -DA:2402,0 -DA:2403,0 -DA:2404,0 -DA:2405,0 -DA:2406,0 -DA:2410,0 -DA:2412,0 -DA:2413,0 -DA:2414,0 -DA:2415,0 -DA:2416,0 -DA:2417,0 -DA:2426,0 -DA:2431,0 -DA:2434,0 -DA:2435,0 -DA:2436,0 -DA:2439,0 -DA:2440,0 -DA:2441,0 -DA:2444,0 -DA:2445,0 -DA:2446,0 -DA:2449,0 -DA:2450,0 -DA:2451,0 -DA:2454,0 -DA:2455,0 -DA:2456,0 -DA:2464,0 -DA:2465,0 -DA:2467,0 -DA:2491,0 -DA:2502,0 -DA:2509,0 -DA:2510,0 -DA:2511,0 -DA:2513,0 -DA:2514,0 -DA:2516,0 -DA:2517,0 -DA:2519,0 -DA:2520,0 -DA:2522,0 -DA:2523,0 -DA:2530,0 -DA:2534,0 -DA:2541,0 -DA:2595,0 -DA:2600,0 -DA:2601,0 -DA:2608,0 -DA:2611,0 -DA:2615,0 -DA:2616,0 -DA:2625,0 -DA:2630,1 -DA:2631,2 -DA:2635,1 -DA:2637,0 -DA:2639,0 -DA:2640,0 -DA:2641,0 -DA:2642,0 -DA:2647,3 -DA:2648,1 -DA:2649,1 -DA:2650,1 -DA:2651,1 -DA:2655,0 -DA:2656,0 -DA:2658,0 -DA:2660,0 -DA:2665,0 -DA:2666,0 -DA:2669,0 -DA:2670,0 -DA:2671,0 -DA:2677,0 -DA:2679,0 -DA:2680,0 -DA:2698,0 -DA:2709,0 -DA:2710,0 -DA:2711,0 -DA:2712,0 -DA:2718,0 -DA:2722,0 -DA:2726,1 -DA:2731,1 -DA:2732,1 -DA:2733,1 -DA:2734,1 -DA:2736,3 -DA:2737,1 -DA:2746,0 -DA:2747,0 -DA:2748,0 -DA:2749,0 -DA:2750,0 -DA:2751,0 -DA:2755,0 -DA:2757,0 -DA:2758,0 -DA:2759,0 -DA:2760,0 -DA:2765,0 -DA:2770,0 -DA:2771,0 -DA:2774,0 -DA:2775,0 -DA:2776,0 -DA:2779,0 -DA:2780,0 -DA:2781,0 -DA:2782,0 -DA:2786,0 -DA:2787,0 -DA:2788,0 -DA:2796,0 -DA:2797,0 -DA:2799,0 -DA:2824,0 -DA:2835,0 -DA:2839,0 -DA:2840,0 -DA:2841,0 -DA:2843,0 -DA:2844,0 -DA:2847,0 -DA:2851,0 -DA:2853,0 -DA:2855,0 -DA:2856,0 -DA:2861,0 -DA:2865,0 -DA:2869,0 -DA:2870,0 -DA:2923,1 -DA:2925,1 -DA:2933,1 -DA:2946,1 -DA:2947,1 -DA:2952,0 -DA:2957,0 -DA:2961,0 -DA:2970,0 -DA:2971,0 -DA:2972,0 -DA:2973,0 -DA:2974,0 -DA:2975,0 -DA:2981,0 -DA:2994,0 -DA:2995,0 -DA:3000,0 -DA:3013,0 -DA:3016,0 -DA:3018,0 -DA:3024,1 -DA:3026,1 -DA:3027,0 -DA:3029,0 -DA:3030,0 -DA:3035,0 -DA:3036,0 -DA:3038,0 -DA:3039,0 -DA:3041,0 -DA:3044,0 -DA:3048,0 -DA:3049,0 -DA:3051,0 -DA:3052,0 -DA:3056,0 -DA:3060,0 -DA:3062,0 -DA:3063,0 -DA:3066,0 -DA:3070,0 -DA:3074,0 -DA:3082,1 -DA:3086,2 -DA:3091,1 -DA:3093,0 -DA:3094,0 -DA:3095,0 -DA:3096,0 -DA:3097,0 -DA:3098,0 -DA:3099,0 -DA:3104,3 -DA:3105,3 -DA:3106,1 -DA:3107,1 -DA:3108,1 -DA:3109,1 -DA:3110,1 -DA:3111,1 -DA:3115,0 -DA:3116,0 -DA:3118,0 -DA:3120,0 -DA:3125,0 -DA:3126,0 -DA:3129,0 -DA:3130,0 -DA:3131,0 -DA:3134,0 -DA:3135,0 -DA:3136,0 -DA:3142,0 -DA:3144,0 -DA:3145,0 -DA:3146,0 -DA:3170,0 -DA:3181,0 -DA:3185,0 -DA:3186,0 -DA:3187,0 -DA:3188,0 -DA:3189,0 -DA:3195,0 -DA:3199,0 -DA:3203,0 -DA:3207,1 -DA:3212,1 -DA:3213,1 -DA:3214,1 -DA:3215,1 -DA:3218,1 -DA:3227,0 -DA:3228,0 -DA:3229,0 -DA:3230,0 -DA:3231,0 -DA:3232,0 -DA:3236,0 -DA:3238,0 -DA:3239,0 -DA:3240,0 -DA:3246,0 -DA:3251,0 -DA:3254,0 -DA:3255,0 -DA:3256,0 -DA:3259,0 -DA:3260,0 -DA:3261,0 -DA:3269,0 -DA:3270,0 -DA:3272,0 -DA:3294,0 -DA:3305,0 -DA:3309,0 -DA:3310,0 -DA:3311,0 -DA:3313,0 -DA:3314,0 -DA:3317,0 -DA:3318,0 -DA:3320,0 -DA:3321,0 -DA:3322,0 -DA:3328,0 -DA:3332,0 -DA:3336,0 -DA:3337,0 -DA:3338,0 -DA:3405,1 -DA:3407,1 -DA:3415,1 -DA:3428,1 -DA:3429,1 -DA:3434,0 -DA:3439,0 -DA:3441,0 -DA:3450,0 -DA:3451,0 -DA:3452,0 -DA:3453,0 -DA:3454,0 -DA:3455,0 -DA:3461,0 -DA:3474,0 -DA:3475,0 -DA:3480,0 -DA:3492,0 -DA:3495,0 -DA:3497,0 -DA:3503,1 -DA:3505,1 -DA:3506,0 -DA:3508,0 -DA:3509,0 -DA:3514,0 -DA:3515,0 -DA:3517,0 -DA:3518,0 -DA:3520,0 -DA:3523,0 -DA:3527,0 -DA:3528,0 -DA:3529,0 -DA:3530,0 -DA:3534,0 -DA:3538,0 -DA:3540,0 -DA:3541,0 -DA:3543,0 -DA:3547,0 -DA:3551,0 -DA:3559,1 -DA:3564,2 -DA:3570,1 -DA:3572,0 -DA:3573,0 -DA:3574,0 -DA:3575,0 -DA:3576,0 -DA:3577,0 -DA:3578,0 -DA:3579,0 -DA:3580,0 -DA:3585,3 -DA:3586,3 -DA:3587,3 -DA:3588,1 -DA:3589,1 -DA:3590,1 -DA:3591,1 -DA:3592,1 -DA:3593,1 -DA:3594,1 -DA:3595,1 -DA:3599,0 -DA:3600,0 -DA:3602,0 -DA:3604,0 -DA:3609,0 -DA:3610,0 -DA:3613,0 -DA:3614,0 -DA:3615,0 -DA:3618,0 -DA:3619,0 -DA:3620,0 -DA:3623,0 -DA:3624,0 -DA:3625,0 -DA:3631,0 -DA:3633,0 -DA:3634,0 -DA:3635,0 -DA:3636,0 -DA:3662,0 -DA:3673,0 -DA:3678,0 -DA:3679,0 -DA:3680,0 -DA:3681,0 -DA:3682,0 -DA:3683,0 -DA:3684,0 -DA:3690,0 -DA:3694,0 -DA:3699,0 -DA:3703,1 -DA:3708,1 -DA:3709,1 -DA:3710,1 -DA:3711,1 -DA:3714,1 -DA:3723,0 -DA:3724,0 -DA:3725,0 -DA:3726,0 -DA:3727,0 -DA:3728,0 -DA:3732,0 -DA:3734,0 -DA:3735,0 -DA:3736,0 -DA:3742,0 -DA:3747,0 -DA:3750,0 -DA:3751,0 -DA:3752,0 -DA:3755,0 -DA:3756,0 -DA:3757,0 -DA:3765,0 -DA:3767,0 -DA:3789,0 -DA:3800,0 -DA:3804,0 -DA:3805,0 -DA:3806,0 -DA:3808,0 -DA:3809,0 -DA:3812,0 -DA:3813,0 -DA:3815,0 -DA:3816,0 -DA:3822,0 -DA:3826,0 -DA:3830,0 -DA:3831,0 -DA:3832,0 -DA:3914,1 -DA:3915,1 -DA:3922,1 -DA:3935,1 -DA:3936,1 -DA:3941,0 -DA:3946,0 -DA:3948,0 -DA:3957,0 -DA:3958,0 -DA:3959,0 -DA:3960,0 -DA:3961,0 -DA:3962,0 -DA:3968,0 -DA:3981,0 -DA:3982,0 -DA:3987,0 -DA:3999,0 -DA:4002,0 -DA:4004,0 -DA:4010,1 -DA:4012,1 -DA:4013,0 -DA:4015,0 -DA:4016,0 -DA:4021,0 -DA:4022,0 -DA:4023,0 -DA:4024,0 -DA:4026,0 -DA:4029,0 -DA:4033,0 -DA:4034,0 -DA:4035,0 -DA:4036,0 -DA:4040,0 -DA:4044,0 -DA:4046,0 -DA:4047,0 -DA:4049,0 -DA:4052,0 -DA:4056,0 -DA:4064,1 -DA:4065,2 -DA:4069,1 -DA:4071,0 -DA:4072,0 -DA:4073,0 -DA:4074,0 -DA:4075,0 -DA:4080,3 -DA:4081,1 -DA:4082,1 -DA:4083,1 -DA:4084,1 -DA:4088,0 -DA:4089,0 -DA:4091,0 -DA:4093,0 -DA:4098,0 -DA:4099,0 -DA:4102,0 -DA:4103,0 -DA:4104,0 -DA:4110,0 -DA:4112,0 -DA:4113,0 -DA:4131,0 -DA:4142,0 -DA:4143,0 -DA:4144,0 -DA:4145,0 -DA:4146,0 -DA:4152,0 -DA:4156,0 -DA:4160,1 -DA:4166,1 -DA:4167,1 -DA:4168,1 -DA:4169,1 -DA:4170,1 -DA:4173,1 -DA:4175,3 -DA:4176,1 -DA:4187,0 -DA:4188,0 -DA:4189,0 -DA:4190,0 -DA:4191,0 -DA:4192,0 -DA:4193,0 -DA:4194,0 -DA:4198,0 -DA:4200,0 -DA:4201,0 -DA:4202,0 -DA:4203,0 -DA:4205,0 -DA:4210,0 -DA:4215,0 -DA:4218,0 -DA:4219,0 -DA:4220,0 -DA:4223,0 -DA:4224,0 -DA:4225,0 -DA:4228,0 -DA:4229,0 -DA:4230,0 -DA:4231,0 -DA:4235,0 -DA:4236,0 -DA:4237,0 -DA:4245,0 -DA:4246,0 -DA:4248,0 -DA:4275,0 -DA:4286,0 -DA:4291,0 -DA:4292,0 -DA:4293,0 -DA:4295,0 -DA:4296,0 -DA:4298,0 -DA:4299,0 -DA:4302,0 -DA:4303,0 -DA:4305,0 -DA:4306,0 -DA:4307,0 -DA:4310,0 -DA:4314,0 -DA:4316,0 -DA:4318,0 -DA:4319,0 -DA:4324,0 -DA:4328,0 -DA:4333,0 -DA:4334,0 -DA:4335,0 -DA:4336,0 -DA:4410,1 -DA:4411,1 -DA:4418,1 -DA:4431,1 -DA:4432,1 -DA:4437,0 -DA:4442,0 -DA:4444,0 -DA:4453,0 -DA:4454,0 -DA:4455,0 -DA:4456,0 -DA:4457,0 -DA:4458,0 -DA:4464,0 -DA:4477,0 -DA:4478,0 -DA:4483,0 -DA:4495,0 -DA:4498,0 -DA:4500,0 -DA:4506,1 -DA:4508,1 -DA:4509,0 -DA:4511,0 -DA:4512,0 -DA:4517,0 -DA:4518,0 -DA:4519,0 -DA:4520,0 -DA:4522,0 -DA:4525,0 -DA:4529,0 -DA:4530,0 -DA:4531,0 -DA:4532,0 -DA:4536,0 -DA:4540,0 -DA:4542,0 -DA:4543,0 -DA:4545,0 -DA:4548,0 -DA:4552,0 -LF:1329 -LH:202 -end_of_record -SF:lib\model\api_server\requests\schema.graphql.dart -DA:20,0 -DA:22,0 -DA:24,0 -DA:26,0 -DA:28,0 -DA:30,0 -DA:32,0 -DA:34,0 -DA:36,0 -DA:38,0 -DA:40,0 -DA:42,0 -DA:44,0 -DA:46,0 -DA:48,0 -DA:50,0 -DA:52,0 -DA:57,1 -DA:59,1 -DA:61,1 -DA:63,1 -DA:65,1 -DA:67,1 -DA:69,1 -DA:71,1 -DA:73,1 -DA:75,1 -DA:77,1 -DA:79,1 -DA:81,1 -DA:83,0 -DA:85,0 -DA:87,0 -DA:126,0 -DA:128,0 -DA:130,0 -DA:132,0 -DA:134,0 -DA:136,0 -DA:138,0 -DA:140,0 -DA:142,0 -DA:144,0 -DA:146,0 -DA:148,0 -DA:150,0 -DA:152,0 -DA:154,0 -DA:156,0 -DA:158,0 -DA:160,0 -DA:162,0 -DA:164,0 -DA:166,0 -DA:168,0 -DA:170,0 -DA:172,0 -DA:174,0 -DA:176,0 -DA:178,0 -DA:180,0 -DA:182,0 -DA:184,0 -DA:189,1 -DA:191,1 -DA:193,1 -DA:195,1 -DA:197,1 -DA:199,1 -DA:201,1 -DA:203,1 -DA:205,1 -DA:207,1 -DA:209,1 -DA:211,1 -DA:213,1 -DA:215,1 -DA:217,1 -DA:219,1 -DA:221,1 -DA:223,1 -DA:225,1 -DA:227,1 -DA:229,1 -DA:231,1 -DA:233,1 -DA:235,1 -DA:237,1 -DA:239,1 -DA:241,1 -DA:243,1 -DA:245,1 -DA:264,0 -DA:266,0 -DA:268,0 -DA:270,0 -DA:272,0 -DA:274,0 -DA:276,0 -DA:278,0 -DA:280,0 -DA:282,0 -DA:287,1 -DA:289,1 -DA:291,1 -DA:293,1 -DA:295,0 -DA:297,0 -DA:299,0 -DA:301,0 -DA:303,0 -DA:320,1 -DA:322,1 -DA:324,1 -DA:326,1 -DA:328,1 -DA:330,0 -DA:332,0 -DA:334,0 -DA:339,0 -DA:341,0 -DA:343,0 -DA:345,0 -DA:347,0 -DA:349,0 -DA:351,0 -LF:126 -LH:51 -end_of_record -SF:lib\view_model\repository\data_classes\meal\Side.dart -DA:14,1 -DA:29,0 -DA:31,0 -DA:32,0 -DA:33,0 -DA:34,0 -DA:35,0 -DA:36,0 -DA:37,0 -DA:41,0 -DA:42,0 -DA:43,0 -DA:45,0 -DA:48,0 -DA:49,0 -DA:50,0 -DA:52,0 -DA:55,0 -DA:57,0 -DA:59,0 -DA:61,0 -DA:63,0 -LF:22 -LH:1 -end_of_record -SF:lib\view_model\repository\data_classes\mealplan\MealPlan.dart -DA:11,1 -DA:21,0 -DA:23,0 -DA:25,0 -DA:27,0 -DA:29,0 -DA:30,0 -DA:31,0 -DA:32,0 -DA:33,0 -LF:10 -LH:1 -end_of_record -SF:lib\view_model\repository\error_handling\NoMealException.dart -DA:8,0 -LF:1 -LH:0 -end_of_record -SF:lib\model\api_server\requests\mutations.graphql.dart -DA:9,1 -DA:10,2 -DA:14,1 -DA:16,0 -DA:18,0 -DA:19,0 -DA:20,0 -DA:21,0 -DA:26,3 -DA:27,1 -DA:28,1 -DA:29,1 -DA:30,1 -DA:34,0 -DA:35,0 -DA:37,0 -DA:39,0 -DA:44,0 -DA:45,0 -DA:48,0 -DA:49,0 -DA:50,0 -DA:56,0 -DA:58,0 -DA:59,0 -DA:77,0 -DA:88,0 -DA:89,0 -DA:90,0 -DA:91,0 -DA:92,0 -DA:98,0 -DA:102,0 -DA:106,1 -DA:111,1 -DA:112,1 -DA:113,1 -DA:114,1 -DA:124,0 -DA:125,0 -DA:126,0 -DA:127,0 -DA:128,0 -DA:129,0 -DA:133,0 -DA:135,0 -DA:136,0 -DA:137,0 -DA:143,0 -DA:148,0 -DA:149,0 -DA:152,0 -DA:153,0 -DA:154,0 -DA:157,0 -DA:158,0 -DA:159,0 -DA:167,0 -DA:168,0 -DA:170,0 -DA:191,0 -DA:202,0 -DA:206,0 -DA:207,0 -DA:208,0 -DA:210,0 -DA:211,0 -DA:218,0 -DA:222,0 -DA:226,0 -DA:268,1 -DA:270,1 -DA:278,1 -DA:291,1 -DA:292,1 -DA:297,0 -DA:301,0 -DA:305,0 -DA:315,0 -DA:316,0 -DA:317,0 -DA:318,0 -DA:319,0 -DA:320,0 -DA:326,0 -DA:339,0 -DA:340,0 -DA:345,0 -DA:357,1 -DA:359,1 -DA:360,0 -DA:362,0 -DA:366,0 -DA:376,0 -DA:379,0 -DA:380,0 -DA:381,0 -DA:382,0 -DA:383,0 -DA:384,0 -DA:386,0 -DA:390,0 -DA:393,0 -DA:397,0 -DA:409,0 -DA:414,0 -DA:418,0 -DA:422,0 -DA:432,0 -DA:433,0 -DA:434,0 -DA:435,0 -DA:436,0 -DA:437,0 -DA:454,0 -DA:458,0 -DA:460,0 -DA:461,0 -DA:465,0 -DA:466,0 -DA:471,0 -DA:472,0 -DA:474,0 -DA:482,1 -DA:483,2 -DA:487,1 -DA:489,0 -DA:490,0 -DA:491,0 -DA:492,0 -DA:493,0 -DA:498,3 -DA:499,1 -DA:500,1 -DA:501,1 -DA:502,1 -DA:506,0 -DA:507,0 -DA:509,0 -DA:511,0 -DA:516,0 -DA:517,0 -DA:520,0 -DA:521,0 -DA:522,0 -DA:528,0 -DA:530,0 -DA:531,0 -DA:549,0 -DA:560,0 -DA:561,0 -DA:562,0 -DA:563,0 -DA:564,0 -DA:570,0 -DA:574,0 -DA:578,1 -DA:583,1 -DA:584,1 -DA:585,1 -DA:586,1 -DA:596,0 -DA:597,0 -DA:598,0 -DA:599,0 -DA:600,0 -DA:601,0 -DA:605,0 -DA:607,0 -DA:608,0 -DA:609,0 -DA:615,0 -DA:620,0 -DA:623,0 -DA:624,0 -DA:625,0 -DA:628,0 -DA:629,0 -DA:630,0 -DA:638,0 -DA:639,0 -DA:641,0 -DA:662,0 -DA:673,0 -DA:677,0 -DA:678,0 -DA:679,0 -DA:681,0 -DA:682,0 -DA:689,0 -DA:693,0 -DA:697,0 -DA:739,1 -DA:741,1 -DA:749,1 -DA:762,1 -DA:763,1 -DA:768,0 -DA:772,0 -DA:774,0 -DA:784,0 -DA:785,0 -DA:786,0 -DA:787,0 -DA:788,0 -DA:789,0 -DA:795,0 -DA:808,0 -DA:809,0 -DA:814,0 -DA:826,1 -DA:828,1 -DA:829,0 -DA:831,0 -DA:835,0 -DA:845,0 -DA:848,0 -DA:849,0 -DA:850,0 -DA:851,0 -DA:852,0 -DA:853,0 -DA:855,0 -DA:859,0 -DA:861,0 -DA:865,0 -DA:877,0 -DA:882,0 -DA:886,0 -DA:888,0 -DA:898,0 -DA:899,0 -DA:900,0 -DA:901,0 -DA:902,0 -DA:903,0 -DA:920,0 -DA:924,0 -DA:926,0 -DA:927,0 -DA:931,0 -DA:932,0 -DA:937,0 -DA:938,0 -DA:940,0 -DA:948,1 -DA:949,2 -DA:953,1 -DA:955,0 -DA:956,0 -DA:957,0 -DA:958,0 -DA:959,0 -DA:964,3 -DA:965,1 -DA:966,1 -DA:967,1 -DA:968,1 -DA:972,0 -DA:973,0 -DA:975,0 -DA:977,0 -DA:982,0 -DA:983,0 -DA:986,0 -DA:987,0 -DA:988,0 -DA:994,0 -DA:996,0 -DA:997,0 -DA:1015,0 -DA:1026,0 -DA:1027,0 -DA:1028,0 -DA:1029,0 -DA:1030,0 -DA:1036,0 -DA:1040,0 -DA:1044,1 -DA:1049,1 -DA:1050,1 -DA:1051,1 -DA:1052,1 -DA:1062,0 -DA:1063,0 -DA:1064,0 -DA:1065,0 -DA:1066,0 -DA:1067,0 -DA:1071,0 -DA:1073,0 -DA:1074,0 -DA:1075,0 -DA:1081,0 -DA:1086,0 -DA:1089,0 -DA:1090,0 -DA:1091,0 -DA:1094,0 -DA:1095,0 -DA:1096,0 -DA:1104,0 -DA:1105,0 -DA:1107,0 -DA:1128,0 -DA:1139,0 -DA:1143,0 -DA:1144,0 -DA:1145,0 -DA:1147,0 -DA:1148,0 -DA:1155,0 -DA:1159,0 -DA:1163,0 -DA:1205,1 -DA:1207,1 -DA:1215,1 -DA:1228,1 -DA:1229,1 -DA:1234,0 -DA:1238,0 -DA:1240,0 -DA:1250,0 -DA:1251,0 -DA:1252,0 -DA:1253,0 -DA:1254,0 -DA:1255,0 -DA:1261,0 -DA:1274,0 -DA:1275,0 -DA:1280,0 -DA:1292,1 -DA:1294,1 -DA:1295,0 -DA:1297,0 -DA:1301,0 -DA:1311,0 -DA:1314,0 -DA:1315,0 -DA:1316,0 -DA:1317,0 -DA:1318,0 -DA:1319,0 -DA:1321,0 -DA:1325,0 -DA:1327,0 -DA:1331,0 -DA:1343,0 -DA:1348,0 -DA:1352,0 -DA:1354,0 -DA:1364,0 -DA:1365,0 -DA:1366,0 -DA:1367,0 -DA:1368,0 -DA:1369,0 -DA:1386,0 -DA:1390,0 -DA:1392,0 -DA:1393,0 -DA:1397,0 -DA:1398,0 -DA:1403,0 -DA:1404,0 -DA:1406,0 -DA:1414,1 -DA:1415,2 -DA:1419,1 -DA:1421,0 -DA:1422,0 -DA:1423,0 -DA:1424,0 -DA:1425,0 -DA:1430,3 -DA:1431,1 -DA:1432,1 -DA:1433,1 -DA:1434,1 -DA:1438,0 -DA:1439,0 -DA:1441,0 -DA:1443,0 -DA:1448,0 -DA:1449,0 -DA:1452,0 -DA:1453,0 -DA:1454,0 -DA:1460,0 -DA:1462,0 -DA:1463,0 -DA:1481,0 -DA:1492,0 -DA:1493,0 -DA:1494,0 -DA:1495,0 -DA:1496,0 -DA:1502,0 -DA:1506,0 -DA:1510,1 -DA:1515,1 -DA:1516,1 -DA:1517,1 -DA:1518,1 -DA:1528,0 -DA:1529,0 -DA:1530,0 -DA:1531,0 -DA:1532,0 -DA:1533,0 -DA:1537,0 -DA:1539,0 -DA:1540,0 -DA:1541,0 -DA:1547,0 -DA:1552,0 -DA:1555,0 -DA:1556,0 -DA:1557,0 -DA:1560,0 -DA:1561,0 -DA:1562,0 -DA:1570,0 -DA:1571,0 -DA:1573,0 -DA:1594,0 -DA:1605,0 -DA:1609,0 -DA:1610,0 -DA:1611,0 -DA:1613,0 -DA:1614,0 -DA:1621,0 -DA:1625,0 -DA:1629,0 -DA:1671,1 -DA:1672,1 -DA:1680,1 -DA:1693,1 -DA:1694,1 -DA:1699,0 -DA:1703,0 -DA:1705,0 -DA:1715,0 -DA:1716,0 -DA:1717,0 -DA:1718,0 -DA:1719,0 -DA:1720,0 -DA:1726,0 -DA:1739,0 -DA:1740,0 -DA:1745,0 -DA:1757,1 -DA:1759,1 -DA:1760,0 -DA:1762,0 -DA:1766,0 -DA:1776,0 -DA:1779,0 -DA:1780,0 -DA:1781,0 -DA:1782,0 -DA:1783,0 -DA:1784,0 -DA:1786,0 -DA:1790,0 -DA:1792,0 -DA:1796,0 -DA:1808,0 -DA:1813,0 -DA:1817,0 -DA:1819,0 -DA:1829,0 -DA:1830,0 -DA:1831,0 -DA:1832,0 -DA:1833,0 -DA:1834,0 -DA:1851,0 -DA:1855,0 -DA:1857,0 -DA:1858,0 -DA:1862,0 -DA:1863,0 -DA:1868,0 -DA:1869,0 -DA:1871,0 -DA:1879,1 -DA:1883,2 -DA:1888,1 -DA:1890,0 -DA:1891,0 -DA:1892,0 -DA:1893,0 -DA:1894,0 -DA:1895,0 -DA:1896,0 -DA:1901,3 -DA:1902,3 -DA:1903,1 -DA:1904,1 -DA:1905,1 -DA:1906,1 -DA:1907,1 -DA:1908,1 -DA:1912,0 -DA:1913,0 -DA:1915,0 -DA:1917,0 -DA:1922,0 -DA:1923,0 -DA:1926,0 -DA:1927,0 -DA:1928,0 -DA:1931,0 -DA:1932,0 -DA:1933,0 -DA:1939,0 -DA:1941,0 -DA:1942,0 -DA:1943,0 -DA:1967,0 -DA:1978,0 -DA:1982,0 -DA:1983,0 -DA:1984,0 -DA:1985,0 -DA:1986,0 -DA:1987,0 -DA:1993,0 -DA:1997,0 -DA:2001,0 -DA:2005,1 -DA:2010,1 -DA:2011,1 -DA:2012,1 -DA:2013,1 -DA:2023,0 -DA:2024,0 -DA:2025,0 -DA:2026,0 -DA:2027,0 -DA:2028,0 -DA:2032,0 -DA:2034,0 -DA:2035,0 -DA:2036,0 -DA:2042,0 -DA:2047,0 -DA:2050,0 -DA:2051,0 -DA:2052,0 -DA:2055,0 -DA:2056,0 -DA:2057,0 -DA:2065,0 -DA:2066,0 -DA:2068,0 -DA:2089,0 -DA:2100,0 -DA:2104,0 -DA:2105,0 -DA:2106,0 -DA:2108,0 -DA:2109,0 -DA:2116,0 -DA:2120,0 -DA:2124,0 -DA:2179,1 -DA:2180,1 -DA:2188,1 -DA:2201,1 -DA:2202,1 -DA:2207,0 -DA:2211,0 -DA:2213,0 -DA:2223,0 -DA:2224,0 -DA:2225,0 -DA:2226,0 -DA:2227,0 -DA:2228,0 -DA:2234,0 -DA:2247,0 -DA:2248,0 -DA:2253,0 -DA:2265,1 -DA:2267,1 -DA:2268,0 -DA:2270,0 -DA:2274,0 -DA:2284,0 -DA:2287,0 -DA:2288,0 -DA:2289,0 -DA:2290,0 -DA:2291,0 -DA:2292,0 -DA:2294,0 -DA:2298,0 -DA:2300,0 -DA:2304,0 -DA:2316,0 -DA:2321,0 -DA:2325,0 -DA:2327,0 -DA:2337,0 -DA:2338,0 -DA:2339,0 -DA:2340,0 -DA:2341,0 -DA:2342,0 -DA:2359,0 -DA:2363,0 -DA:2365,0 -DA:2366,0 -DA:2370,0 -DA:2371,0 -DA:2376,0 -DA:2377,0 -DA:2379,0 -DA:2387,1 -DA:2391,2 -DA:2396,1 -DA:2398,0 -DA:2399,0 -DA:2400,0 -DA:2401,0 -DA:2402,0 -DA:2403,0 -DA:2404,0 -DA:2409,3 -DA:2410,3 -DA:2411,1 -DA:2412,1 -DA:2413,1 -DA:2414,1 -DA:2415,1 -DA:2416,2 -DA:2420,0 -DA:2421,0 -DA:2423,0 -DA:2425,0 -DA:2430,0 -DA:2431,0 -DA:2434,0 -DA:2435,0 -DA:2436,0 -DA:2439,0 -DA:2440,0 -DA:2441,0 -DA:2447,0 -DA:2449,0 -DA:2450,0 -DA:2451,0 -DA:2475,0 -DA:2486,0 -DA:2490,0 -DA:2491,0 -DA:2492,0 -DA:2493,0 -DA:2494,0 -DA:2495,0 -DA:2501,0 -DA:2505,0 -DA:2509,0 -DA:2513,1 -DA:2518,1 -DA:2519,1 -DA:2520,1 -DA:2521,1 -DA:2531,0 -DA:2532,0 -DA:2533,0 -DA:2534,0 -DA:2535,0 -DA:2536,0 -DA:2540,0 -DA:2542,0 -DA:2543,0 -DA:2544,0 -DA:2550,0 -DA:2555,0 -DA:2558,0 -DA:2559,0 -DA:2560,0 -DA:2563,0 -DA:2564,0 -DA:2565,0 -DA:2573,0 -DA:2574,0 -DA:2576,0 -DA:2597,0 -DA:2608,0 -DA:2612,0 -DA:2613,0 -DA:2614,0 -DA:2616,0 -DA:2617,0 -DA:2624,0 -DA:2628,0 -DA:2632,0 -DA:2687,1 -DA:2689,1 -DA:2697,1 -DA:2710,1 -DA:2711,1 -DA:2716,0 -DA:2720,0 -DA:2722,0 -DA:2732,0 -DA:2733,0 -DA:2734,0 -DA:2735,0 -DA:2736,0 -DA:2737,0 -DA:2743,0 -DA:2756,0 -DA:2757,0 -DA:2762,0 -DA:2774,1 -DA:2776,1 -DA:2777,0 -DA:2779,0 -DA:2783,0 -DA:2793,0 -DA:2796,0 -DA:2797,0 -DA:2798,0 -DA:2799,0 -DA:2800,0 -DA:2801,0 -DA:2803,0 -DA:2807,0 -DA:2809,0 -DA:2813,0 -DA:2825,0 -DA:2830,0 -DA:2834,0 -DA:2836,0 -DA:2846,0 -DA:2847,0 -DA:2848,0 -DA:2849,0 -DA:2850,0 -DA:2851,0 -DA:2868,0 -DA:2872,0 -DA:2874,0 -DA:2875,0 -DA:2879,0 -DA:2880,0 -DA:2885,0 -DA:2886,0 -DA:2888,0 -DA:2896,1 -DA:2900,2 -DA:2905,1 -DA:2907,0 -DA:2908,0 -DA:2909,0 -DA:2910,0 -DA:2911,0 -DA:2912,0 -DA:2913,0 -DA:2918,3 -DA:2919,3 -DA:2920,1 -DA:2921,1 -DA:2922,1 -DA:2923,1 -DA:2924,1 -DA:2925,1 -DA:2929,0 -DA:2930,0 -DA:2932,0 -DA:2934,0 -DA:2939,0 -DA:2940,0 -DA:2943,0 -DA:2944,0 -DA:2945,0 -DA:2948,0 -DA:2949,0 -DA:2950,0 -DA:2956,0 -DA:2958,0 -DA:2959,0 -DA:2960,0 -DA:2984,0 -DA:2995,0 -DA:2999,0 -DA:3000,0 -DA:3001,0 -DA:3002,0 -DA:3003,0 -DA:3009,0 -DA:3013,0 -DA:3017,0 -DA:3021,1 -DA:3026,1 -DA:3027,1 -DA:3028,1 -DA:3029,1 -DA:3039,0 -DA:3040,0 -DA:3041,0 -DA:3042,0 -DA:3043,0 -DA:3044,0 -DA:3048,0 -DA:3050,0 -DA:3051,0 -DA:3052,0 -DA:3058,0 -DA:3063,0 -DA:3066,0 -DA:3067,0 -DA:3068,0 -DA:3071,0 -DA:3072,0 -DA:3073,0 -DA:3081,0 -DA:3082,0 -DA:3084,0 -DA:3105,0 -DA:3116,0 -DA:3120,0 -DA:3121,0 -DA:3122,0 -DA:3124,0 -DA:3125,0 -DA:3132,0 -DA:3136,0 -DA:3140,0 -DA:3195,1 -DA:3197,1 -DA:3205,1 -DA:3218,1 -DA:3219,1 -DA:3224,0 -DA:3228,0 -DA:3230,0 -DA:3240,0 -DA:3241,0 -DA:3242,0 -DA:3243,0 -DA:3244,0 -DA:3245,0 -DA:3251,0 -DA:3264,0 -DA:3265,0 -DA:3270,0 -DA:3282,1 -DA:3284,1 -DA:3285,0 -DA:3287,0 -DA:3291,0 -DA:3301,0 -DA:3304,0 -DA:3305,0 -DA:3306,0 -DA:3307,0 -DA:3308,0 -DA:3309,0 -DA:3311,0 -DA:3315,0 -DA:3317,0 -DA:3321,0 -DA:3333,0 -DA:3338,0 -DA:3342,0 -DA:3344,0 -DA:3354,0 -DA:3355,0 -DA:3356,0 -DA:3357,0 -DA:3358,0 -DA:3359,0 -DA:3376,0 -DA:3380,0 -DA:3382,0 -DA:3383,0 -DA:3387,0 -DA:3388,0 -DA:3393,0 -DA:3394,0 -DA:3396,0 -LF:890 -LH:149 -end_of_record From bc823c5786e35594d11fe3623c26118ba43fe1c1 Mon Sep 17 00:00:00 2001 From: Jonatan Ziegler Date: Tue, 18 Jul 2023 18:16:17 +0200 Subject: [PATCH 084/184] removed unnecessary constant --- app/lib/model/api_server/GraphQlServerAccess.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index b12cdeee..7e9850fe 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -235,8 +235,6 @@ class GraphQlServerAccess implements IServerAccess { return mealPlan; } - static const defaultUuid = "00000000-0000-0000-0000-000000000000"; - @override Future getDefaultCanteen() async { final result = await _client From 52eb1d22c8a383cf13e2b2e722fe5bf84e2361be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 18 Jul 2023 20:42:02 +0200 Subject: [PATCH 085/184] strings in localization format --- app/assets/locales/de/snackbar.json | 11 +++++++++++ .../view_model/logic/image/ImageAccess.dart | 18 ++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 app/assets/locales/de/snackbar.json diff --git a/app/assets/locales/de/snackbar.json b/app/assets/locales/de/snackbar.json new file mode 100644 index 00000000..f61969c9 --- /dev/null +++ b/app/assets/locales/de/snackbar.json @@ -0,0 +1,11 @@ +{ + "updateRatingSuccess": "Die Bewertung wurde erfolgreich aktualisiert.", + "updateRatingError": "Die Bewertung konnte nicht übernommen werden. Bitte versuchen Sie es später erneut.", + "refreshMealPlanError": "Der Speiseplan konnte nicht aktualisiert werden. Bitte versuchen Sie es später erneut.", + + "voteError": "Die Bewertung konnte nicht übernommen werden. Bitte versuchen Sie es später erneut.", + "linkImageSuccess": "Das Bild wurde erfolgreich verlinkt.", + "linkImageError": "Das Bild konnte nicht verlinkt werden. Bitte versuchen Sie es später erneut.", + "reportImageSuccess": "Das Bild wurde erfolgreich gemeldet.", + "reportImageError": "Das Bild konnte nicht gemeldet werden. Bitte versuchen Sie es später erneut." +} \ No newline at end of file diff --git a/app/lib/view_model/logic/image/ImageAccess.dart b/app/lib/view_model/logic/image/ImageAccess.dart index 94fba7d2..bc28ef50 100644 --- a/app/lib/view_model/logic/image/ImageAccess.dart +++ b/app/lib/view_model/logic/image/ImageAccess.dart @@ -16,7 +16,7 @@ class ImageAccess extends ChangeNotifier implements IImageAccess { final result = await _api.deleteDownvote(image); if (!result) { - return "error"; + return "snackbar.voteError"; } image.deleteRating(); @@ -29,7 +29,7 @@ class ImageAccess extends ChangeNotifier implements IImageAccess { final result = await _api.deleteUpvote(image); if (!result) { - return "error"; + return "snackbar.voteError"; } image.deleteRating(); @@ -42,7 +42,7 @@ class ImageAccess extends ChangeNotifier implements IImageAccess { final result = await _api.downvoteImage(image); if (!result) { - return "error"; + return "snackbar.voteError"; } image.downvote(); @@ -55,13 +55,11 @@ class ImageAccess extends ChangeNotifier implements IImageAccess { final result = await _api.linkImage(url, meal); if (!result) { - return "error"; + return "snackbar.linkImageError"; } - // todo aktualisieren? - notifyListeners(); - return "success"; + return "snackbar.linkImageSuccess"; } @override @@ -69,13 +67,13 @@ class ImageAccess extends ChangeNotifier implements IImageAccess { final result = await _api.reportImage(image, reportReason); if (!result) { - return "error"; + return "snackbar.reportImageError"; } // todo wie wird es nicht mehr angezeigt notifyListeners(); - return "success"; + return "snackbar.reportImageSuccess"; } @override @@ -83,7 +81,7 @@ class ImageAccess extends ChangeNotifier implements IImageAccess { final result = await _api.upvoteImage(image); if (!result) { - return "error"; + return "snackbar.voteError"; } image.upvote(); From 66f6eb34dd700da20777f5f3d48505ce756413eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 18 Jul 2023 20:43:01 +0200 Subject: [PATCH 086/184] add modification in meal object if favorites get removed or added --- app/lib/view_model/logic/favorite/FavoriteMealAccess.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart index 1c705417..65bd8ab2 100644 --- a/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart +++ b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart @@ -30,6 +30,7 @@ class FavoriteMealAccess extends ChangeNotifier implements IFavoriteMealAccess { await _database.addFavorite(meal); _favorites.add(meal); + meal.setFavorite(); notifyListeners(); } @@ -55,6 +56,7 @@ class FavoriteMealAccess extends ChangeNotifier implements IFavoriteMealAccess { await _database.deleteFavorite(meal); _favorites.removeWhere((element) => element.id == meal.id); + meal.deleteFavorite(); notifyListeners(); } From f09545329ee5065312a7d30b4de2ba1098594aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 18 Jul 2023 21:03:35 +0200 Subject: [PATCH 087/184] something for later --- .../repository/data_classes/filter/FilterPreferences.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart index f7444b27..e2ad6524 100644 --- a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart +++ b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart @@ -144,6 +144,7 @@ class FilterPreferences { _frequency = [Frequency.newMeal]; } + // todo wollen wir hier auch neue Gerichte? /// only rare meals are to be shown setRareFrequency() { _frequency = [Frequency.rare]; From b315094fab024dbdb7c802cd604e9ee9dd65ca8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 18 Jul 2023 21:15:07 +0200 Subject: [PATCH 088/184] testing --- .../logic/meal/CombinedMealPlanAccess.dart | 50 +- app/test/view-model/MealPlanAccessTest.dart | 563 ++++++++++++++++-- 2 files changed, 553 insertions(+), 60 deletions(-) diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index d4f7ae05..824230c5 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -15,8 +15,6 @@ import 'package:flutter/material.dart'; import '../../repository/data_classes/meal/Side.dart'; -// todo wann ist das Zeug wirklich zu? einfach rauslöschen? -// todo string for snack-bar class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { final ILocalStorage _preferences; final IServerAccess _api; @@ -60,7 +58,9 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { canteen = await _api.getDefaultCanteen(); // save canteen id in local storage - _preferences.setCanteen(canteen!.id); + if (canteen != null) { + _preferences.setCanteen(canteen.id); + } } else { // get canteen from database // updates from server are already stored or there is no connection @@ -93,11 +93,13 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { Future changeCanteen(Canteen canteen) async { await _doneInitialization; _activeCanteen = canteen; + _preferences.setCanteen(_activeCanteen.id); // requests and stores the new meal plan // filters meal plan // notifies listener await _setNewMealPlan(); + notifyListeners(); } @override @@ -109,6 +111,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { // filters meal plan // notifies listener await _setNewMealPlan(); + notifyListeners(); } @override @@ -117,6 +120,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { await _doneInitialization; _filter = filterPreferences; + await _preferences.setFilterPreferences(_filter); await _filterMealPlans(); notifyListeners(); @@ -158,24 +162,33 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { Future, MealPlanException>> getMealPlan() async { await _doneInitialization; + // no data for date + if (_noDataYet) { + return Future.value(Failure(NoDataException("no data to date"))); + } + // no connection to server and no data if (_mealPlans.isEmpty) { return Future.value(Failure(NoConnectionException("no connection"))); } - // everything is filtered - if (_filteredMealPlan.isEmpty) { - return Future.value(Failure(FilteredMealException("all filtered"))); + // canteen is closed + bool closed = true; + for (final mealPlan in _mealPlans) { + if (!mealPlan.isClosed) { + closed = false; + break; + } } - // canteen is closed - if (_mealPlans.first.isClosed) { + if (closed) { return Future.value(Failure(ClosedCanteenException("canteen closed"))); } - // no data for date - if (_noDataYet) { - return Future.value(Failure(NoDataException("no data to date"))); + + // everything is filtered + if (_filteredMealPlan.isEmpty) { + return Future.value(Failure(FilteredMealException("all filtered"))); } // success @@ -189,7 +202,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { final mealPlan = await _getMealPlanFromServer(); if (_mealPlans.isEmpty) { - return "error"; + return "snackbar.refreshMealPlanError"; } _mealPlans = mealPlan; @@ -206,12 +219,12 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { final result = await _api.updateMealRating(rating, meal); if (!result) { - return "error"; + return "snackbar.updateRatingError"; } _changeRatingOfMeal(meal, rating); notifyListeners(); - return "success"; + return "snackbar.updateRatingSuccess"; } @override @@ -240,7 +253,9 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { await _doneInitialization; _filter = FilterPreferences(); + await _preferences.setFilterPreferences(_filter); await _filterMealPlans(); + notifyListeners(); } void _changeRatingOfMeal(Meal changedMeal, int rating) { @@ -262,7 +277,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { Future _filterMealPlans() async { _filteredMealPlan = []; // any kind of failure so no data is present - if (_mealPlans.isEmpty || _noDataYet || _mealPlans.first.isClosed) { + if (_mealPlans.isEmpty || _noDataYet) { return; } @@ -283,7 +298,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { } } - if (mealPlan.meals.isNotEmpty) { + if (filteredMealPlan.meals.isNotEmpty) { newFilteredMealPlans.add(filteredMealPlan); } } @@ -362,8 +377,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { } // check onlyFavorite - if (_filter.onlyFavorite && - !(await _database.getFavorites()).map((e) => e.id).contains(meal.id)) { + if (_filter.onlyFavorite && !meal.isFavorite) { return false; } diff --git a/app/test/view-model/MealPlanAccessTest.dart b/app/test/view-model/MealPlanAccessTest.dart index 33e12319..0f9efb70 100644 --- a/app/test/view-model/MealPlanAccessTest.dart +++ b/app/test/view-model/MealPlanAccessTest.dart @@ -1,9 +1,11 @@ import 'package:app/view_model/logic/meal/CombinedMealPlanAccess.dart'; import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; +import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/meal/Price.dart'; +import 'package:app/view_model/repository/data_classes/meal/Side.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; @@ -17,47 +19,106 @@ import '../model/mocks/ApiMock.dart'; import '../model/mocks/DatabaseMock.dart'; import '../model/mocks/LocalStorageMock.dart'; +class FilterPreferencesFake extends Fake implements FilterPreferences {} + void main() { final localStorage = LocalStorageMock(); final api = ApiMock(); final database = DatabaseMock(); - late CombinedMealPlanAccess mealPlan; + late CombinedMealPlanAccess mealPlanAccess; + FilterPreferences filter = FilterPreferences(); const String canteenID = "id"; final Canteen canteen = Canteen(id: canteenID, name: "name"); + final List sides = [ + Side( + id: "01", + name: "Side vegan", + foodType: FoodType.vegan, + price: Price(student: 123, employee: 234, pupil: 345, guest: 356), + allergens: [], + additives: []), + Side( + id: "02", + name: "Side vegetarian", + foodType: FoodType.vegetarian, + price: Price(student: 333, employee: 453, pupil: 345, guest: 356), + allergens: [Allergen.lu, Allergen.ka, Allergen.kr], + additives: []), + Side( + id: "03", + name: "Side fish", + foodType: FoodType.fish, + price: Price(student: 143, employee: 654, pupil: 345, guest: 356), + allergens: [Allergen.lu, Allergen.er, Allergen.kr], + additives: []), + Side( + id: "04", + name: "Side beef", + foodType: FoodType.beef, + price: Price(student: 123, employee: 123, pupil: 345, guest: 356), + allergens: [Allergen.sn, Allergen.ka, Allergen.kr], + additives: []), + ]; + final List meals = [ Meal( id: "1", name: "vegan Meal", foodType: FoodType.vegan, relativeFrequency: Frequency.newMeal, - price: Price(student: 200, employee: 300, pupil: 400, guest: 500)), + price: Price(student: 200, employee: 300, pupil: 400, guest: 500), + allergens: [Allergen.lu, Allergen.ka], + additives: [], + sides: [sides[0]], + averageRating: 5, + isFavorite: true), Meal( id: "42", name: "vegetarian Meal", foodType: FoodType.vegetarian, - relativeFrequency: Frequency.newMeal, - price: Price(student: 200, employee: 300, pupil: 400, guest: 500)), + relativeFrequency: Frequency.normal, + price: Price(student: 200, employee: 300, pupil: 400, guest: 500), + allergens: [Allergen.lu, Allergen.sn, Allergen.kr], + additives: [], + sides: [sides[1], sides[0]], + averageRating: 4, + isFavorite: true), Meal( id: "12", name: "fishi Meal", foodType: FoodType.fish, - relativeFrequency: Frequency.newMeal, - price: Price(student: 200, employee: 300, pupil: 400, guest: 500)), + relativeFrequency: Frequency.rare, + price: Price(student: 200, employee: 300, pupil: 400, guest: 500), + allergens: [Allergen.sn, Allergen.er, Allergen.kr], + additives: [], + sides: [sides[2], sides[0], sides[1]], + averageRating: 3, + isFavorite: false), Meal( id: "34", name: "meal with beef", foodType: FoodType.beef, - relativeFrequency: Frequency.newMeal, - price: Price(student: 100, employee: 120, pupil: 130, guest: 140)), + relativeFrequency: Frequency.rare, + price: Price(student: 100, employee: 120, pupil: 130, guest: 140), + allergens: [Allergen.sn, Allergen.ka, Allergen.kr], + additives: [], + sides: [sides[0], sides[1], sides[2], sides[3]], + averageRating: 2, + isFavorite: true), Meal( id: "54", name: "pig", foodType: FoodType.pork, - relativeFrequency: Frequency.newMeal, - price: Price(student: 123, employee: 456, pupil: 345, guest: 789)), + relativeFrequency: Frequency.normal, + price: Price(student: 123, employee: 456, pupil: 345, guest: 789), + allergens: [Allergen.sn, Allergen.ka, Allergen.kr], + additives: [], + sides: [sides[0], sides[1], sides[2], sides[3]], + averageRating: 1, + isFavorite: false), ]; final List lines = [ @@ -98,11 +159,16 @@ void main() { meals: [meals[3], meals[4]]), ]; + setUpAll(() { + registerFallbackValue(FilterPreferencesFake()); + }); + setUp(() { when(() => localStorage.getFilterPreferences()) .thenAnswer((_) async => null); when(() => localStorage.getCanteen()).thenAnswer((_) async => canteenID); - when(() => localStorage.getPriceCategory()).thenAnswer((_) async => PriceCategory.student); + when(() => localStorage.getPriceCategory()) + .thenAnswer((_) async => PriceCategory.student); when(() => api.updateAll()) .thenAnswer((_) async => Failure(NoConnectionException("error"))); @@ -113,21 +179,21 @@ void main() { when(() => database.getMealPlan(any(), canteen)) .thenAnswer((_) async => Success(mealplans)); when(() => database.getFavorites()).thenAnswer((_) async => []); - - mealPlan = CombinedMealPlanAccess(localStorage, api, database); + + mealPlanAccess = CombinedMealPlanAccess(localStorage, api, database); }); - + group("initialization", () { test("simple initialization", () async { - expect(await mealPlan.getCanteen(), canteen); - expect(await mealPlan.getFilterPreferences(), FilterPreferences()); + expect(await mealPlanAccess.getCanteen(), canteen); + expect(await mealPlanAccess.getFilterPreferences(), FilterPreferences()); - final date = await mealPlan.getDate(); + final date = await mealPlanAccess.getDate(); expect(date.year, DateTime.now().year); expect(date.month, DateTime.now().month); expect(date.day, DateTime.now().day); - final returnedMealPlan = switch (await mealPlan.getMealPlan()) { + final returnedMealPlan = switch (await mealPlanAccess.getMealPlan()) { Success(value: final value) => value, Failure(exception: _) => [] }; @@ -138,35 +204,448 @@ void main() { }); // todo initialization with other values - }); - group("change meal plan", () { - // todo - // change date - // change canteen + group("filter meals", () { + when(() => localStorage.setFilterPreferences(filter)) + .thenAnswer((_) async {}); + when(() => database.getFavorites()) + .thenAnswer((_) async => [meals[0], meals[1], meals[3]]); + when(() => localStorage.getPriceCategory()) + .thenAnswer((_) async => PriceCategory.student); + + group("allergens", () { + test("change allergens er", () async { + filter.removeAllergen(Allergen.er); + await mealPlanAccess.changeFilterPreferences(filter); - }); + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; - group("filter meals", () { - // todo write test cases - // change allergens - // change frequency - // change favorites - // change price - // change rating - // change category - - // filter sides - // reset filters + // first meal plan + expect(returnedMealPlan[0].meals[0], meals[0]); + expect(returnedMealPlan[0].meals[1], meals[1]); + // sides first meal plan + expect(returnedMealPlan[0].meals[0].sides?.length, 1); + expect(returnedMealPlan[0].meals[1].sides?.length, 2); + expect(returnedMealPlan[0].meals[1].sides?[0], sides[1]); + expect(returnedMealPlan[0].meals[1].sides?[1], sides[0]); + + // second meal plan of [mealplans] + // -> should be emplty + expect(returnedMealPlan.length, 2); + + // third meal plan + expect(returnedMealPlan[1].meals[0], meals[3]); + expect(returnedMealPlan[1].meals[1], meals[4]); + // sides third meal plan + expect(returnedMealPlan[1].meals[0].sides?.length, 3); + expect(returnedMealPlan[1].meals[1].sides?.length, 3); + expect(returnedMealPlan[1].meals[0].sides?[0], sides[0]); + expect(returnedMealPlan[1].meals[0].sides?[1], sides[1]); + expect(returnedMealPlan[1].meals[0].sides?[2], sides[3]); + }); + + test("change allergens er and sn", () async { + filter.removeAllergen(Allergen.er); + filter.removeAllergen(Allergen.sn); + await mealPlanAccess.changeFilterPreferences(filter); + + final returnedMealPlan = switch (await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + // first meal plan + expect(returnedMealPlan[0].meals[0], meals[0]); + // sides first meal plan + expect(returnedMealPlan[0].meals[0].sides?.length, 1); + expect(returnedMealPlan[0].meals[0].sides?[0], sides[0]); + + // second and third meal plan of [mealplans] + // -> should be emplty + expect(returnedMealPlan.length, 1); + }); + + test("change allergens er, sn and kr", () async { + filter.removeAllergen(Allergen.kr); + await mealPlanAccess.changeFilterPreferences(filter); + + final returnedMealPlan = switch (await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + // first meal plan + expect(returnedMealPlan[0].meals[0], meals[0]); + // sides first meal plan + expect(returnedMealPlan[0].meals[0].sides?.length, 1); + expect(returnedMealPlan[0].meals[0].sides?[0], sides[0]); + + // second and third meal plan of [mealplans] + // -> should be emplty + expect(returnedMealPlan.length, 1); + }); + + test("remove filter allergens", () async { + filter.addAllergen(Allergen.er); + filter.addAllergen(Allergen.sn); + filter.addAllergen(Allergen.kr); + + await mealPlanAccess.changeFilterPreferences(filter); + + final returnedMealPlan = switch (await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan, mealplans); + }); + }); + + group("frequency", () { + test("only new", () async { + filter.setNewFrequency(); + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan.length, 1); + expect(returnedMealPlan[0].meals.length, 1); + expect(returnedMealPlan[0].meals[0], meals[0]); + }); + + test("only rare", () async { + filter.setRareFrequency(); + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan.length, 2); + expect(returnedMealPlan[0].meals.length, 1); + expect(returnedMealPlan[0].meals[0], meals[2]); + expect(returnedMealPlan[1].meals.length, 1); + expect(returnedMealPlan[1].meals[0], meals[3]); + }); + + test("all", () async { + filter.setAllFrequencies(); + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan, mealplans); + }); + }); + + group("favorites", () { + test("only favorites", () async { + filter.onlyFavorite = true; + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + // first meal plan + expect(returnedMealPlan[0].meals[0], meals[0]); + expect(returnedMealPlan[0].meals[1], meals[1]); + // sides first meal plan + expect(returnedMealPlan[0].meals[0].sides?.length, 1); + expect(returnedMealPlan[0].meals[1].sides?.length, 2); + + // second meal plan of [mealplans] + // -> should be emplty + expect(returnedMealPlan.length, 2); + + // third meal plan + expect(returnedMealPlan[1].meals.length, 1); + expect(returnedMealPlan[1].meals[0], meals[3]); + // sides third meal plan + expect(returnedMealPlan[1].meals[0].sides?.length, 4); + }); + + test("all", () async { + filter.onlyFavorite = false; + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan, mealplans); + }); + }); + + group("rating", () { + test("set rating limit", () async { + filter.rating = 3; + + when(() => localStorage.setFilterPreferences(filter)) + .thenAnswer((_) async {}); + + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan.length, 2); + expect(returnedMealPlan[0].meals.length, 2); + expect(returnedMealPlan[0].meals[0], meals[0]); + expect(returnedMealPlan[0].meals[1], meals[1]); + expect(returnedMealPlan[1].meals.length, 1); + expect(returnedMealPlan[1].meals[0], meals[2]); + // sides + expect(returnedMealPlan[0].meals[0].sides?.length, 1); + expect(returnedMealPlan[0].meals[1].sides?.length, 2); + expect(returnedMealPlan[1].meals[0].sides?.length, 3); + }); + + test("no rating limit", () async { + filter = FilterPreferences(); + + when(() => localStorage.setFilterPreferences(filter)) + .thenAnswer((_) async {}); + + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan, mealplans); + }); + }); + + group("food types", () { + test("vegan", () async { + filter.setCategoriesVegan(); + + when(() => localStorage.setFilterPreferences(filter)) + .thenAnswer((_) async {}); + + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch (await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan.length, 1); + expect(returnedMealPlan[0].meals.length, 1); + expect(returnedMealPlan[0].meals[0], meals[0]); + // sides + expect(returnedMealPlan[0].meals[0].sides?.length, 1); + }); + + test("vegetarian", () async { + filter.setCategoriesVegetarian(); + + when(() => localStorage.setFilterPreferences(filter)) + .thenAnswer((_) async {}); + + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch (await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan.length, 1); + expect(returnedMealPlan[0].meals.length, 2); + expect(returnedMealPlan[0].meals[0], meals[0]); + expect(returnedMealPlan[0].meals[1], meals[1]); + // sides + expect(returnedMealPlan[0].meals[0].sides?.length, 1); + expect(returnedMealPlan[0].meals[1].sides?.length, 2); + }); + + test("all", () async { + filter.setAllCategories(); + + when(() => localStorage.setFilterPreferences(filter)) + .thenAnswer((_) async {}); + + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan, mealplans); + }); + }); + + group("price", () { + test("price limit student", () async { + filter.price = 130; + + when(() => localStorage.setFilterPreferences(filter)) + .thenAnswer((_) async {}); + + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan.length, 1); + expect(returnedMealPlan[0].meals.length, 2); + expect(returnedMealPlan[0].meals[0], meals[3]); + expect(returnedMealPlan[0].meals[1], meals[4]); + // sides + expect(returnedMealPlan[0].meals[0].sides?.length, 2); + expect(returnedMealPlan[0].meals[1].sides?.length, 2); + expect(returnedMealPlan[0].meals[0].sides?[0], sides[0]); + expect(returnedMealPlan[0].meals[0].sides?[1], sides[3]); + expect(returnedMealPlan[0].meals[1].sides?[0], sides[0]); + expect(returnedMealPlan[0].meals[1].sides?[1], sides[3]); + }); + + test("price limit employee", () async { + when(() => localStorage.getPriceCategory()) + .thenAnswer((_) async => PriceCategory.employee); + + filter.price = 130; + + when(() => localStorage.setFilterPreferences(filter)) + .thenAnswer((_) async {}); + + await mealPlanAccess.changeFilterPreferences(filter); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan.length, 1); + expect(returnedMealPlan[0].meals.length, 1); + expect(returnedMealPlan[0].meals[0], meals[3]); + // sides + expect(returnedMealPlan[0].meals[0].sides?.length, 1); + expect(returnedMealPlan[0].meals[0].sides?[0], sides[3]); + }); + }); + + test("reset filter preferences", () async { + filter = FilterPreferences(); + when(() => localStorage.setFilterPreferences(filter)) + .thenAnswer((_) async {}); + + await mealPlanAccess.resetFilterPreferences(); + expect(await mealPlanAccess.getFilterPreferences(), filter); + }); }); group("edge cases", () { - // todo - // just first line is closed, other are open - // all lines closed - // no Data yet - // all filterd - // no connection + test("closed canteen", () async { + when(() => database.getMealPlan(any(), canteen)) + .thenAnswer((_) async => Success(closedCanteen)); + + await mealPlanAccess.changeDate(DateTime.now()); + + final result = switch (await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: final exception) => exception + }; + + expect(result is ClosedCanteenException, isTrue); + }); + + test("first line closed", () async { + when(() => database.getMealPlan(any(), canteen)) + .thenAnswer((_) async => Success(closedLine)); + + await mealPlanAccess.changeDate(DateTime.now()); + + final List result = switch (await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(result.isNotEmpty, isTrue); + expect(result.length, 1); + }); + + test("no data yet", () async { + when(() => database.getMealPlan(any(), canteen)) + .thenAnswer((_) async => Failure(NoDataException("error"))); + when(() => api.updateCanteen(canteen, any())) + .thenAnswer((_) async => Failure(NoDataException("error"))); + + await mealPlanAccess.changeDate(DateTime.now()); + + final result = switch (await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: final exception) => exception + }; + + expect(result is NoDataException, isTrue); + }); + + test("no connection", () async { + when(() => database.getMealPlan(any(), canteen)) + .thenAnswer((_) async => Failure(NoConnectionException("error"))); + + await mealPlanAccess.changeDate(DateTime.now()); + + final result = switch (await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: final exception) => exception + }; + + expect(result is NoConnectionException, isTrue); + }); + + test("all filtered", () async { + when(() => database.getMealPlan(any(), canteen)) + .thenAnswer((_) async => Success(mealplans)); + + await mealPlanAccess.changeDate(DateTime.now()); + + filter.setCategoriesVegan(); + filter.removeAllergen(Allergen.lu); + + when(() => localStorage.setFilterPreferences(filter)) + .thenAnswer((_) async {}); + + await mealPlanAccess.changeFilterPreferences(filter); + + final result = switch (await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: final exception) => exception + }; + + expect(result is FilteredMealException, isTrue); + }); }); -} \ No newline at end of file +} From 74dd3ebec62cd2c05d37c8792c84c498298bb3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 18 Jul 2023 21:16:40 +0200 Subject: [PATCH 089/184] remove unneeded todo --- app/test/view-model/MealPlanAccessTest.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/test/view-model/MealPlanAccessTest.dart b/app/test/view-model/MealPlanAccessTest.dart index 0f9efb70..231f10d2 100644 --- a/app/test/view-model/MealPlanAccessTest.dart +++ b/app/test/view-model/MealPlanAccessTest.dart @@ -202,8 +202,6 @@ void main() { expect(returnedMealPlan.contains(mealplan), true); } }); - - // todo initialization with other values }); group("filter meals", () { From 23aa6715b501e913c42f795ce28498e98b53ea7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 18 Jul 2023 21:55:04 +0200 Subject: [PATCH 090/184] add method for switching to meal plan view --- .../logic/meal/CombinedMealPlanAccess.dart | 45 ++++++++++++++++--- .../view_model/logic/meal/IMealAccess.dart | 6 ++- app/test/view-model/MealPlanAccessTest.dart | 22 +++++---- 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 824230c5..9afd4a7d 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -27,6 +27,8 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { late FilterPreferences _filter; late bool _noDataYet = false; + late PriceCategory _priceCategory; + // waits until _init() is finished initializing late Future _doneInitialization; @@ -37,6 +39,8 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { Future _init() async { _displayedDate = DateTime.timestamp(); _filter = await _preferences.getFilterPreferences() ?? FilterPreferences(); + _priceCategory = + await _preferences.getPriceCategory() ?? PriceCategory.student; // get meal plans form server List mealPlans = switch (await _api.updateAll()) { @@ -185,7 +189,6 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { return Future.value(Failure(ClosedCanteenException("canteen closed"))); } - // everything is filtered if (_filteredMealPlan.isEmpty) { return Future.value(Failure(FilteredMealException("all filtered"))); @@ -258,6 +261,24 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { notifyListeners(); } + @override + Future switchToMealPlanView() async { + bool changed = await _updateFavorites(); + final category = await _preferences.getPriceCategory(); + + // check if changed + if (category != null && category != _priceCategory) { + _priceCategory = category; + changed = true; + } + + // refresh if changed + if (changed) { + await _filterMealPlans(); + notifyListeners(); + } + } + void _changeRatingOfMeal(Meal changedMeal, int rating) { for (final mealPlan in _mealPlans) { // check if right meal plan @@ -332,8 +353,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { } // check price - final price = side.price.getPrice( - await _preferences.getPriceCategory() ?? PriceCategory.student); + final price = side.price.getPrice(_priceCategory); if (price > _filter.price) { return false; } @@ -357,8 +377,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { } } - final price = meal.price.getPrice( - await _preferences.getPriceCategory() ?? PriceCategory.student); + final price = meal.price.getPrice(_priceCategory); // check price if (_filter.price < price) { @@ -468,4 +487,20 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { ]; } } + + Future _updateFavorites() async { + final favorites = await _database.getFavorites(); + bool changed = false; + + for (final mealPlan in _mealPlans) { + for (final meal in mealPlan.meals) { + if (favorites.map((favorite) => favorite.id).contains(meal.id)) { + meal.setFavorite(); + changed = true; + } + } + } + + return changed; + } } diff --git a/app/lib/view_model/logic/meal/IMealAccess.dart b/app/lib/view_model/logic/meal/IMealAccess.dart index d1185dd1..06366a9c 100644 --- a/app/lib/view_model/logic/meal/IMealAccess.dart +++ b/app/lib/view_model/logic/meal/IMealAccess.dart @@ -67,4 +67,8 @@ abstract class IMealAccess { /// @param date The new date /// @return The result of the update Future changeDate(DateTime date); -} \ No newline at end of file + + /// This method checks if settings or favorites are changed since the last time the mealplan was displayed. + /// If they were changed it corrects the displayed data if needed. + Future switchToMealPlanView(); +} diff --git a/app/test/view-model/MealPlanAccessTest.dart b/app/test/view-model/MealPlanAccessTest.dart index 231f10d2..dce6cb13 100644 --- a/app/test/view-model/MealPlanAccessTest.dart +++ b/app/test/view-model/MealPlanAccessTest.dart @@ -159,6 +159,8 @@ void main() { meals: [meals[3], meals[4]]), ]; + final List favorites = [meals[0], meals[1], meals[3]]; + setUpAll(() { registerFallbackValue(FilterPreferencesFake()); }); @@ -207,8 +209,7 @@ void main() { group("filter meals", () { when(() => localStorage.setFilterPreferences(filter)) .thenAnswer((_) async {}); - when(() => database.getFavorites()) - .thenAnswer((_) async => [meals[0], meals[1], meals[3]]); + when(() => database.getFavorites()).thenAnswer((_) async => favorites); when(() => localStorage.getPriceCategory()) .thenAnswer((_) async => PriceCategory.student); @@ -404,7 +405,7 @@ void main() { await mealPlanAccess.changeFilterPreferences(filter); final List returnedMealPlan = switch ( - await mealPlanAccess.getMealPlan()) { + await mealPlanAccess.getMealPlan()) { Success(value: final value) => value, Failure(exception: _) => [] }; @@ -430,7 +431,7 @@ void main() { await mealPlanAccess.changeFilterPreferences(filter); final List returnedMealPlan = switch ( - await mealPlanAccess.getMealPlan()) { + await mealPlanAccess.getMealPlan()) { Success(value: final value) => value, Failure(exception: _) => [] }; @@ -448,7 +449,8 @@ void main() { await mealPlanAccess.changeFilterPreferences(filter); - final List returnedMealPlan = switch (await mealPlanAccess.getMealPlan()) { + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { Success(value: final value) => value, Failure(exception: _) => [] }; @@ -468,7 +470,8 @@ void main() { await mealPlanAccess.changeFilterPreferences(filter); - final List returnedMealPlan = switch (await mealPlanAccess.getMealPlan()) { + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { Success(value: final value) => value, Failure(exception: _) => [] }; @@ -491,7 +494,7 @@ void main() { await mealPlanAccess.changeFilterPreferences(filter); final List returnedMealPlan = switch ( - await mealPlanAccess.getMealPlan()) { + await mealPlanAccess.getMealPlan()) { Success(value: final value) => value, Failure(exception: _) => [] }; @@ -531,7 +534,9 @@ void main() { test("price limit employee", () async { when(() => localStorage.getPriceCategory()) .thenAnswer((_) async => PriceCategory.employee); + when(() => database.getFavorites()).thenAnswer((_) async => favorites); + mealPlanAccess.switchToMealPlanView(); filter.price = 130; when(() => localStorage.setFilterPreferences(filter)) @@ -585,7 +590,8 @@ void main() { await mealPlanAccess.changeDate(DateTime.now()); - final List result = switch (await mealPlanAccess.getMealPlan()) { + final List result = switch ( + await mealPlanAccess.getMealPlan()) { Success(value: final value) => value, Failure(exception: _) => [] }; From 52b4f4ffd94127bd72f2d8b280862b29a24b2ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 18 Jul 2023 21:55:28 +0200 Subject: [PATCH 091/184] flutter pub --- app/pubspec.lock | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/app/pubspec.lock b/app/pubspec.lock index 897c7fea..e501e430 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -345,10 +345,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" + sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.4" path_provider_linux: dependency: transitive description: @@ -405,14 +405,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" provider: dependency: "direct main" description: @@ -449,10 +441,10 @@ packages: dependency: transitive description: name: shared_preferences_foundation - sha256: b046999bf0ff58f04c364491bb803dcfa8f42e47b19c75478f53d323684a8cc1 + sha256: f39696b83e844923b642ce9dd4bd31736c17e697f6731a5adf445b1274cf3cd4 url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shared_preferences_linux: dependency: transitive description: @@ -694,10 +686,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" xml: dependency: transitive description: From 4a43890e829a10e3e2cbbc59699abbf526dd717e Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Wed, 19 Jul 2023 10:30:35 +0200 Subject: [PATCH 092/184] fixed pubspec.lock --- app/pubspec.lock | 48 ++++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/app/pubspec.lock b/app/pubspec.lock index 2b49062f..5ed80dbd 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -169,6 +169,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + url: "https://pub.dev" + source: hosted + version: "1.6.3" crypto: dependency: "direct main" description: @@ -369,10 +377,10 @@ packages: dependency: transitive description: name: graphql - sha256: b061201579040e9548cec2bae17bbdea0ab30666cb4e7ba48b9675f14d982199 + sha256: bda5b794345087ccbd16942045be8091e2ac4619285bb22e73555d5fd88c4043 url: "https://pub.dev" source: hosted - version: "5.1.3" + version: "5.2.0-beta.1" graphql_codegen: dependency: "direct dev" description: @@ -525,14 +533,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - node_preamble: - dependency: transitive - description: - name: node_preamble - sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" - source: hosted - version: "2.0.2" nm: dependency: transitive description: @@ -541,14 +541,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" normalize: dependency: transitive description: name: normalize - sha256: baf8caf2d8b745af5737cca6c24f7fe3cf3158897fdbcde9a909b9c8d3e2e5af + sha256: "8a60e37de5b608eeaf9b839273370c71ebba445e9f73b08eee7725e0d92dbc43" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.8.2+1" package_config: dependency: transitive description: @@ -653,14 +661,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" provider: dependency: "direct main" description: @@ -950,10 +950,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b8c67f5fa3897b122cf60fe9ff314f7b0ef71eab25c5f8b771480bc338f48823 + sha256: ada49637c27973c183dad90beb6bd781eea4c9f5f955d35da172de0af7bd3440 url: "https://pub.dev" source: hosted - version: "11.7.2" + version: "11.8.0" watcher: dependency: transitive description: @@ -978,10 +978,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" - sha256: "3a969ddcc204a3e34e863d204b29c0752716f78b6f9cc8235083208d268a4ccd" - url: "https://pub.dev" - source: hosted - version: "2.2.0" win32: dependency: transitive description: From 7ba1012662d395ab4547295829d46d916adc3e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 19 Jul 2023 14:29:29 +0200 Subject: [PATCH 093/184] setting dropdown entry --- .../view/settings/SettingsDropdownEntry.dart | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 app/lib/view/settings/SettingsDropdownEntry.dart diff --git a/app/lib/view/settings/SettingsDropdownEntry.dart b/app/lib/view/settings/SettingsDropdownEntry.dart new file mode 100644 index 00000000..9b5786cd --- /dev/null +++ b/app/lib/view/settings/SettingsDropdownEntry.dart @@ -0,0 +1,66 @@ +import 'package:app/view/core/selection_components/MensaDropdown.dart'; +import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; + +/// A dropdown with heading that is used in the settings of the mensa app. +class SettingsDropdownEntry extends StatelessWidget { + final void Function(T?)? _onChanged; + final T _value; + final List> _items; + final String _heading; + + /// Creates a new MensaDropdown with heading. + /// @param key The key to identify this widget. + /// @param onChanged The function that is called when the value changes. + /// @param value The value that is currently selected. + /// @param items The items that can be selected. + /// @param heading The heading of the dropdown. + /// @returns A new MensaDropdown with heading. + const SettingsDropdownEntry( + {super.key, + required Function(T?)? onChanged, + required T value, + required List> items, + required String heading}) + : _onChanged = onChanged, + _value = value, + _items = items, + _heading = heading; + + /// Builds the widget. + /// @param context The context in which the widget is built. + /// @returns The widget. + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + FlutterI18n.translate(context, _heading), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + height: 1.5), + ), + Container( + // Container is used to give the dropdown a background color. + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.0), + color: Theme.of(context).colorScheme.surface, + ), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: MensaDropdown( + onChanged: _onChanged, + value: _value, + items: _items), + ) + ] + )) + ], + ); + } +} From b38a7d836c8e71437bd5283e1508bb252ec3e809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 19 Jul 2023 14:29:41 +0200 Subject: [PATCH 094/184] setting section --- app/lib/view/settings/SettingsSection.dart | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 app/lib/view/settings/SettingsSection.dart diff --git a/app/lib/view/settings/SettingsSection.dart b/app/lib/view/settings/SettingsSection.dart new file mode 100644 index 00000000..fd423d98 --- /dev/null +++ b/app/lib/view/settings/SettingsSection.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; + +class SettingsSection extends StatelessWidget { + final String _heading; + final List _children; + + /// Creates a new SettingsSection. + /// @param key The key to identify this widget. + /// @param heading The heading of the section. + /// @param children The children of the section. + /// @returns A new SettingsSection. + const SettingsSection({super.key, + required heading, + required children}) + : _heading = heading, + _children = children; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + FlutterI18n.translate(context, _heading), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + height: 1.5), + ), + ..._children + ], + ); + } + +} \ No newline at end of file From 851579c0297f8c94ca13dff063629cbf7dad67d6 Mon Sep 17 00:00:00 2001 From: Whatsuup Date: Wed, 19 Jul 2023 14:51:51 +0200 Subject: [PATCH 095/184] added sqLite dependency --- app/pubspec.lock | 124 +++++++++++++++++++++++++++++------------------ app/pubspec.yaml | 2 + 2 files changed, 80 insertions(+), 46 deletions(-) diff --git a/app/pubspec.lock b/app/pubspec.lock index 973e5eb2..7ff0e6d4 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" async: dependency: transitive description: @@ -65,14 +73,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" - file: - dependency: transitive - description: - name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" - url: "https://pub.dev" - source: hosted - version: "6.1.4" flutter: dependency: "direct main" description: flutter @@ -90,10 +90,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_localizations: dependency: "direct main" description: flutter @@ -103,10 +103,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "9ac1967e2f72a08af11b05b39167920f90d043cf67163d13a544a358c8f31afa" + sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" url: "https://pub.dev" source: hosted - version: "0.22.0" + version: "2.0.7" flutter_test: dependency: "direct dev" description: flutter @@ -153,10 +153,10 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.1" logging: dependency: transitive description: @@ -190,29 +190,21 @@ packages: source: hosted version: "1.9.1" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted version: "1.8.3" - path_drawing: - dependency: transitive - description: - name: path_drawing - sha256: "3bdd251dae9ffaef944450b73f168610db7e968e7b20daf0c3907f8b4aafc8a2" - url: "https://pub.dev" - source: hosted - version: "0.5.1+1" path_parsing: dependency: transitive description: name: path_parsing - sha256: ee5c47c1058ad66b4a41746ec3996af9593d0858872807bcd64ac118f0700337 + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "1.0.1" path_provider: dependency: "direct main" description: @@ -233,10 +225,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" + sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.4" path_provider_linux: dependency: transitive description: @@ -265,10 +257,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.4.0" platform: dependency: transitive description: @@ -285,14 +277,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" sky_engine: dependency: transitive description: flutter @@ -306,6 +290,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + sqflite: + dependency: "direct main" + description: + name: sqflite + sha256: b4d6710e1200e96845747e37338ea8a819a12b51689a3bcf31eff0003b37a0b9 + url: "https://pub.dev" + source: hosted + version: "2.2.8+4" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "8f7603f3f8f126740bc55c4ca2d1027aab4b74a1267a3e31ce51fe40e3b65b8f" + url: "https://pub.dev" + source: hosted + version: "2.4.5+1" stack_trace: dependency: transitive description: @@ -330,6 +330,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + url: "https://pub.dev" + source: hosted + version: "3.1.0" term_glyph: dependency: transitive description: @@ -362,6 +370,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "670f6e07aca990b4a2bcdc08a784193c4ccdd1932620244c3a86bb72a0eac67f" + url: "https://pub.dev" + source: hosted + version: "1.1.7" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "7451721781d967db9933b63f5733b1c4533022c0ba373a01bdd79d1a5457f69f" + url: "https://pub.dev" + source: hosted + version: "1.1.7" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "80a13c613c8bde758b1464a1755a7b3a8f2b6cec61fbf0f5a53c94c30f03ba2e" + url: "https://pub.dev" + source: hosted + version: "1.1.7" vector_math: dependency: transitive description: @@ -374,34 +406,34 @@ packages: dependency: transitive description: name: win32 - sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee url: "https://pub.dev" source: hosted - version: "4.1.4" + version: "5.0.5" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" xml: dependency: transitive description: name: xml - sha256: "80d494c09849dc3f899d227a78c30c5b949b985ededf884cb3f3bcd39f4b447a" + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "5.4.1" + version: "6.3.0" xml2json: dependency: transitive description: name: xml2json - sha256: b32ce8fece91ed4967f4b2023df43e9777d7cfbfe4f51e5999c1ca128b60cbf9 + sha256: c8cb35b83cce879c2ea86951fd257f4e765b0030a0298b35cf94f2b3d0f32095 url: "https://pub.dev" source: hosted - version: "5.3.2" + version: "5.3.6" yaml: dependency: transitive description: @@ -412,4 +444,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.0.0 <4.0.0" - flutter: ">=3.3.0" + flutter: ">=3.7.0-0" diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 08f860ae..6f4a40d7 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -44,6 +44,8 @@ dependencies: sdk: flutter flutter_svg: ^2.0.7 + sqflite: ^2.2.8+4 + path: ^1.8.3 dev_dependencies: flutter_test: From 66f6f54fa81b25f738ec35315354a7713e61b88b Mon Sep 17 00:00:00 2001 From: Whatsuup Date: Wed, 19 Jul 2023 14:52:28 +0200 Subject: [PATCH 096/184] implemented database constructor and initial creation --- .../model/database/SQLiteDatabaseAccess.dart | 69 +++++++++++++++---- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index 5e9357c9..142797fb 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -7,11 +7,15 @@ import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Mealplan.dart'; import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; +import 'dart:async'; +import 'package:flutter/widgets.dart'; +import 'package:path/path.dart'; +import 'package:sqflite/sqflite.dart'; class SQLiteDatabaseAccess implements IDatabaseAccess { /// The string to create a table for the canteen. - final String _canteen = ''' + static const String _canteen = ''' CREATE TABLE Canteen ( canteenID TEXT PRIMARY KEY, name TEXT NOT NULL @@ -19,7 +23,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { '''; /// The string to create a table for a line of a canteen. - final String _line = ''' + static const String _line = ''' CREATE TABLE Line( lineID TEXT PRIMARY KEY, canteenID TEXT NOT NULL, @@ -30,7 +34,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { '''; /// The string to create a table for a mealplan. - final String _mealplan = ''' + static const String _mealplan = ''' CREATE TABLE MealPlan( mealplanID TEXT, lineID TEXT NOT NULL, @@ -42,7 +46,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { '''; /// The string to create a table for a meal. - final String _meal = ''' + static final String _meal = ''' CREATE TABLE Meal( mealID TEXT PRIMARY KEY, mealplanID TEXT NOT NULL, @@ -63,7 +67,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { '''; /// The string to create a table for a side. - final String _side = ''' + static final String _side = ''' CREATE TABLE Side( sideID TEXT PRIMARY KEY, mealID TEXT NOT NULL, @@ -78,7 +82,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { '''; /// The string to create a table for an additive. - final String _image = ''' + static const String _image = ''' CREATE TABLE Image( imageID TEXT PRIMARY KEY, mealID TEXT NOT NULL, @@ -88,7 +92,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { '''; /// The string to create a table for an additive of a meal. - final String _mealAdditive = ''' + static final String _mealAdditive = ''' CREATE TABLE MealAdditive( mealID TEXT, additiveID TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), @@ -98,7 +102,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { '''; /// The string to create a table for an allergen of a meal. - final String _mealAllergen = ''' + static final String _mealAllergen = ''' CREATE TABLE MealAllergen( mealID TEXT, allergenID TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), @@ -108,7 +112,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { '''; /// The string to create a table for an additive of a side. - final String _sideAdditive = ''' + static final String _sideAdditive = ''' CREATE TABLE SideAdditive( sideID TEXT, additiveID TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), @@ -118,7 +122,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { '''; /// The string to create a table for an allergen of a side. - final String _sideAllergen = ''' + static final String _sideAllergen = ''' CREATE TABLE SideAllergen( sideID TEXT, allergenID TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), @@ -128,7 +132,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { '''; /// The string to create a table for a favorite. - final String _favorite = ''' + static final String _favorite = ''' CREATE TABLE Favorite( favoriteID TEXT PRIMARY KEY, lineID TEXT NOT NULL, @@ -142,8 +146,47 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { ) '''; - SQLiteDatabaseAccess() { - // TODO: implement constructor + static List _getDatabaseBuilder() { + return [ + _canteen, + _line, + _mealplan, + _meal, + _side, + _image, + _mealAdditive, + _mealAllergen, + _sideAdditive, + _sideAllergen, + _favorite + ]; + } + + // Database access is provided by a singleton instance to prevent several databases. + static final SQLiteDatabaseAccess _databaseAccess = SQLiteDatabaseAccess._internal(); + + factory SQLiteDatabaseAccess() { + return _databaseAccess; + } + + SQLiteDatabaseAccess._internal() { + db = _initiate(); + } + + static const String _dbName = 'meal_plan.db'; + late final Future db; + + static Future _initiate() async { + WidgetsFlutterBinding.ensureInitialized(); + return await openDatabase( + join(await getDatabasesPath(), _dbName), + onCreate: (db, version) { + for (String sql in _getDatabaseBuilder()) { + db.execute(sql); + } + }, + version: 1, + ); } @override From 642597db640e18a6542c39428ac354a4526b801d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 19 Jul 2023 15:03:49 +0200 Subject: [PATCH 097/184] exception texts --- app/assets/locales/de/mealplanException.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 app/assets/locales/de/mealplanException.json diff --git a/app/assets/locales/de/mealplanException.json b/app/assets/locales/de/mealplanException.json new file mode 100644 index 00000000..3a1d872a --- /dev/null +++ b/app/assets/locales/de/mealplanException.json @@ -0,0 +1,9 @@ +{ + "filterException": "Zu deinen Filtereinstellungen konnten keine Ergebnisse gefunden werden.", + "filterButton": "Filter temporär deaktivieren", + + "closedCanteenException": "Die Mensa ist an diesem Tag geschlossen.", + "noDataException": "Für diesen Tag liegen keine Daten vor.", + "noConnectionException": "Gerichte konnten nicht geladen werden", + "noConnectionButton": "Erneut versuchen" +} \ No newline at end of file From 41e5e426731d33363d46d77b111181d1acf528f5 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Wed, 19 Jul 2023 17:28:24 +0200 Subject: [PATCH 098/184] added icons --- app/assets/icons/exceptions/error.svg | 1 + app/assets/icons/exceptions/filter.svg | 1 + app/assets/icons/exceptions/mensa_closed.svg | 1 + app/assets/icons/exceptions/no_data.svg | 1 + app/pubspec.yaml | 1 + 5 files changed, 5 insertions(+) create mode 100644 app/assets/icons/exceptions/error.svg create mode 100644 app/assets/icons/exceptions/filter.svg create mode 100644 app/assets/icons/exceptions/mensa_closed.svg create mode 100644 app/assets/icons/exceptions/no_data.svg diff --git a/app/assets/icons/exceptions/error.svg b/app/assets/icons/exceptions/error.svg new file mode 100644 index 00000000..f6390b3c --- /dev/null +++ b/app/assets/icons/exceptions/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/exceptions/filter.svg b/app/assets/icons/exceptions/filter.svg new file mode 100644 index 00000000..8d6de982 --- /dev/null +++ b/app/assets/icons/exceptions/filter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/exceptions/mensa_closed.svg b/app/assets/icons/exceptions/mensa_closed.svg new file mode 100644 index 00000000..f7ed17d4 --- /dev/null +++ b/app/assets/icons/exceptions/mensa_closed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/exceptions/no_data.svg b/app/assets/icons/exceptions/no_data.svg new file mode 100644 index 00000000..51ebcda2 --- /dev/null +++ b/app/assets/icons/exceptions/no_data.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 171b7a42..dfd9f245 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -81,6 +81,7 @@ flutter: - assets/images/ - assets/icons/ - assets/icons/allergens/ + - assets/icons/exceptions/ - assets/locales/en/ - assets/locales/de/ # To add assets to your application, add an assets section, like this: From f29c238030526dfee413e40a86a99b2e658e3d7c Mon Sep 17 00:00:00 2001 From: Whatsuup Date: Wed, 19 Jul 2023 21:14:31 +0200 Subject: [PATCH 099/184] added getter to price class --- app/lib/view_model/repository/data_classes/meal/Price.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/lib/view_model/repository/data_classes/meal/Price.dart b/app/lib/view_model/repository/data_classes/meal/Price.dart index cbb70d1c..0dcefd46 100644 --- a/app/lib/view_model/repository/data_classes/meal/Price.dart +++ b/app/lib/view_model/repository/data_classes/meal/Price.dart @@ -39,4 +39,11 @@ class Price { }; } + int get guest => _guest; + + int get pupil => _pupil; + + int get employee => _employee; + + int get student => _student; } \ No newline at end of file From e6bd2acb7b2d3c85e08b233f560a7bcba0b49f39 Mon Sep 17 00:00:00 2001 From: Whatsuup Date: Wed, 19 Jul 2023 21:16:15 +0200 Subject: [PATCH 100/184] implemented database "getModel" functions --- .../model/database/SQLiteDatabaseAccess.dart | 274 ++++++++---------- .../model/database/model/database_model.dart | 3 + app/lib/model/database/model/db_canteen.dart | 39 +++ app/lib/model/database/model/db_favorite.dart | 80 +++++ app/lib/model/database/model/db_image.dart | 49 ++++ app/lib/model/database/model/db_line.dart | 57 ++++ app/lib/model/database/model/db_meal.dart | 117 ++++++++ .../database/model/db_meal_additive.dart | 45 +++ .../database/model/db_meal_allergen.dart | 45 +++ .../model/database/model/db_meal_plan.dart | 55 ++++ app/lib/model/database/model/db_side.dart | 80 +++++ .../database/model/db_side_additive.dart | 45 +++ .../database/model/db_side_allergen.dart | 41 +++ app/test/database_test.dart | 17 ++ 14 files changed, 798 insertions(+), 149 deletions(-) create mode 100644 app/lib/model/database/model/database_model.dart create mode 100644 app/lib/model/database/model/db_canteen.dart create mode 100644 app/lib/model/database/model/db_favorite.dart create mode 100644 app/lib/model/database/model/db_image.dart create mode 100644 app/lib/model/database/model/db_line.dart create mode 100644 app/lib/model/database/model/db_meal.dart create mode 100644 app/lib/model/database/model/db_meal_additive.dart create mode 100644 app/lib/model/database/model/db_meal_allergen.dart create mode 100644 app/lib/model/database/model/db_meal_plan.dart create mode 100644 app/lib/model/database/model/db_side.dart create mode 100644 app/lib/model/database/model/db_side_additive.dart create mode 100644 app/lib/model/database/model/db_side_allergen.dart create mode 100644 app/test/database_test.dart diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index 142797fb..43697d86 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -1,7 +1,3 @@ -import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; -import 'package:app/view_model/repository/data_classes/meal/Additive.dart'; -import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; -import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Mealplan.dart'; @@ -13,152 +9,35 @@ import 'package:flutter/widgets.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; +import 'model/db_favorite.dart'; +import 'model/db_image.dart'; +import 'model/db_meal.dart'; +import 'model/db_meal_additive.dart'; +import 'model/db_meal_allergen.dart'; +import 'model/db_side.dart'; +import 'model/db_canteen.dart'; +import 'model/db_meal_plan.dart'; +import 'model/db_line.dart'; +import 'model/db_side_additive.dart'; +import 'model/db_side_allergen.dart'; + + + class SQLiteDatabaseAccess implements IDatabaseAccess { - /// The string to create a table for the canteen. - static const String _canteen = ''' - CREATE TABLE Canteen ( - canteenID TEXT PRIMARY KEY, - name TEXT NOT NULL - ) - '''; - - /// The string to create a table for a line of a canteen. - static const String _line = ''' - CREATE TABLE Line( - lineID TEXT PRIMARY KEY, - canteenID TEXT NOT NULL, - name TEXT NOT NULL, - position INTEGER NOT NULL, - FOREIGN KEY(canteenID) REFERENCES Canteen(canteenID) - ) - '''; - - /// The string to create a table for a mealplan. - static const String _mealplan = ''' - CREATE TABLE MealPlan( - mealplanID TEXT, - lineID TEXT NOT NULL, - date TEXT, - isClosed BOOLEAN NOT NULL, - FOREIGN KEY(lineID) REFERENCES Line(lineID), - PRIMARY KEY(mealplanID, date) - ) - '''; - - /// The string to create a table for a meal. - static final String _meal = ''' - CREATE TABLE Meal( - mealID TEXT PRIMARY KEY, - mealplanID TEXT NOT NULL, - name TEXT NOT NULL, - foodtype TEXT NOT NULL CHECK(foodtype IN (${FoodType.values.map((type) => "'$type'").join(', ')})), - priceStudent INTEGER NOT NULL CHECK(priceStudent >= 0), - priceEmployee INTEGER NOT NULL CHECK(priceEmployee >= 0), - pricePupil INTEGER NOT NULL CHECK(pricePupil >= 0), - priceGuest INTEGER NOT NULL CHECK(priceGuest >= 0), - individualRating INTEGER, - numberOfRatings INTEGER NOT NULL, - averageRating DECIMAL(1,1), - lastServed TEXT NOT NULL, - nextServed TEXT, - relativeFrequency TEXT CHECK IN (${Frequency.values.map((frequency) => "'$frequency'").join(', ')}), - FOREIGN KEY(mealplanID) REFERENCES MealPlan(mealplanID) - ) - '''; - - /// The string to create a table for a side. - static final String _side = ''' - CREATE TABLE Side( - sideID TEXT PRIMARY KEY, - mealID TEXT NOT NULL, - name TEXT NOT NULL, - foodtype TEXT NOT NULL CHECK(foodtype IN (${FoodType.values.map((type) => "'$type'").join(', ')})), - priceStudent INTEGER NOT NULL CHECK(priceStudent >= 0), - priceEmployee INTEGER NOT NULL CHECK(priceEmployee >= 0), - pricePupil INTEGER NOT NULL CHECK(pricePupil >= 0), - priceGuest INTEGER NOT NULL CHECK(priceGuest >= 0), - FOREIGN KEY(mealID) REFERENCES Meal(mealID) - ) - '''; - - /// The string to create a table for an additive. - static const String _image = ''' - CREATE TABLE Image( - imageID TEXT PRIMARY KEY, - mealID TEXT NOT NULL, - url TEXT NOT NULL, - FOREIGN KEY(mealID) REFERENCES Meal(mealID) - ) - '''; - - /// The string to create a table for an additive of a meal. - static final String _mealAdditive = ''' - CREATE TABLE MealAdditive( - mealID TEXT, - additiveID TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), - FOREIGN KEY(mealID) REFERENCES Meal(mealID), - PRIMARY KEY(mealID, additiveID) - ) - '''; - - /// The string to create a table for an allergen of a meal. - static final String _mealAllergen = ''' - CREATE TABLE MealAllergen( - mealID TEXT, - allergenID TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), - FOREIGN KEY(mealID) REFERENCES Meal(mealID), - PRIMARY KEY(mealID, allergenID) - ) - '''; - - /// The string to create a table for an additive of a side. - static final String _sideAdditive = ''' - CREATE TABLE SideAdditive( - sideID TEXT, - additiveID TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), - FOREIGN KEY(sideID) REFERENCES Side(sideID), - PRIMARY KEY(sideID, additiveID) - ) - '''; - - /// The string to create a table for an allergen of a side. - static final String _sideAllergen = ''' - CREATE TABLE SideAllergen( - sideID TEXT, - allergenID TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), - FOREIGN KEY(sideID) REFERENCES Side(sideID), - PRIMARY KEY(sideID, allergenID) - ) - '''; - - /// The string to create a table for a favorite. - static final String _favorite = ''' - CREATE TABLE Favorite( - favoriteID TEXT PRIMARY KEY, - lineID TEXT NOT NULL, - lastDate TEXT NOT NULL, - foodtype TEXT CHECK(foodtype IN (${FoodType.values.map((type) => "'$type'").join(', ')})), - priceStudent INTEGER CHECK(priceStudent > 0), - priceEmployee INTEGER CHECK(priceEmployee > 0), - pricePupil INTEGER CHECK(pricePupil > 0), - priceGuest INTEGER CHECK(priceGuest > 0), - FOREIGN KEY(lineID) REFERENCES Line(lineID) - ) - '''; static List _getDatabaseBuilder() { return [ - _canteen, - _line, - _mealplan, - _meal, - _side, - _image, - _mealAdditive, - _mealAllergen, - _sideAdditive, - _sideAllergen, - _favorite + DBCanteen.initTable(), + DBLine.initTable(), + DBMealPlan.initTable(), + DBMeal.initTable(), + DBSide.initTable(), + DBImage.initTable(), + DBMealAdditive.initTable(), + DBMealAllergen.initTable(), + DBSideAdditive.initTable(), + DBSideAllergen.initTable(), + DBFavorite.initTable() ]; } @@ -170,11 +49,11 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } SQLiteDatabaseAccess._internal() { - db = _initiate(); + database = _initiate(); } static const String _dbName = 'meal_plan.db'; - late final Future db; + late final Future database; static Future _initiate() async { WidgetsFlutterBinding.ensureInitialized(); @@ -190,7 +69,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } @override - Future addFavorite(Meal meal) { + Future addFavorite(Meal meal) async { // TODO: implement addFavorite throw UnimplementedError(); } @@ -225,4 +104,101 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { throw UnimplementedError(); } + Future _getCanteen(String canteenID) async { + var db = await database; + var result = await db.query( + DBCanteen.tableName, + where: '${DBCanteen.columnCanteenID} = ?', + whereArgs: [canteenID] + ); + if(result.isNotEmpty) { + return DBCanteen.fromMap(result.first); + } else { + return null; + } + } + + Future _getLine(String lineID) async { + var db = await database; + var result = await db.query( + DBLine.tableName, + where: '${DBLine.columnLineID} = ?', + whereArgs: [lineID] + ); + if(result.isNotEmpty) { + return DBLine.fromMap(result.first); + } else { + return null; + } + } + + Future _getMeal(String mealID) async { + var db = await database; + var result = await db.query( + DBMeal.tableName, + where: '${DBMeal.columnMealID} = ?', + whereArgs: [mealID] + ); + if(result.isNotEmpty) { + return DBMeal.fromMap(result.first); + } else { + return null; + } + } + + Future _getSide(String sideID) async { + var db = await database; + var result = await db.query( + DBSide.tableName, + where: '${DBSide.columnSideID} = ?', + whereArgs: [sideID] + ); + if(result.isNotEmpty) { + return DBSide.fromMap(result.first); + } else { + return null; + } + } + + Future _getImage(String imageID) async { + var db = await database; + var result = await db.query( + DBImage.tableName, + where: '${DBImage.columnImageID} = ?', + whereArgs: [imageID] + ); + if(result.isNotEmpty) { + return DBImage.fromMap(result.first); + } else { + return null; + } + } + + Future _getMealPlan(String mealPlanID) async { + var db = await database; + var result = await db.query( + DBMealPlan.tableName, + where: '${DBMealPlan.columnMealPlanID} = ?', + whereArgs: [mealPlanID] + ); + if(result.isNotEmpty) { + return DBMealPlan.fromMap(result.first); + } else { + return null; + } + } + + Future _getFavorite(String favoriteID) async { + var db = await database; + var result = await db.query( + DBFavorite.tableName, + where: '${DBFavorite.columnFavoriteID} = ?', + whereArgs: [favoriteID] + ); + if(result.isNotEmpty) { + return DBFavorite.fromMap(result.first); + } else { + return null; + } + } } \ No newline at end of file diff --git a/app/lib/model/database/model/database_model.dart b/app/lib/model/database/model/database_model.dart new file mode 100644 index 00000000..6acf6c12 --- /dev/null +++ b/app/lib/model/database/model/database_model.dart @@ -0,0 +1,3 @@ +abstract class DatabaseModel { + Map toMap(); +} \ No newline at end of file diff --git a/app/lib/model/database/model/db_canteen.dart b/app/lib/model/database/model/db_canteen.dart new file mode 100644 index 00000000..0e6a9afe --- /dev/null +++ b/app/lib/model/database/model/db_canteen.dart @@ -0,0 +1,39 @@ +import 'database_model.dart'; + +class DBCanteen implements DatabaseModel { + final String _canteenID; + final String _name; + + static const String tableName = 'canteen'; + + static const String columnCanteenID = 'canteenID'; + static const String columnName = 'name'; + + DBCanteen(this._canteenID, this._name); + + @override + Map toMap() { + return { + columnCanteenID: _canteenID, + columnName: _name + }; + } + + static DBCanteen fromMap(Map map) { + return DBCanteen(map[columnCanteenID], map[columnName]); + } + + /// The string to create a table for the canteen. + static String initTable() { + return ''' + CREATE TABLE $tableName( + $columnCanteenID TEXT PRIMARY KEY, + $columnName TEXT NOT NULL + ) + '''; + } + + String get name => _name; + + String get canteenID => _canteenID; +} \ No newline at end of file diff --git a/app/lib/model/database/model/db_favorite.dart b/app/lib/model/database/model/db_favorite.dart new file mode 100644 index 00000000..4384b7e9 --- /dev/null +++ b/app/lib/model/database/model/db_favorite.dart @@ -0,0 +1,80 @@ + +import 'package:app/model/database/model/database_model.dart'; +import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; + +import 'db_line.dart'; + +class DBFavorite implements DatabaseModel { + final String _favoriteID; + final String _lineID; + final String _lastDate; + final FoodType _foodType; + final int _priceStudent; + final int _priceEmployee; + final int _pricePupil; + final int _priceGuest; + + static const String tableName = 'favorite'; + + static const String columnFavoriteID = 'favoriteID'; + static const String columnLineID = 'lineID'; + static const String columnLastDate = 'lastDate'; + static const String columnFoodType = 'foodType'; + static const String columnPriceStudent = 'priceStudent'; + static const String columnPriceEmployee = 'priceEmployee'; + static const String columnPricePupil = 'pricePupil'; + static const String columnPriceGuest = 'priceGuest'; + + DBFavorite(this._favoriteID, this._lineID, this._lastDate, this._foodType, this._priceStudent, this._priceEmployee, this._pricePupil, this._priceGuest); + + @override + Map toMap() { + return { + columnFavoriteID: _favoriteID, + columnLineID: _lineID, + columnLastDate: _lastDate, + columnFoodType: _foodType, + columnPriceStudent: _priceStudent, + columnPriceEmployee: _priceEmployee, + columnPricePupil: _pricePupil, + columnPriceGuest: _priceGuest + }; + } + + static DBFavorite fromMap(Map map) { + return DBFavorite(map[columnFavoriteID], map[columnLineID], map[columnLastDate], map[columnFoodType], map[columnPriceStudent], map[columnPriceEmployee], map[columnPricePupil], map[columnPriceGuest]); + } + + /// The string to create a table for a favorite. + static String initTable() { + return ''' + CREATE TABLE $tableName( + $columnFavoriteID TEXT PRIMARY KEY, + $columnLineID TEXT NOT NULL, + $columnLastDate TEXT NOT NULL, + $columnFoodType TEXT CHECK($columnFoodType IN (${FoodType.values.map((type) => "'$type'").join(', ')})), + $columnPriceStudent INTEGER CHECK($columnPriceStudent > 0), + $columnPriceEmployee INTEGER CHECK($columnPriceEmployee > 0), + $columnPricePupil INTEGER CHECK($columnPricePupil > 0), + $columnPriceGuest INTEGER CHECK($columnPriceGuest > 0), + FOREIGN KEY($columnLineID) REFERENCES ${DBLine.tableName}(${DBLine.columnLineID}) + ) + '''; + } + + int get priceGuest => _priceGuest; + + int get pricePupil => _pricePupil; + + int get priceEmployee => _priceEmployee; + + int get priceStudent => _priceStudent; + + FoodType get foodType => _foodType; + + String get lastDate => _lastDate; + + String get lineID => _lineID; + + String get favoriteID => _favoriteID; +} \ No newline at end of file diff --git a/app/lib/model/database/model/db_image.dart b/app/lib/model/database/model/db_image.dart new file mode 100644 index 00000000..9f2d6e04 --- /dev/null +++ b/app/lib/model/database/model/db_image.dart @@ -0,0 +1,49 @@ +import 'package:app/model/database/model/database_model.dart'; + +import 'db_meal.dart'; + +class DBImage implements DatabaseModel { + + final String _imageID; + final String _mealID; + final String _url; + + static const String tableName = 'image'; + + static const String columnImageID = 'imageID'; + static const String columnMealID = 'mealID'; + static const String columnUrl = 'url'; + + DBImage(this._imageID, this._mealID, this._url); + + @override + Map toMap() { + return { + columnImageID: _imageID, + columnMealID: _mealID, + columnUrl: _url + }; + } + + static DBImage fromMap(Map map) { + return DBImage(map[columnImageID], map[columnMealID], map[columnUrl]); + } + + /// The string to create a table for an image. + static String initTable() { + return ''' + CREATE TABLE $tableName( + $columnImageID TEXT PRIMARY KEY, + $columnMealID TEXT NOT NULL, + $columnUrl TEXT NOT NULL, + FOREIGN KEY($columnMealID) REFERENCES ${DBMeal.tableName}(${DBMeal.columnMealID}) + ) + '''; + } + + String get url => _url; + + String get mealID => _mealID; + + String get imageID => _imageID; +} \ No newline at end of file diff --git a/app/lib/model/database/model/db_line.dart b/app/lib/model/database/model/db_line.dart new file mode 100644 index 00000000..d1cc460c --- /dev/null +++ b/app/lib/model/database/model/db_line.dart @@ -0,0 +1,57 @@ +import 'package:app/model/database/model/db_canteen.dart'; + +import 'database_model.dart'; + +class DBLine implements DatabaseModel { + + final String _lineID; + final String _canteenID; + final String _name; + final int _position; + + static const String tableName = 'line'; + + static const String columnLineID = 'lineID'; + static const String columnCanteenID = 'canteenID'; + static const String columnName = 'name'; + static const String columnPosition = 'position'; + + DBLine(this._lineID, this._canteenID, this._name, this._position); + + @override + Map toMap() { + return { + columnLineID: _lineID, + columnCanteenID: _canteenID, + columnName: _name, + columnPosition: _position + }; + } + + static DBLine fromMap(Map map) { + return DBLine(map[columnLineID], map[columnCanteenID], map[columnName], map[columnPosition]); + } + + /// The string to create a table for a line of a canteen. + static String initTable() { + return ''' + CREATE TABLE $tableName( + $columnLineID TEXT PRIMARY KEY, + $columnCanteenID TEXT NOT NULL, + $columnName TEXT NOT NULL, + $columnPosition INTEGER NOT NULL, + FOREIGN KEY($columnCanteenID) REFERENCES ${DBCanteen.tableName}(${DBCanteen.columnCanteenID}) + ) + '''; + } + + int get position => _position; + + String get name => _name; + + String get canteenID => _canteenID; + + String get lineID => _lineID; + + +} \ No newline at end of file diff --git a/app/lib/model/database/model/db_meal.dart b/app/lib/model/database/model/db_meal.dart new file mode 100644 index 00000000..8dc21466 --- /dev/null +++ b/app/lib/model/database/model/db_meal.dart @@ -0,0 +1,117 @@ +import 'package:app/model/database/model/database_model.dart'; + +import '../../../view_model/repository/data_classes/filter/Frequency.dart'; +import '../../../view_model/repository/data_classes/meal/FoodType.dart'; +import 'db_meal_plan.dart'; + +class DBMeal implements DatabaseModel { + + final String _mealID; + final String _mealPlanID; + final String _name; + final FoodType _foodType; + final int _priceStudent; + final int _priceEmployee; + final int _pricePupil; + final int _priceGuest; + final int _individualRating; + final int _numberOfRatings; + final double _averageRating; + final String _lastServed; + final String _nextServed; + final Frequency _relativeFrequency; + + static const String tableName = 'meal'; + + static const String columnMealID = 'mealID'; + static const String columnMealPlanID = 'mealPlanID'; + static const String columnName = 'name'; + static const String columnFoodType = 'foodType'; + static const String columnPriceStudent = 'priceStudent'; + static const String columnPriceEmployee = 'priceEmployee'; + static const String columnPricePupil = 'pricePupil'; + static const String columnPriceGuest = 'priceGuest'; + static const String columnIndividualRating = 'individualRating'; + static const String columnNumberOfRatings = 'numberOfRatings'; + static const String columnAverageRating = 'averageRating'; + static const String columnLastServed = 'lastServed'; + static const String columnNextServed = 'nextServed'; + static const String columnRelativeFrequency = 'relativeFrequency'; + + DBMeal(this._mealID, this._mealPlanID, this._name, this._foodType, this._priceStudent, this._priceEmployee, this._pricePupil, this._priceGuest, this._individualRating, this._numberOfRatings, this._averageRating, this._lastServed, this._nextServed, this._relativeFrequency); + + @override + Map toMap() { + return { + columnMealID: _mealID, + columnMealPlanID: _mealPlanID, + columnName: _name, + columnFoodType: _foodType, + columnPriceStudent: _priceStudent, + columnPriceEmployee: _priceEmployee, + columnPricePupil: _pricePupil, + columnPriceGuest: _priceGuest, + columnIndividualRating: _individualRating, + columnNumberOfRatings: _numberOfRatings, + columnAverageRating: _averageRating, + columnLastServed: _lastServed, + columnNextServed: _nextServed, + columnRelativeFrequency: _relativeFrequency + }; + } + + static DBMeal fromMap(Map map) { + return DBMeal(map[columnMealID], map[columnMealPlanID], map[columnName], map[columnFoodType], map[columnPriceStudent], map[columnPriceEmployee], map[columnPricePupil], map[columnPriceGuest], map[columnIndividualRating], map[columnNumberOfRatings], map[columnAverageRating], map[columnLastServed], map[columnNextServed], map[columnRelativeFrequency]); + } + + static String initTable() { + /// The string to create a table for a meal. + return ''' + CREATE TABLE $tableName( + $columnMealID TEXT PRIMARY KEY, + $columnMealPlanID TEXT NOT NULL, + $columnName TEXT NOT NULL, + $columnFoodType TEXT NOT NULL CHECK($columnFoodType IN (${FoodType.values.map((type) => "'$type'").join(', ')})), + $columnPriceStudent INTEGER NOT NULL CHECK($columnPriceStudent >= 0), + $columnPriceEmployee INTEGER NOT NULL CHECK($columnPriceEmployee >= 0), + $columnPricePupil INTEGER NOT NULL CHECK($columnPricePupil >= 0), + $columnPriceGuest INTEGER NOT NULL CHECK($columnPriceGuest >= 0), + $columnIndividualRating INTEGER, + $columnNumberOfRatings INTEGER NOT NULL, + $columnAverageRating DECIMAL(1,1), + $columnLastServed TEXT NOT NULL, + $columnNextServed TEXT, + $columnRelativeFrequency TEXT CHECK IN (${Frequency.values.map((frequency) => "'$frequency'").join(', ')}), + FOREIGN KEY($columnMealPlanID) REFERENCES ${DBMealPlan.tableName}(${DBMealPlan.columnMealPlanID}) + ) + '''; + } + + Frequency get relativeFrequency => _relativeFrequency; + + String get nextServed => _nextServed; + + String get lastServed => _lastServed; + + double get averageRating => _averageRating; + + int get numberOfRatings => _numberOfRatings; + + int get individualRating => _individualRating; + + int get priceGuest => _priceGuest; + + int get pricePupil => _pricePupil; + + int get priceEmployee => _priceEmployee; + + int get priceStudent => _priceStudent; + + FoodType get foodType => _foodType; + + String get name => _name; + + String get mealPlanID => _mealPlanID; + + String get mealID => _mealID; +} \ No newline at end of file diff --git a/app/lib/model/database/model/db_meal_additive.dart b/app/lib/model/database/model/db_meal_additive.dart new file mode 100644 index 00000000..e81de746 --- /dev/null +++ b/app/lib/model/database/model/db_meal_additive.dart @@ -0,0 +1,45 @@ +import 'package:app/model/database/model/database_model.dart'; +import 'package:app/view_model/repository/data_classes/meal/Additive.dart'; + +import 'db_meal.dart'; + +class DBMealAdditive implements DatabaseModel { + + final String _mealID; + final Additive _additive; + + static const String tableName = 'mealAdditive'; + + static const String columnMealID = 'mealID'; + static const String columnAdditive = 'additive'; + + DBMealAdditive(this._mealID, this._additive); + + @override + Map toMap() { + return { + columnMealID: _mealID, + columnAdditive: _additive + }; + } + + static DBMealAdditive fromMap(Map map) { + return DBMealAdditive(map[columnMealID], map[columnAdditive]); + } + + /// The string to create a table for an additive of a meal. + static String initTable() { + return ''' + CREATE TABLE $tableName( + $columnMealID TEXT, + $columnAdditive TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), + FOREIGN KEY($columnMealID) REFERENCES ${DBMeal.tableName}(${DBMeal.columnMealID}), + PRIMARY KEY($columnMealID, $columnAdditive) + ) + '''; + } + + Additive get additive => _additive; + + String get mealID => _mealID; +} \ No newline at end of file diff --git a/app/lib/model/database/model/db_meal_allergen.dart b/app/lib/model/database/model/db_meal_allergen.dart new file mode 100644 index 00000000..dfd240ca --- /dev/null +++ b/app/lib/model/database/model/db_meal_allergen.dart @@ -0,0 +1,45 @@ +import 'package:app/model/database/model/database_model.dart'; + +import '../../../view_model/repository/data_classes/meal/Allergen.dart'; +import 'db_meal.dart'; + +class DBMealAllergen implements DatabaseModel { + + final String _mealID; + final Allergen _allergen; + + static const String tableName = 'mealAllergen'; + + static const String columnMealID = 'mealID'; + static const String columnAllergen = 'allergen'; + + DBMealAllergen(this._mealID, this._allergen); + + @override + Map toMap() { + return { + columnMealID: _mealID, + columnAllergen: _allergen + }; + } + + static DBMealAllergen fromMap(Map map) { + return DBMealAllergen(map[columnMealID], map[columnAllergen]); + } + + /// The string to create a table for an allergen of a meal. + static String initTable() { + return ''' + CREATE TABLE $tableName( + $columnMealID TEXT, + $columnAllergen TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), + FOREIGN KEY($columnMealID) REFERENCES ${DBMeal.tableName}(${DBMeal.columnMealID}), + PRIMARY KEY($columnMealID, $columnAllergen) + ) + '''; + } + + Allergen get allergen => _allergen; + + String get mealID => _mealID; +} \ No newline at end of file diff --git a/app/lib/model/database/model/db_meal_plan.dart b/app/lib/model/database/model/db_meal_plan.dart new file mode 100644 index 00000000..ff53faf0 --- /dev/null +++ b/app/lib/model/database/model/db_meal_plan.dart @@ -0,0 +1,55 @@ +import 'package:app/model/database/model/database_model.dart'; + +import 'db_line.dart'; + +class DBMealPlan implements DatabaseModel { + final String _mealPlanID; + final String _lineID; + final String _date; + final bool _isClosed; + + static const String tableName = 'mealPlan'; + + static const String columnMealPlanID = 'mealPlanID'; + static const String columnLineID = 'lineID'; + static const String columnDate = 'date'; + static const String columnIsClosed = 'isClosed'; + + DBMealPlan(this._mealPlanID, this._lineID, this._date, this._isClosed); + + @override + Map toMap() { + return { + columnMealPlanID: _mealPlanID, + columnLineID: _lineID, + columnDate: _date, + columnIsClosed: _isClosed + }; + } + + static DBMealPlan fromMap(Map map) { + return DBMealPlan(map[columnMealPlanID], map[columnLineID], map[columnDate], map[columnIsClosed]); + } + + /// The string to create a table for a meal plan. + static String initTable() { + return ''' + CREATE TABLE $tableName( + $columnMealPlanID TEXT NOT NULL, + $columnLineID TEXT NOT NULL, + $columnDate TEXT, + $columnIsClosed BOOLEAN NOT NULL, + FOREIGN KEY($columnLineID) REFERENCES ${DBLine.tableName}(${DBLine.columnLineID}), + PRIMARY KEY($columnMealPlanID, $columnDate) + ) + '''; + } + + bool get isClosed => _isClosed; + + String get date => _date; + + String get lineID => _lineID; + + String get mealPlanID => _mealPlanID; +} \ No newline at end of file diff --git a/app/lib/model/database/model/db_side.dart b/app/lib/model/database/model/db_side.dart new file mode 100644 index 00000000..27433c71 --- /dev/null +++ b/app/lib/model/database/model/db_side.dart @@ -0,0 +1,80 @@ +import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; + +import 'database_model.dart'; +import 'db_meal.dart'; + +class DBSide implements DatabaseModel { + + final String _sideID; + final String _mealID; + final String _name; + final FoodType _foodType; + final int _priceStudent; + final int _priceEmployee; + final int _pricePupil; + final int _priceGuest; + + static const String tableName = 'side'; + + static const String columnSideID = 'sideID'; + static const String columnMealID = 'mealID'; + static const String columnName = 'name'; + static const String columnFoodType = 'foodType'; + static const String columnPriceStudent = 'priceStudent'; + static const String columnPriceEmployee = 'priceEmployee'; + static const String columnPricePupil = 'pricePupil'; + static const String columnPriceGuest = 'priceGuest'; + + DBSide(this._sideID, this._mealID, this._name, this._foodType, this._priceStudent, this._priceEmployee, this._pricePupil, this._priceGuest); + + @override + Map toMap() { + return { + columnSideID: _sideID, + columnMealID: _mealID, + columnName: _name, + columnFoodType: _foodType, + columnPriceStudent: _priceStudent, + columnPriceEmployee: _priceEmployee, + columnPricePupil: _pricePupil, + columnPriceGuest: _priceGuest + }; + } + + static DBSide fromMap(Map map) { + return DBSide(map[columnSideID], map[columnMealID], map[columnName], map[columnFoodType], map[columnPriceStudent], map[columnPriceEmployee], map[columnPricePupil], map[columnPriceGuest]); + } + + /// The string to create a table for a side. + static String initTable() { + return ''' + CREATE TABLE $tableName( + $columnSideID TEXT PRIMARY KEY, + $columnMealID TEXT NOT NULL, + $columnName TEXT NOT NULL, + $columnFoodType TEXT NOT NULL CHECK($columnFoodType IN (${FoodType.values.map((type) => "'$type'").join(', ')})), + $columnPriceStudent INTEGER NOT NULL CHECK($columnPriceStudent >= 0), + $columnPriceEmployee INTEGER NOT NULL CHECK($columnPriceEmployee >= 0), + $columnPricePupil INTEGER NOT NULL CHECK($columnPricePupil >= 0), + $columnPriceGuest INTEGER NOT NULL CHECK($columnPriceGuest >= 0), + FOREIGN KEY($columnMealID) REFERENCES ${DBMeal.tableName}(${DBMeal.columnMealID}) + ) + '''; + } + + int get priceGuest => _priceGuest; + + int get pricePupil => _pricePupil; + + int get priceEmployee => _priceEmployee; + + int get priceStudent => _priceStudent; + + FoodType get foodType => _foodType; + + String get name => _name; + + String get mealID => _mealID; + + String get sideID => _sideID; +} \ No newline at end of file diff --git a/app/lib/model/database/model/db_side_additive.dart b/app/lib/model/database/model/db_side_additive.dart new file mode 100644 index 00000000..e2ae1692 --- /dev/null +++ b/app/lib/model/database/model/db_side_additive.dart @@ -0,0 +1,45 @@ +import 'package:app/model/database/model/database_model.dart'; +import 'package:app/model/database/model/db_side.dart'; + +import '../../../view_model/repository/data_classes/meal/Additive.dart'; + +class DBSideAdditive implements DatabaseModel { + + final String _sideID; + final Additive _additive; + + static const String tableName = 'sideAdditive'; + + static const String columnSideID = 'sideID'; + static const String columnAdditive = 'additive'; + + DBSideAdditive(this._sideID, this._additive); + + @override + Map toMap() { + return { + columnSideID: _sideID, + columnAdditive: _additive + }; + } + + static DBSideAdditive fromMap(Map map) { + return DBSideAdditive(map[columnSideID], map[columnAdditive]); + } + + /// The string to create a table for an additive of a side. + static String initTable() { + return ''' + CREATE TABLE $tableName( + $columnSideID TEXT, + $columnAdditive TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), + FOREIGN KEY($columnSideID) REFERENCES ${DBSide.tableName}(${DBSide.columnSideID}), + PRIMARY KEY($columnSideID, $columnAdditive) + ) + '''; + } + + Additive get additive => _additive; + + String get sideID => _sideID; +} \ No newline at end of file diff --git a/app/lib/model/database/model/db_side_allergen.dart b/app/lib/model/database/model/db_side_allergen.dart new file mode 100644 index 00000000..348fdf08 --- /dev/null +++ b/app/lib/model/database/model/db_side_allergen.dart @@ -0,0 +1,41 @@ +import 'package:app/model/database/model/database_model.dart'; +import 'package:app/model/database/model/db_side.dart'; + +import '../../../view_model/repository/data_classes/meal/Allergen.dart'; + +class DBSideAllergen implements DatabaseModel { + + final String _sideID; + final Allergen _allergen; + + static const String tableName = 'sideAllergen'; + + static const String columnSideID = 'sideID'; + static const String columnAllergen = 'allergen'; + + DBSideAllergen(this._sideID, this._allergen); + + @override + Map toMap() { + return { + columnSideID: _sideID, + columnAllergen: _allergen + }; + } + + static DBSideAllergen fromMap(Map map) { + return DBSideAllergen(map[columnSideID], map[columnAllergen]); + } + + /// The string to create a table for an allergen of a side. + static String initTable() { + return ''' + CREATE TABLE $tableName( + $columnSideID TEXT, + $columnAllergen TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), + FOREIGN KEY($columnSideID) REFERENCES ${DBSide.tableName}(${DBSide.columnSideID}), + PRIMARY KEY($columnSideID, $columnAllergen) + ) + '''; + } +} \ No newline at end of file diff --git a/app/test/database_test.dart b/app/test/database_test.dart new file mode 100644 index 00000000..65c7e355 --- /dev/null +++ b/app/test/database_test.dart @@ -0,0 +1,17 @@ +import 'package:app/model/database/SQLiteDatabaseAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/meal/Price.dart'; + + +Meal getMeal() { + return Meal(id: "18fb9f41-b4e8-4c6c-8b62-49ba63665516", name: "DummyMeal", foodType: FoodType.beef, price: Price(student: 200, employee: 200, pupil: 200, guest: 200)); +} + + + + +void main() { + var database = SQLiteDatabaseAccess(); + database.addFavorite(getMeal()); +} \ No newline at end of file From 1820dc9974836d119c0fc4bc8c68894ab7aa22dc Mon Sep 17 00:00:00 2001 From: Whatsuup Date: Thu, 20 Jul 2023 01:06:30 +0200 Subject: [PATCH 101/184] changed models --- app/lib/model/database/model/db_image.dart | 30 +++++++++++++++++-- .../database/model/db_side_allergen.dart | 4 +++ .../repository/data_classes/meal/Meal.dart | 4 +-- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/app/lib/model/database/model/db_image.dart b/app/lib/model/database/model/db_image.dart index 9f2d6e04..370445b8 100644 --- a/app/lib/model/database/model/db_image.dart +++ b/app/lib/model/database/model/db_image.dart @@ -7,26 +7,38 @@ class DBImage implements DatabaseModel { final String _imageID; final String _mealID; final String _url; + final double _imageRank; + final int _positiveRating; + final int _negativeRating; + final int _individualRating; static const String tableName = 'image'; static const String columnImageID = 'imageID'; static const String columnMealID = 'mealID'; static const String columnUrl = 'url'; + static const String columnImageRank = 'imageRank'; + static const String columnPositiveRating = 'positiveRating'; + static const String columnNegativeRating = 'negativeRating'; + static const String columnIndividualRating = 'individualRating'; - DBImage(this._imageID, this._mealID, this._url); + DBImage(this._imageID, this._mealID, this._url, this._imageRank, this._positiveRating, this._negativeRating, this._individualRating); @override Map toMap() { return { columnImageID: _imageID, columnMealID: _mealID, - columnUrl: _url + columnUrl: _url, + columnImageRank: _imageRank, + columnPositiveRating: _positiveRating, + columnNegativeRating: _negativeRating, + columnIndividualRating: _negativeRating }; } static DBImage fromMap(Map map) { - return DBImage(map[columnImageID], map[columnMealID], map[columnUrl]); + return DBImage(map[columnImageID], map[columnMealID], map[columnUrl], map[columnImageRank], map[columnPositiveRating], map[columnNegativeRating], map[columnIndividualRating]); } /// The string to create a table for an image. @@ -36,6 +48,10 @@ class DBImage implements DatabaseModel { $columnImageID TEXT PRIMARY KEY, $columnMealID TEXT NOT NULL, $columnUrl TEXT NOT NULL, + $columnImageRank REAL, + $columnPositiveRating INTEGER, + $columnNegativeRating INTEGER, + $columnIndividualRating INTEGER, FOREIGN KEY($columnMealID) REFERENCES ${DBMeal.tableName}(${DBMeal.columnMealID}) ) '''; @@ -46,4 +62,12 @@ class DBImage implements DatabaseModel { String get mealID => _mealID; String get imageID => _imageID; + + int get individualRating => _individualRating; + + int get negativeRating => _negativeRating; + + int get positiveRating => _positiveRating; + + double get imageRank => _imageRank; } \ No newline at end of file diff --git a/app/lib/model/database/model/db_side_allergen.dart b/app/lib/model/database/model/db_side_allergen.dart index 348fdf08..72455b43 100644 --- a/app/lib/model/database/model/db_side_allergen.dart +++ b/app/lib/model/database/model/db_side_allergen.dart @@ -38,4 +38,8 @@ class DBSideAllergen implements DatabaseModel { ) '''; } + + Allergen get allergen => _allergen; + + String get sideID => _sideID; } \ No newline at end of file diff --git a/app/lib/view_model/repository/data_classes/meal/Meal.dart b/app/lib/view_model/repository/data_classes/meal/Meal.dart index 7dd6be4f..91244d52 100644 --- a/app/lib/view_model/repository/data_classes/meal/Meal.dart +++ b/app/lib/view_model/repository/data_classes/meal/Meal.dart @@ -16,7 +16,7 @@ class Meal { final List? _sides; final int? _individualRating; final int? _numberOfRatings; - final int? _averageRating; + final double? _averageRating; final DateTime? _lastServed; final DateTime? _nextServed; final Frequency? _relativeFrequency; @@ -33,7 +33,7 @@ class Meal { List? sides, int? individualRating, int? numberOfRatings, - int? averageRating, + double? averageRating, DateTime? lastServed, DateTime? nextServed, Frequency? relativeFrequency, From 2a7944bf48dda3ea2e33b7e9742e2136e5234cef Mon Sep 17 00:00:00 2001 From: Whatsuup Date: Thu, 20 Jul 2023 01:06:47 +0200 Subject: [PATCH 102/184] added transformer functions --- .../model/database/model/database_model.dart | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/app/lib/model/database/model/database_model.dart b/app/lib/model/database/model/database_model.dart index 6acf6c12..35488499 100644 --- a/app/lib/model/database/model/database_model.dart +++ b/app/lib/model/database/model/database_model.dart @@ -1,3 +1,82 @@ +import 'package:app/model/database/SQLiteDatabaseAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/meal/Price.dart'; + +import '../../../view_model/repository/data_classes/meal/Additive.dart'; +import '../../../view_model/repository/data_classes/meal/Image.dart'; +import '../../../view_model/repository/data_classes/meal/Side.dart'; +import 'db_image.dart'; +import 'db_meal.dart'; +import 'db_side.dart'; +import 'db_side_additive.dart'; +import 'db_side_allergen.dart'; +import 'db_meal_additive.dart'; +import 'db_meal_allergen.dart'; + abstract class DatabaseModel { Map toMap(); +} + +class DatabaseTransformer { + static Meal fromDBMeal( + DBMeal dbMeal, + List allergens, + List additives, + List sides, + Map> sideAllergens, + Map> sideAdditives, + List images, + bool isFavorite + ) { + return Meal( + id: dbMeal.mealID, + name: dbMeal.name, + foodType: dbMeal.foodType, + price: Price( + student: dbMeal.priceStudent, + employee: dbMeal.priceEmployee, + pupil: dbMeal.pricePupil, + guest: dbMeal.priceGuest + ), + additives: additives, + allergens: allergens, + sides: sides.map((side) => fromDBSide(side, sideAllergens[side]!, sideAdditives[side]!)).toList(), + individualRating: dbMeal.individualRating, + numberOfRatings: dbMeal.numberOfRatings, + averageRating: dbMeal.averageRating, + lastServed: DateTime.tryParse(dbMeal.lastServed), + nextServed: DateTime.tryParse(dbMeal.nextServed), + relativeFrequency: dbMeal.relativeFrequency, + images: images.map((image) => fromDBImage(image)).toList(), + isFavorite: isFavorite + ); + } + + static Side fromDBSide(DBSide side, List allergens, List additives) { + return Side( + id: side.sideID, + name: side.name, + foodType: side.foodType, + price: Price( + student: side.priceStudent, + employee: side.priceEmployee, + pupil: side.pricePupil, + guest: side.priceGuest + ), + allergens: allergens, + additives: additives + ); + } + + static Image fromDBImage(DBImage image) { + return Image( + id: image.imageID, + url: image.url, + imageRank: image.imageRank, + individualRating: image.individualRating, + positiveRating: image.positiveRating, + negativeRating: image.positiveRating + ); + } } \ No newline at end of file From a42b1ff3997109c18f7290d0245761cbe5c16534 Mon Sep 17 00:00:00 2001 From: Whatsuup Date: Thu, 20 Jul 2023 01:07:12 +0200 Subject: [PATCH 103/184] implemented almost all interface functions --- .../model/database/SQLiteDatabaseAccess.dart | 155 ++++++++++++++++-- 1 file changed, 141 insertions(+), 14 deletions(-) diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index 43697d86..e9639776 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -1,3 +1,5 @@ +import 'package:app/model/database/model/database_model.dart'; +import 'package:app/view_model/repository/data_classes/meal/Additive.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Mealplan.dart'; @@ -9,6 +11,8 @@ import 'package:flutter/widgets.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; +import '../../view_model/repository/data_classes/meal/Allergen.dart'; +import '../../view_model/repository/data_classes/mealplan/Line.dart'; import 'model/db_favorite.dart'; import 'model/db_image.dart'; import 'model/db_meal.dart'; @@ -70,20 +74,50 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { @override Future addFavorite(Meal meal) async { - // TODO: implement addFavorite - throw UnimplementedError(); + var db = await database; + var dbMeal = await _getMeal(meal.id); + var dbMealPlan = await _getMealPlan(dbMeal!.mealPlanID); + var dbLine = await _getLine(dbMealPlan!.lineID); + // FavoriteID is now the related mealID. Seems right. TODO: DateTime to String, if toString() isn't enough. + var favorite = DBFavorite(meal.id, dbLine!.lineID, meal.lastServed.toString(), meal.foodType, meal.price.student, meal.price.employee, meal.price.pupil, meal.price.guest); + return db.insert( + DBFavorite.tableName, + favorite.toMap() + ); } @override - Future deleteFavorite(Meal meal) { - // TODO: implement deleteFavorite - throw UnimplementedError(); + Future deleteFavorite(Meal meal) async { + var db = await database; + return db.delete( + DBFavorite.tableName, + where: '${DBFavorite.columnFavoriteID} = ?', + // Same as above: mealID and favID is the same meal. + whereArgs: [meal.id] + ); } @override - Future> getFavorites() { - // TODO: implement getFavorites - throw UnimplementedError(); + Future> getFavorites() async { + var db = await database; + var meals = List.empty(); + var dbFavoritesListResult = await db.query(DBFavorite.tableName); + for (Map favoriteMap in dbFavoritesListResult) { + var favorite = DBFavorite.fromMap(favoriteMap); + var dbMeal = await _getMeal(favorite.favoriteID); + var allergens = await _getMealAllergens(dbMeal!.mealID); + var additives = await _getMealAdditive(dbMeal.mealID); + var sides = await _getSides(dbMeal.mealID); + var sideAllergens = >{}; + var sideAdditives = >{}; + for (DBSide side in sides) { + sideAllergens[side] = (await _getSideAllergens(side.sideID))!; + sideAdditives[side] = (await _getSideAdditive(side.sideID))!; + } + var images = await _getImages(dbMeal.mealID); + meals.add(DatabaseTransformer.fromDBMeal(dbMeal, allergens!, additives!, sides, sideAllergens, sideAdditives, images, true)); + } + return meals; } @override @@ -93,15 +127,47 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } @override - Future>> getMealPlan(DateTime date, Canteen canteen) { - // TODO: implement getMealPlan - throw UnimplementedError(); + Future>> getMealPlan(DateTime date, Canteen canteen) async { + var db = await database; + var result = await db.query( + DBMealPlan.tableName, + where: '${DBCanteen.tableName}.${DBCanteen.columnCanteenID} = ${DBLine.tableName}.${DBLine.columnCanteenID} AND ${DBLine.tableName}.${DBLine.columnLineID} = ${DBMealPlan.tableName}.${DBMealPlan.columnLineID} AND ${DBCanteen.tableName}.${DBCanteen.columnCanteenID} = "?" AND ${DBMealPlan.tableName}.${DBMealPlan.columnDate} = "?"', + whereArgs: [canteen.id, date.toString()] + ); + if(result.isNotEmpty) { + return Success(result.map((mealPlanRow) => DBMealPlan.fromMap(mealPlanRow)).cast().toList()); + } else { + return Failure(result as Exception); + } + } @override - Future updateAll(List mealplans) { - // TODO: implement updateAll - throw UnimplementedError(); + Future updateAll(List mealPlans) async { + for (Mealplan mealPlan in mealPlans) { + await _insertCanteen(mealPlan.line.canteen); + await _insertLine(mealPlan.line); + await _insertMealPlan(mealPlan); + } + } + + Future _insertLine(Line line) async { + var db = await database; + var dbLine = DBLine(line.id, line.canteen.id, line.name, line.position); + return db.insert(DBLine.tableName, dbLine.toMap()); + } + + Future _insertCanteen(Canteen canteen) async { + var db = await database; + var dbCanteen = DBCanteen(canteen.id, canteen.name); + return db.insert(DBCanteen.tableName, dbCanteen.toMap()); + } + + Future _insertMealPlan(Mealplan mealPlan) async { + var db = await database; + var uuid = ''; // TODO: generate UUID + var dbMealPlan = DBMealPlan(uuid, mealPlan.line.id, mealPlan.date.toString(), mealPlan.isClosed); + return db.insert(DBMealPlan.tableName, dbMealPlan.toMap()); } Future _getCanteen(String canteenID) async { @@ -160,6 +226,16 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } } + Future> _getSides(String mealID) async { + var db = await database; + var result = await db.query( + DBSide.tableName, + where: '${DBSide.columnMealID} = ?', + whereArgs: [mealID] + ); + return result.map((sideRow) => DBSide.fromMap(sideRow)).toList(); + } + Future _getImage(String imageID) async { var db = await database; var result = await db.query( @@ -174,6 +250,16 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } } + Future> _getImages(String mealID) async { + var db = await database; + var result = await db.query( + DBImage.tableName, + where: '${DBImage.columnMealID} = ?', + whereArgs: [mealID] + ); + return result.map((imageRow) => DBImage.fromMap(imageRow)).toList(); + } + Future _getMealPlan(String mealPlanID) async { var db = await database; var result = await db.query( @@ -201,4 +287,45 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { return null; } } + + Future?> _getMealAllergens(String id) async { + var db = await database; + var result = await db.query( + DBMealAllergen.tableName, + where: '${DBMealAllergen.columnMealID} = ?', + whereArgs: [id] + ); + return result.map((allergenMap) => DBMealAllergen.fromMap(allergenMap).allergen).toList(); + } + + Future?> _getSideAllergens(String id) async { + var db = await database; + var result = await db.query( + DBSideAllergen.tableName, + where: '${DBSideAllergen.columnSideID} = ?', + whereArgs: [id] + ); + return result.map((allergenMap) => DBMealAllergen.fromMap(allergenMap).allergen).toList(); + } + + Future?> _getMealAdditive(String id) async { + var db = await database; + var result = await db.query( + DBMealAdditive.tableName, + where: '${DBMealAdditive.columnMealID} = ?', + whereArgs: [id] + ); + return result.map((allergenMap) => DBMealAdditive.fromMap(allergenMap).additive).toList(); + } + + Future?> _getSideAdditive(String id) async { + var db = await database; + var result = await db.query( + DBSideAdditive.tableName, + where: '${DBSideAdditive.columnSideID} = ?', + whereArgs: [id] + ); + return result.map((allergenMap) => DBSideAdditive.fromMap(allergenMap).additive).toList(); + } + } \ No newline at end of file From 0f4703df46428f8bc14d92b9bef4f669c3b0db60 Mon Sep 17 00:00:00 2001 From: Whatsuup Date: Thu, 20 Jul 2023 01:07:36 +0200 Subject: [PATCH 104/184] fixed errors --- app/lib/view_model/repository/data_classes/meal/Meal.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/view_model/repository/data_classes/meal/Meal.dart b/app/lib/view_model/repository/data_classes/meal/Meal.dart index 91244d52..aeb8b5a4 100644 --- a/app/lib/view_model/repository/data_classes/meal/Meal.dart +++ b/app/lib/view_model/repository/data_classes/meal/Meal.dart @@ -111,7 +111,7 @@ class Meal { int? get numberOfRatings => _numberOfRatings; - int? get averageRating => _averageRating; + double? get averageRating => _averageRating; DateTime? get lastServed => _lastServed; From 562d44f74226f80b8d4ad31cbf9dce7eb7c67949 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Thu, 20 Jul 2023 08:57:52 +0200 Subject: [PATCH 105/184] added icons --- .../{mensa_closed.svg => canteen_closed.svg} | 0 .../CanteenClosedExceptionIcon.dart | 28 +++++++++++++++++++ .../icons/exceptions/ErrorExceptionIcon.dart | 28 +++++++++++++++++++ .../icons/exceptions/FilterExceptionIcon.dart | 28 +++++++++++++++++++ .../icons/exceptions/NoDataExceptionIcon.dart | 28 +++++++++++++++++++ 5 files changed, 112 insertions(+) rename app/assets/icons/exceptions/{mensa_closed.svg => canteen_closed.svg} (100%) create mode 100644 app/lib/view/core/icons/exceptions/CanteenClosedExceptionIcon.dart create mode 100644 app/lib/view/core/icons/exceptions/ErrorExceptionIcon.dart create mode 100644 app/lib/view/core/icons/exceptions/FilterExceptionIcon.dart create mode 100644 app/lib/view/core/icons/exceptions/NoDataExceptionIcon.dart diff --git a/app/assets/icons/exceptions/mensa_closed.svg b/app/assets/icons/exceptions/canteen_closed.svg similarity index 100% rename from app/assets/icons/exceptions/mensa_closed.svg rename to app/assets/icons/exceptions/canteen_closed.svg diff --git a/app/lib/view/core/icons/exceptions/CanteenClosedExceptionIcon.dart b/app/lib/view/core/icons/exceptions/CanteenClosedExceptionIcon.dart new file mode 100644 index 00000000..737b21b5 --- /dev/null +++ b/app/lib/view/core/icons/exceptions/CanteenClosedExceptionIcon.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the exceptions icon for a closed canteen +class CanteenClosedExceptionIcon extends StatelessWidget { + final double _size; + final Color? _color; + + /// Creates an filter icon. + /// @param key The key to use for this widget. + /// @param size The size of the icon. + /// @param color The color of the icon. + /// @returns a widget that displays the icon for a closed canteen + const CanteenClosedExceptionIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/exceptions/canteen_closed.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/exceptions/ErrorExceptionIcon.dart b/app/lib/view/core/icons/exceptions/ErrorExceptionIcon.dart new file mode 100644 index 00000000..fb414078 --- /dev/null +++ b/app/lib/view/core/icons/exceptions/ErrorExceptionIcon.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the exceptions icon for an error +class ErrorExceptionIcon extends StatelessWidget { + final double _size; + final Color? _color; + + /// Creates an error icon. + /// @param key The key to use for this widget. + /// @param size The size of the icon. + /// @param color The color of the icon. + /// @returns a widget that displays the icon for an error + const ErrorExceptionIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/exceptions/error.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/exceptions/FilterExceptionIcon.dart b/app/lib/view/core/icons/exceptions/FilterExceptionIcon.dart new file mode 100644 index 00000000..e2eb1c7d --- /dev/null +++ b/app/lib/view/core/icons/exceptions/FilterExceptionIcon.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the exceptions icon for a filter issue +class FilterExceptionIcon extends StatelessWidget { + final double _size; + final Color? _color; + + /// Creates an filter icon. + /// @param key The key to use for this widget. + /// @param size The size of the icon. + /// @param color The color of the icon. + /// @returns a widget that displays the icon for a filter issue + const FilterExceptionIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/exceptions/filter.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/exceptions/NoDataExceptionIcon.dart b/app/lib/view/core/icons/exceptions/NoDataExceptionIcon.dart new file mode 100644 index 00000000..c8bcf491 --- /dev/null +++ b/app/lib/view/core/icons/exceptions/NoDataExceptionIcon.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the exceptions icon for unavailable data +class NoDataExceptionIcon extends StatelessWidget { + final double _size; + final Color? _color; + + /// Creates an filter icon. + /// @param key The key to use for this widget. + /// @param size The size of the icon. + /// @param color The color of the icon. + /// @returns a widget that displays the icon for unavailable data + const NoDataExceptionIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/exceptions/no_data.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} From a05b2e6a171eca2d23bdd855c284baaf98f6d778 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Thu, 20 Jul 2023 09:43:33 +0200 Subject: [PATCH 106/184] implemented MensaAppBar --- .../model/api_server/GraphQlServerAccess.dart | 20 ++++++ app/lib/view/core/MensaAppBar.dart | 69 +++++++++++++++++++ .../core/icons/allergens/AllergenIcon.dart | 2 +- .../selection_components/MensaDropdown.dart | 10 ++- .../logic/meal/CombinedMealPlanAccess.dart | 7 ++ .../view_model/logic/meal/IMealAccess.dart | 7 +- .../repository/interface/IServerAccess.dart | 4 ++ 7 files changed, 114 insertions(+), 5 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index 7e9850fe..e25d752d 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -253,6 +253,26 @@ class GraphQlServerAccess implements IServerAccess { return _convertCanteen(canteen); } + @override + Future?> getCanteens() async { + final result = await _client + .query$GetDefaultCanteen(Options$Query$GetDefaultCanteen()); + + final exception = result.exception; + if (exception != null) { + return null; + } + + var canteen = result.parsedData?.getCanteens.first; + + if (canteen == null) { + return null; + } + return result.parsedData?.getCanteens + .map((e) => _convertCanteen(e)) + .toList(); + } + // --------------- auth --------------- static const int apiKeyIdentifierPrefixLength = 10; static const String authenticationScheme = "Mensa"; diff --git a/app/lib/view/core/MensaAppBar.dart b/app/lib/view/core/MensaAppBar.dart index e69de29b..d649c629 100644 --- a/app/lib/view/core/MensaAppBar.dart +++ b/app/lib/view/core/MensaAppBar.dart @@ -0,0 +1,69 @@ +import 'package:app/view/core/selection_components/MensaDropdown.dart'; +import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; +import 'package:app/view_model/logic/meal/IMealAccess.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class MensaAppBar extends StatelessWidget implements PreferredSizeWidget { + final PreferredSizeWidget? _bottom; + final double? _appBarHeight; + + MensaAppBar({super.key, PreferredSizeWidget? bottom, double? appBarHeight}) + : _appBarHeight = appBarHeight, + _bottom = bottom, + preferredSize = + _PreferredAppBarSize(appBarHeight, bottom?.preferredSize.height); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: preferredSize.height, + child: Column(children: [ + Consumer( + builder: (context, mealAccess, child) => FutureBuilder( + future: mealAccess.getAvailableCanteens(), + builder: (context, availableCanteens) => FutureBuilder( + future: mealAccess.getCanteen(), + builder: (context, selectedCanteen) { + if (selectedCanteen.hasData) { + return SizedBox( + height: _appBarHeight, + child: MensaDropdown( + backgroundColor: Theme.of(context) + .colorScheme + .background, + onChanged: (canteen) => { + if (canteen != null) + { + mealAccess.changeCanteen(canteen), + } + }, + value: selectedCanteen.requireData, + items: availableCanteens.requireData + .map((canteen) => + MensaDropdownEntry( + value: canteen, + label: canteen.name, + )) + .toList())); + } + return const SizedBox.shrink(); + }), + )), + if (_bottom != null) _bottom! + ])); + } + + @override + final Size preferredSize; +} + +class _PreferredAppBarSize extends Size { + _PreferredAppBarSize(this.appBarHeight, this.bottomHeight) + : super.fromHeight( + (appBarHeight ?? kToolbarHeight) + (bottomHeight ?? 0)); + + final double? appBarHeight; + final double? bottomHeight; +} diff --git a/app/lib/view/core/icons/allergens/AllergenIcon.dart b/app/lib/view/core/icons/allergens/AllergenIcon.dart index 4af54ec5..10315cb3 100644 --- a/app/lib/view/core/icons/allergens/AllergenIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenIcon.dart @@ -183,7 +183,7 @@ class AllergenIcon extends IAllergenIcon { width: width, height: height, color: _color ?? Theme.of(context).colorScheme.onSurface); - case Allergen.gt: + case Allergen.gl: return AllergenGelatineIcon( width: width, height: height, diff --git a/app/lib/view/core/selection_components/MensaDropdown.dart b/app/lib/view/core/selection_components/MensaDropdown.dart index 5bbbdd9b..10e43ccd 100644 --- a/app/lib/view/core/selection_components/MensaDropdown.dart +++ b/app/lib/view/core/selection_components/MensaDropdown.dart @@ -6,21 +6,25 @@ class MensaDropdown extends StatelessWidget { final void Function(T?)? _onChanged; final T _value; final List> _items; + final Color? _backgroundColor; /// Creates a new MensaDropdown. /// @param key The key to identify this widget. /// @param onChanged The function that is called when the value changes. /// @param value The value that is currently selected. /// @param items The items that can be selected. + /// @param backgroundColor The background color of the dropdown. /// @returns A new MensaDropdown. const MensaDropdown( {super.key, required Function(T?)? onChanged, required T value, - required List> items}) + required List> items, + Color? backgroundColor}) : _onChanged = onChanged, _value = value, - _items = items; + _items = items, + _backgroundColor = backgroundColor; /// Builds the widget. /// @param context The context in which the widget is built. @@ -30,7 +34,7 @@ class MensaDropdown extends StatelessWidget { return Container( // Container is used to give the dropdown a background color. decoration: BoxDecoration( borderRadius: BorderRadius.circular(4.0), - color: Theme.of(context).colorScheme.surface, + color: _backgroundColor ?? Theme.of(context).colorScheme.surface, ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 9afd4a7d..6c511dc5 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -237,6 +237,13 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { return _activeCanteen; } + @override + Future> getAvailableCanteens() async { + await _doneInitialization; + + return await _api.getCanteens() ?? List.empty(); + } + @override Future getDate() async { await _doneInitialization; diff --git a/app/lib/view_model/logic/meal/IMealAccess.dart b/app/lib/view_model/logic/meal/IMealAccess.dart index 06366a9c..2855ca71 100644 --- a/app/lib/view_model/logic/meal/IMealAccess.dart +++ b/app/lib/view_model/logic/meal/IMealAccess.dart @@ -1,13 +1,14 @@ import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; +import 'package:flutter/cupertino.dart'; import '../../repository/data_classes/meal/Meal.dart'; import '../../repository/data_classes/mealplan/Canteen.dart'; import '../../repository/error_handling/Result.dart'; /// This class is the interface for the access to the meal data. The access can be done via the database or the server. -abstract class IMealAccess { +abstract class IMealAccess with ChangeNotifier { /// This method requests the mealplan of the committed canteen for the committed day from the database. /// If the requested data is not stored there, the data is requested from the server. /// @param date The date of the mealplan @@ -54,6 +55,10 @@ abstract class IMealAccess { /// @return The currently selected Canteen. Future getCanteen(); + /// This method returns all available canteens. + /// @return All available canteens. + Future> getAvailableCanteens(); + /// This method changes the last used canteen that is stored. /// @param canteen The new canteen /// @return The result of the update diff --git a/app/lib/view_model/repository/interface/IServerAccess.dart b/app/lib/view_model/repository/interface/IServerAccess.dart index c322f5c6..0550cc7c 100644 --- a/app/lib/view_model/repository/interface/IServerAccess.dart +++ b/app/lib/view_model/repository/interface/IServerAccess.dart @@ -67,4 +67,8 @@ abstract class IServerAccess { /// This method requests the default canteen from the server. /// @return The default canteen or null if no connection could be established. Future getDefaultCanteen(); + + /// This method requests all canteens from the server. + /// @return All canteens or null if no connection could be established. + Future?> getCanteens(); } From 1be18c11ba65bb8a551c30d3ae58fe248ef1bdc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 19 Jul 2023 16:03:37 +0200 Subject: [PATCH 107/184] first drafts --- app/lib/view/mealplan/MealPlanClosed.dart | 17 ++++++++++++ app/lib/view/mealplan/MealPlanError.dart | 34 +++++++++++++++++++++++ app/lib/view/mealplan/MealPlanFilter.dart | 10 +++++++ app/lib/view/mealplan/MealPlanNoData.dart | 15 ++++++++++ 4 files changed, 76 insertions(+) create mode 100644 app/lib/view/mealplan/MealPlanClosed.dart create mode 100644 app/lib/view/mealplan/MealPlanError.dart create mode 100644 app/lib/view/mealplan/MealPlanFilter.dart create mode 100644 app/lib/view/mealplan/MealPlanNoData.dart diff --git a/app/lib/view/mealplan/MealPlanClosed.dart b/app/lib/view/mealplan/MealPlanClosed.dart new file mode 100644 index 00000000..ed19a79b --- /dev/null +++ b/app/lib/view/mealplan/MealPlanClosed.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; + +class MealPlanClosed extends StatelessWidget { + + + @override + Widget build(BuildContext context) { + return Center( + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + // todo add icon + Text(FlutterI18n.translate( + context, "mealplanException.closedCanteenException")), + ])); + } + +} \ No newline at end of file diff --git a/app/lib/view/mealplan/MealPlanError.dart b/app/lib/view/mealplan/MealPlanError.dart new file mode 100644 index 00000000..341741c6 --- /dev/null +++ b/app/lib/view/mealplan/MealPlanError.dart @@ -0,0 +1,34 @@ +import 'package:app/view/core/buttons/MensaButton.dart'; +import 'package:app/view_model/logic/meal/IMealAccess.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; + +// todo text style +class MealPlanError extends StatelessWidget { + final IMealAccess _mealAccess; + String _temporalMessage = ""; + + MealPlanError({super.key, required IMealAccess mealAccess}) + : _mealAccess = mealAccess; + + @override + Widget build(BuildContext context) { + return Center( + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + // todo add icon + Text(FlutterI18n.translate( + context, "mealplanException.noConnectionException")), + MensaButton(onPressed: () async { + _temporalMessage = await _mealAccess.refreshMealplan() ?? ""; + if (_temporalMessage.isNotEmpty) { + final snackBar = SnackBar( + content: Text(FlutterI18n.translate(context, _temporalMessage)), + ); + + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } + }, + text: "mealplanException.noConnectionButton"), + ])); + } +} diff --git a/app/lib/view/mealplan/MealPlanFilter.dart b/app/lib/view/mealplan/MealPlanFilter.dart new file mode 100644 index 00000000..5e6aeddf --- /dev/null +++ b/app/lib/view/mealplan/MealPlanFilter.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class MealPlanFilter extends StatelessWidget { + @override + Widget build(BuildContext context) { + // TODO: implement build + throw UnimplementedError(); + } + +} \ No newline at end of file diff --git a/app/lib/view/mealplan/MealPlanNoData.dart b/app/lib/view/mealplan/MealPlanNoData.dart new file mode 100644 index 00000000..31a7cac5 --- /dev/null +++ b/app/lib/view/mealplan/MealPlanNoData.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; + +class MealPlanNoData extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Center( + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + // todo add icon + Text(FlutterI18n.translate( + context, "mealplanException.noDataException")), + ])); + } + +} \ No newline at end of file From 6b48db8f0c4e0b68657b9a846d1e03fb0d105125 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Thu, 20 Jul 2023 12:16:03 +0200 Subject: [PATCH 108/184] implemented MealPlanDateSelect --- app/assets/icons/navigation/arrow_left.svg | 1 + app/assets/icons/navigation/arrow_right.svg | 1 + app/lib/view/core/buttons/MensaTapable.dart | 28 +++++++++ .../navigation/NavigationArrowLeftIcon.dart | 23 +++++++ .../navigation/NavigationArrowRightIcon.dart | 23 +++++++ app/lib/view/mealplan/MealPlanDateSelect.dart | 61 +++++++++++++++++++ app/pubspec.yaml | 1 + 7 files changed, 138 insertions(+) create mode 100644 app/assets/icons/navigation/arrow_left.svg create mode 100644 app/assets/icons/navigation/arrow_right.svg create mode 100644 app/lib/view/core/buttons/MensaTapable.dart create mode 100644 app/lib/view/core/icons/navigation/NavigationArrowLeftIcon.dart create mode 100644 app/lib/view/core/icons/navigation/NavigationArrowRightIcon.dart create mode 100644 app/lib/view/mealplan/MealPlanDateSelect.dart diff --git a/app/assets/icons/navigation/arrow_left.svg b/app/assets/icons/navigation/arrow_left.svg new file mode 100644 index 00000000..03961670 --- /dev/null +++ b/app/assets/icons/navigation/arrow_left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/navigation/arrow_right.svg b/app/assets/icons/navigation/arrow_right.svg new file mode 100644 index 00000000..18deaa3f --- /dev/null +++ b/app/assets/icons/navigation/arrow_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/lib/view/core/buttons/MensaTapable.dart b/app/lib/view/core/buttons/MensaTapable.dart new file mode 100644 index 00000000..2b0e657b --- /dev/null +++ b/app/lib/view/core/buttons/MensaTapable.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +class MensaTapable extends StatelessWidget { + + final Widget _child; + final Color? _color; + final Function() _onTap; + + MensaTapable({super.key, required Widget child, Color? color, required Function() onTap}) : _child = child, _color = color, _onTap = onTap; + + @override + Widget build(BuildContext context) { + return Material( + color: _color ?? Colors.transparent, + borderRadius: BorderRadius.circular(4), + child: InkWell( + borderRadius: BorderRadius.circular(4), + onTap: _onTap, + child: _child, + ), + ); + } + + + + + +} \ No newline at end of file diff --git a/app/lib/view/core/icons/navigation/NavigationArrowLeftIcon.dart b/app/lib/view/core/icons/navigation/NavigationArrowLeftIcon.dart new file mode 100644 index 00000000..5620680d --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationArrowLeftIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationArrowLeftIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationArrowLeftIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/arrow_left.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/navigation/NavigationArrowRightIcon.dart b/app/lib/view/core/icons/navigation/NavigationArrowRightIcon.dart new file mode 100644 index 00000000..33006580 --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationArrowRightIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationArrowRightIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationArrowRightIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/arrow_right.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/mealplan/MealPlanDateSelect.dart b/app/lib/view/mealplan/MealPlanDateSelect.dart new file mode 100644 index 00000000..4fbe9d4f --- /dev/null +++ b/app/lib/view/mealplan/MealPlanDateSelect.dart @@ -0,0 +1,61 @@ +import 'package:app/view/core/buttons/MensaTapable.dart'; +import 'package:app/view/core/icons/navigation/NavigationArrowLeftIcon.dart'; +import 'package:app/view/core/icons/navigation/NavigationArrowRightIcon.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class MealPlanDateSelect extends StatelessWidget { + final DateTime _date; + final Function(DateTime) _onDateChanged; + + final DateFormat _dateFormat = DateFormat('dd.MM.yyyy'); + + MealPlanDateSelect( + {super.key, + required DateTime date, + required Function(DateTime) onDateChanged}) + : _date = date, + _onDateChanged = onDateChanged; + + @override + Widget build(BuildContext context) { + return Row(children: [ + MensaTapable( + child: const Padding( + padding: EdgeInsets.all(8), child: NavigationArrowLeftIcon()), + onTap: () { + DateTime before = _date.subtract(const Duration(days: 1)); + _onDateChanged(before.isBefore(DateTime(1923)) ? _date : before); + }, + ), + MensaTapable( + child: Padding( + padding: const EdgeInsets.all(10), + child: Text( + _dateFormat.format(_date), + style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold), + ), + ), + onTap: () => { + showDatePicker( + context: context, + initialDate: _date, + firstDate: DateTime(1923), + lastDate: DateTime.now().add(const Duration(days: 365 * 10)), + initialEntryMode: DatePickerEntryMode.calendarOnly, + ).then((value) => _onDateChanged(value!)) + }), + MensaTapable( + child: const Padding( + padding: EdgeInsets.all(8), child: NavigationArrowRightIcon()), + onTap: () { + DateTime after = _date.add(const Duration(days: 1)); + _onDateChanged( + after.isAfter(DateTime.now().add(const Duration(days: 365 * 10))) + ? _date + : after); + }, + ), + ]); + } +} diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 171b7a42..811f55c4 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -81,6 +81,7 @@ flutter: - assets/images/ - assets/icons/ - assets/icons/allergens/ + - assets/icons/navigation/ - assets/locales/en/ - assets/locales/de/ # To add assets to your application, add an assets section, like this: From 9ada7eee0a6c999f28a3858b2cdb89d13b0a8e41 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Thu, 20 Jul 2023 15:04:45 +0200 Subject: [PATCH 109/184] made corrections --- app/lib/main.dart | 2 +- app/lib/view/mealplan/MealPlanError.dart | 44 ++++++++++++++---------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/app/lib/main.dart b/app/lib/main.dart index 0e105810..a28a88d9 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -6,7 +6,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; void main() { final FlutterI18nDelegate delegate = FlutterI18nDelegate( translationLoader: NamespaceFileTranslationLoader( - namespaces: ["common", "ratings"], + namespaces: ["common", "ratings", "mealplanException", "snackbar"], useCountryCode: false, basePath: 'assets/locales', fallbackDir: 'de'), diff --git a/app/lib/view/mealplan/MealPlanError.dart b/app/lib/view/mealplan/MealPlanError.dart index 341741c6..9a47ad2d 100644 --- a/app/lib/view/mealplan/MealPlanError.dart +++ b/app/lib/view/mealplan/MealPlanError.dart @@ -2,33 +2,39 @@ import 'package:app/view/core/buttons/MensaButton.dart'; import 'package:app/view_model/logic/meal/IMealAccess.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:provider/provider.dart'; // todo text style class MealPlanError extends StatelessWidget { - final IMealAccess _mealAccess; String _temporalMessage = ""; - MealPlanError({super.key, required IMealAccess mealAccess}) - : _mealAccess = mealAccess; + MealPlanError({super.key}); @override Widget build(BuildContext context) { - return Center( - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - // todo add icon - Text(FlutterI18n.translate( - context, "mealplanException.noConnectionException")), - MensaButton(onPressed: () async { - _temporalMessage = await _mealAccess.refreshMealplan() ?? ""; - if (_temporalMessage.isNotEmpty) { - final snackBar = SnackBar( - content: Text(FlutterI18n.translate(context, _temporalMessage)), - ); + return Consumer( + builder: (context, mealAccess, child) => Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // todo add icon + Text(FlutterI18n.translate( + context, "mealplanException.noConnectionException")), + MensaButton( + onPressed: () async { + // Mach das einfach als lokale Variable + _temporalMessage = + await mealAccess.refreshMealplan() ?? ""; + if (_temporalMessage.isNotEmpty) { + final snackBar = SnackBar( + content: Text(FlutterI18n.translate( + context, _temporalMessage)), + ); - ScaffoldMessenger.of(context).showSnackBar(snackBar); - } - }, - text: "mealplanException.noConnectionButton"), - ])); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } + }, + text: "mealplanException.noConnectionButton"), + ]))); } } From 37d7a5b67934c69151ad8924673632d9c05a2171 Mon Sep 17 00:00:00 2001 From: Whatsuup Date: Thu, 20 Jul 2023 20:44:14 +0200 Subject: [PATCH 110/184] added uuid generation --- app/lib/model/database/SQLiteDatabaseAccess.dart | 5 +++-- app/pubspec.yaml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index e9639776..567ace1c 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -10,6 +10,7 @@ import 'dart:async'; import 'package:flutter/widgets.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; +import 'package:uuid/uuid.dart'; import '../../view_model/repository/data_classes/meal/Allergen.dart'; import '../../view_model/repository/data_classes/mealplan/Line.dart'; @@ -165,8 +166,8 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { Future _insertMealPlan(Mealplan mealPlan) async { var db = await database; - var uuid = ''; // TODO: generate UUID - var dbMealPlan = DBMealPlan(uuid, mealPlan.line.id, mealPlan.date.toString(), mealPlan.isClosed); + var uuid = const Uuid(); // TODO: generate UUID or get uuid from backend + var dbMealPlan = DBMealPlan(uuid.toString(), mealPlan.line.id, mealPlan.date.toString(), mealPlan.isClosed); return db.insert(DBMealPlan.tableName, dbMealPlan.toMap()); } diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 6f4a40d7..fbb420f3 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -46,6 +46,7 @@ dependencies: flutter_svg: ^2.0.7 sqflite: ^2.2.8+4 path: ^1.8.3 + uuid: ^3.0.6 dev_dependencies: flutter_test: From bf626cbe24c91620773516218813c4da15b44cf8 Mon Sep 17 00:00:00 2001 From: Whatsuup Date: Thu, 20 Jul 2023 21:24:13 +0200 Subject: [PATCH 111/184] added more transformer functions --- .../model/database/SQLiteDatabaseAccess.dart | 110 +++++++++++++----- .../model/database/model/database_model.dart | 31 +++++ 2 files changed, 111 insertions(+), 30 deletions(-) diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index 567ace1c..07430fbf 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -14,6 +14,8 @@ import 'package:uuid/uuid.dart'; import '../../view_model/repository/data_classes/meal/Allergen.dart'; import '../../view_model/repository/data_classes/mealplan/Line.dart'; +import '../../view_model/repository/error_handling/NoDataExeption.dart'; +import '../../view_model/repository/error_handling/NoMealException.dart'; import 'model/db_favorite.dart'; import 'model/db_image.dart'; import 'model/db_meal.dart'; @@ -76,9 +78,9 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { @override Future addFavorite(Meal meal) async { var db = await database; - var dbMeal = await _getMeal(meal.id); - var dbMealPlan = await _getMealPlan(dbMeal!.mealPlanID); - var dbLine = await _getLine(dbMealPlan!.lineID); + var dbMeal = await _getDBMeal(meal.id); + var dbMealPlan = await _getDBMealPlan(dbMeal!.mealPlanID); + var dbLine = await _getDBLine(dbMealPlan!.lineID); // FavoriteID is now the related mealID. Seems right. TODO: DateTime to String, if toString() isn't enough. var favorite = DBFavorite(meal.id, dbLine!.lineID, meal.lastServed.toString(), meal.foodType, meal.price.student, meal.price.employee, meal.price.pupil, meal.price.guest); return db.insert( @@ -104,27 +106,28 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { var meals = List.empty(); var dbFavoritesListResult = await db.query(DBFavorite.tableName); for (Map favoriteMap in dbFavoritesListResult) { - var favorite = DBFavorite.fromMap(favoriteMap); - var dbMeal = await _getMeal(favorite.favoriteID); - var allergens = await _getMealAllergens(dbMeal!.mealID); - var additives = await _getMealAdditive(dbMeal.mealID); - var sides = await _getSides(dbMeal.mealID); - var sideAllergens = >{}; - var sideAdditives = >{}; - for (DBSide side in sides) { - sideAllergens[side] = (await _getSideAllergens(side.sideID))!; - sideAdditives[side] = (await _getSideAdditive(side.sideID))!; - } - var images = await _getImages(dbMeal.mealID); - meals.add(DatabaseTransformer.fromDBMeal(dbMeal, allergens!, additives!, sides, sideAllergens, sideAdditives, images, true)); + var dbFavorite = DBFavorite.fromMap(favoriteMap); + var meal = await _getMeal(dbFavorite.favoriteID, true); + meals.add(meal); } return meals; } @override - Future> getMealFavorite(String id) { - // TODO: implement getMealFavorite - throw UnimplementedError(); + Future> getMealFavorite(String id) async { + var db = await database; + var result = await db.query( + DBFavorite.tableName, + where: '${DBFavorite.columnFavoriteID} = ?', + whereArgs: [id] + ); + if(result.isNotEmpty) { + DBFavorite dbFavorite = DBFavorite.fromMap(result.first); + var meal = await _getMeal(dbFavorite.favoriteID, true); + return Success(meal); + } else { + return Failure(result as NoMealException); + } } @override @@ -136,11 +139,33 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { whereArgs: [canteen.id, date.toString()] ); if(result.isNotEmpty) { - return Success(result.map((mealPlanRow) => DBMealPlan.fromMap(mealPlanRow)).cast().toList()); + var mealPlans = List.empty(); + for (DBMealPlan dbMealPlan in result.map((plan) => DBMealPlan.fromMap(plan))) { + var dbLine = await _getDBLine(dbMealPlan.lineID); + var dbCanteen = await _getDBCanteen(dbLine!.canteenID); + var dbMeals = await _getDBMeals(dbMealPlan.mealPlanID); + var meals = List.empty(); + for (DBMeal dbMeal in dbMeals) { + bool isFavorite = await _isFavorite(dbMeal.mealID); + Meal meal = await _getMeal(dbMeal.mealID, isFavorite); + meals.add(meal); + } + mealPlans.add(DatabaseTransformer.fromDBMealPlan(dbMealPlan, dbLine, dbCanteen!, meals)); + } + return Success(mealPlans); } else { - return Failure(result as Exception); + return Failure(result as NoDataException); } + } + Future _isFavorite(String id) async { + var db = await database; + var result = await db.query( + DBFavorite.tableName, + where: '${DBFavorite.columnFavoriteID} = ?', + whereArgs: [id] + ); + return result.isNotEmpty; } @override @@ -152,6 +177,21 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } } + Future _getMeal(String mealID, bool isFavorite) async { + var dbMeal = await _getDBMeal(mealID); + var allergens = await _getMealAllergens(dbMeal!.mealID); + var additives = await _getMealAdditive(dbMeal.mealID); + var sides = await _getDBSides(dbMeal.mealID); + var sideAllergens = >{}; + var sideAdditives = >{}; + for (DBSide side in sides) { + sideAllergens[side] = (await _getSideAllergens(side.sideID))!; + sideAdditives[side] = (await _getSideAdditive(side.sideID))!; + } + var images = await _getDBImages(dbMeal.mealID); + return DatabaseTransformer.fromDBMeal(dbMeal, allergens!, additives!, sides, sideAllergens, sideAdditives, images, isFavorite); + } + Future _insertLine(Line line) async { var db = await database; var dbLine = DBLine(line.id, line.canteen.id, line.name, line.position); @@ -171,7 +211,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { return db.insert(DBMealPlan.tableName, dbMealPlan.toMap()); } - Future _getCanteen(String canteenID) async { + Future _getDBCanteen(String canteenID) async { var db = await database; var result = await db.query( DBCanteen.tableName, @@ -185,7 +225,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } } - Future _getLine(String lineID) async { + Future _getDBLine(String lineID) async { var db = await database; var result = await db.query( DBLine.tableName, @@ -199,7 +239,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } } - Future _getMeal(String mealID) async { + Future _getDBMeal(String mealID) async { var db = await database; var result = await db.query( DBMeal.tableName, @@ -213,7 +253,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } } - Future _getSide(String sideID) async { + Future _getDBSide(String sideID) async { var db = await database; var result = await db.query( DBSide.tableName, @@ -227,7 +267,17 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } } - Future> _getSides(String mealID) async { + Future> _getDBMeals(String mealPlanID) async { + var db = await database; + var result = await db.query( + DBMeal.tableName, + where: '${DBMeal.columnMealPlanID} = ?', + whereArgs: [mealPlanID] + ); + return result.map((mealRow) => DBMeal.fromMap(mealRow)).toList(); + } + + Future> _getDBSides(String mealID) async { var db = await database; var result = await db.query( DBSide.tableName, @@ -237,7 +287,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { return result.map((sideRow) => DBSide.fromMap(sideRow)).toList(); } - Future _getImage(String imageID) async { + Future _getDBImage(String imageID) async { var db = await database; var result = await db.query( DBImage.tableName, @@ -251,7 +301,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } } - Future> _getImages(String mealID) async { + Future> _getDBImages(String mealID) async { var db = await database; var result = await db.query( DBImage.tableName, @@ -261,7 +311,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { return result.map((imageRow) => DBImage.fromMap(imageRow)).toList(); } - Future _getMealPlan(String mealPlanID) async { + Future _getDBMealPlan(String mealPlanID) async { var db = await database; var result = await db.query( DBMealPlan.tableName, @@ -275,7 +325,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } } - Future _getFavorite(String favoriteID) async { + Future _getDBFavorite(String favoriteID) async { var db = await database; var result = await db.query( DBFavorite.tableName, diff --git a/app/lib/model/database/model/database_model.dart b/app/lib/model/database/model/database_model.dart index 35488499..1a24df10 100644 --- a/app/lib/model/database/model/database_model.dart +++ b/app/lib/model/database/model/database_model.dart @@ -1,13 +1,19 @@ import 'package:app/model/database/SQLiteDatabaseAccess.dart'; +import 'package:app/model/database/model/db_canteen.dart'; import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/meal/Price.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Mealplan.dart'; import '../../../view_model/repository/data_classes/meal/Additive.dart'; import '../../../view_model/repository/data_classes/meal/Image.dart'; import '../../../view_model/repository/data_classes/meal/Side.dart'; +import '../../../view_model/repository/data_classes/mealplan/Line.dart'; import 'db_image.dart'; +import 'db_line.dart'; import 'db_meal.dart'; +import 'db_meal_plan.dart'; import 'db_side.dart'; import 'db_side_additive.dart'; import 'db_side_allergen.dart'; @@ -79,4 +85,29 @@ class DatabaseTransformer { negativeRating: image.positiveRating ); } + + static Line fromDBLine(DBLine line, DBCanteen canteen) { + return Line( + id: line.lineID, + name: line.name, + canteen: DatabaseTransformer.fromDBCanteen(canteen), + position: line.position + ); + } + + static Canteen fromDBCanteen(DBCanteen canteen) { + return Canteen( + id: canteen.canteenID, + name: canteen.name + ); + } + + static Mealplan fromDBMealPlan(DBMealPlan plan, DBLine line, DBCanteen canteen, List meals) { + return Mealplan( + date: DateTime.tryParse(plan.date)!, + line: DatabaseTransformer.fromDBLine(line, canteen), + isClosed: plan.isClosed, + meals: meals + ); + } } \ No newline at end of file From 31df343abaa99304b64b4befe04bfcc2ff9bc67f Mon Sep 17 00:00:00 2001 From: Whatsuup Date: Thu, 20 Jul 2023 21:24:47 +0200 Subject: [PATCH 112/184] implemented all interface functions --- app/lib/model/database/SQLiteDatabaseAccess.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index 07430fbf..d7081734 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -378,5 +378,4 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { ); return result.map((allergenMap) => DBSideAdditive.fromMap(allergenMap).additive).toList(); } - } \ No newline at end of file From 6b213d25fd129200f16e1165378f5f398ef3ad6d Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Fri, 21 Jul 2023 08:18:45 +0200 Subject: [PATCH 113/184] implemented MensaAppBar --- app/lib/view/core/MensaAppBar.dart | 42 +++++++----------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/app/lib/view/core/MensaAppBar.dart b/app/lib/view/core/MensaAppBar.dart index d649c629..d5e81ef5 100644 --- a/app/lib/view/core/MensaAppBar.dart +++ b/app/lib/view/core/MensaAppBar.dart @@ -7,50 +7,26 @@ import 'package:provider/provider.dart'; class MensaAppBar extends StatelessWidget implements PreferredSizeWidget { final PreferredSizeWidget? _bottom; + final Widget _child; final double? _appBarHeight; - MensaAppBar({super.key, PreferredSizeWidget? bottom, double? appBarHeight}) + MensaAppBar( + {super.key, + PreferredSizeWidget? bottom, + double? appBarHeight, + required Widget child}) : _appBarHeight = appBarHeight, _bottom = bottom, preferredSize = - _PreferredAppBarSize(appBarHeight, bottom?.preferredSize.height); + _PreferredAppBarSize(appBarHeight, bottom?.preferredSize.height), + _child = child; @override Widget build(BuildContext context) { return SizedBox( height: preferredSize.height, child: Column(children: [ - Consumer( - builder: (context, mealAccess, child) => FutureBuilder( - future: mealAccess.getAvailableCanteens(), - builder: (context, availableCanteens) => FutureBuilder( - future: mealAccess.getCanteen(), - builder: (context, selectedCanteen) { - if (selectedCanteen.hasData) { - return SizedBox( - height: _appBarHeight, - child: MensaDropdown( - backgroundColor: Theme.of(context) - .colorScheme - .background, - onChanged: (canteen) => { - if (canteen != null) - { - mealAccess.changeCanteen(canteen), - } - }, - value: selectedCanteen.requireData, - items: availableCanteens.requireData - .map((canteen) => - MensaDropdownEntry( - value: canteen, - label: canteen.name, - )) - .toList())); - } - return const SizedBox.shrink(); - }), - )), + SizedBox(height: _appBarHeight, child: _child), if (_bottom != null) _bottom! ])); } From c6756a7b479d0520af5445a4c511daeef4409855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 21 Jul 2023 19:21:58 +0200 Subject: [PATCH 114/184] documentation changes --- .../view/core/icons/exceptions/CanteenClosedExceptionIcon.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/view/core/icons/exceptions/CanteenClosedExceptionIcon.dart b/app/lib/view/core/icons/exceptions/CanteenClosedExceptionIcon.dart index 737b21b5..86963455 100644 --- a/app/lib/view/core/icons/exceptions/CanteenClosedExceptionIcon.dart +++ b/app/lib/view/core/icons/exceptions/CanteenClosedExceptionIcon.dart @@ -6,7 +6,7 @@ class CanteenClosedExceptionIcon extends StatelessWidget { final double _size; final Color? _color; - /// Creates an filter icon. + /// Creates a closed canteen icon. /// @param key The key to use for this widget. /// @param size The size of the icon. /// @param color The color of the icon. From e0ce710767fbad9828db76fa3ca26752ac698c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 21 Jul 2023 19:22:38 +0200 Subject: [PATCH 115/184] documentation, add icon and style --- app/lib/view/mealplan/MealPlanClosed.dart | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/app/lib/view/mealplan/MealPlanClosed.dart b/app/lib/view/mealplan/MealPlanClosed.dart index ed19a79b..85f9529f 100644 --- a/app/lib/view/mealplan/MealPlanClosed.dart +++ b/app/lib/view/mealplan/MealPlanClosed.dart @@ -1,17 +1,23 @@ +import 'package:app/view/core/icons/exceptions/CanteenClosedExceptionIcon.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; +/// This widget is used to display the exception for a closed canteen. class MealPlanClosed extends StatelessWidget { - + /// Creates a closed canteen widget. + /// @return a widget that displays the exception for a closed canteen + const MealPlanClosed({super.key}); @override Widget build(BuildContext context) { - return Center( - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - // todo add icon - Text(FlutterI18n.translate( - context, "mealplanException.closedCanteenException")), - ])); + return Column(crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const CanteenClosedExceptionIcon(size: 48), + Text(FlutterI18n.translate( + context, "mealplanException.closedCanteenException"), + style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), + textAlign: TextAlign.center, + ), + ]); } - -} \ No newline at end of file +} From 7593162d0620c7603128ba2000d9983f90ba0473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 21 Jul 2023 19:24:39 +0200 Subject: [PATCH 116/184] documentation, add icon and style --- app/lib/view/mealplan/MealPlanNoData.dart | 24 +++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/app/lib/view/mealplan/MealPlanNoData.dart b/app/lib/view/mealplan/MealPlanNoData.dart index 31a7cac5..b23568b1 100644 --- a/app/lib/view/mealplan/MealPlanNoData.dart +++ b/app/lib/view/mealplan/MealPlanNoData.dart @@ -1,15 +1,23 @@ +import 'package:app/view/core/icons/exceptions/NoDataExceptionIcon.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; +/// This widget is used to display the exception for no available data on the server. class MealPlanNoData extends StatelessWidget { + /// Creates a no data widget + /// @return a widget that displays the exeption for no available data on the server + const MealPlanNoData({super.key}); + @override Widget build(BuildContext context) { - return Center( - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - // todo add icon - Text(FlutterI18n.translate( - context, "mealplanException.noDataException")), - ])); + return Column(crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const NoDataExceptionIcon(size: 48), + Text( + FlutterI18n.translate(context, "mealplanException.noDataException"), + style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), + textAlign: TextAlign.center, + ), + ]); } - -} \ No newline at end of file +} From 00abf4a918eb203a23b5c2de20275c80f89b2ccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 21 Jul 2023 19:30:35 +0200 Subject: [PATCH 117/184] add icon and text style --- app/lib/view/mealplan/MealPlanError.dart | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/app/lib/view/mealplan/MealPlanError.dart b/app/lib/view/mealplan/MealPlanError.dart index 9a47ad2d..d6449e51 100644 --- a/app/lib/view/mealplan/MealPlanError.dart +++ b/app/lib/view/mealplan/MealPlanError.dart @@ -1,40 +1,41 @@ import 'package:app/view/core/buttons/MensaButton.dart'; +import 'package:app/view/core/icons/exceptions/ErrorExceptionIcon.dart'; import 'package:app/view_model/logic/meal/IMealAccess.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; -// todo text style class MealPlanError extends StatelessWidget { - String _temporalMessage = ""; - MealPlanError({super.key}); + const MealPlanError({super.key}); @override Widget build(BuildContext context) { return Consumer( - builder: (context, mealAccess, child) => Center( - child: Column( + builder: (context, mealAccess, child) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // todo add icon + const ErrorExceptionIcon(size: 48), Text(FlutterI18n.translate( - context, "mealplanException.noConnectionException")), + context, "mealplanException.noConnectionException"), + style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), + textAlign: TextAlign.center, + ), MensaButton( onPressed: () async { // Mach das einfach als lokale Variable - _temporalMessage = + final temporalMessage = await mealAccess.refreshMealplan() ?? ""; - if (_temporalMessage.isNotEmpty) { + if (temporalMessage.isNotEmpty) { final snackBar = SnackBar( content: Text(FlutterI18n.translate( - context, _temporalMessage)), + context, temporalMessage)), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); } }, text: "mealplanException.noConnectionButton"), - ]))); + ])); } } From 812ea3c4495676444b0ca14d8fba238b22279798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 21 Jul 2023 19:42:58 +0200 Subject: [PATCH 118/184] create class --- app/lib/view/settings/Settings.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/lib/view/settings/Settings.dart b/app/lib/view/settings/Settings.dart index e69de29b..be36bdff 100644 --- a/app/lib/view/settings/Settings.dart +++ b/app/lib/view/settings/Settings.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class Settings extends StatefulWidget { + const Settings({super.key}); + + @override + State createState() => _SettingsState(); +} + +class _SettingsState extends State { + + + @override + Widget build(BuildContext context) { + // TODO: implement build + throw UnimplementedError(); + } + +} \ No newline at end of file From f4e51d5b6813c1067daf3964accbe831a1a2ee99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 21 Jul 2023 21:41:21 +0200 Subject: [PATCH 119/184] activate and deactivate filter implementation --- .../logic/meal/CombinedMealPlanAccess.dart | 34 +++++++++++++++++-- .../view_model/logic/meal/IMealAccess.dart | 8 +++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 6c511dc5..f3fb037c 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -22,10 +22,12 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { late Canteen _activeCanteen; late DateTime _displayedDate; - late List _mealPlans = []; late List _filteredMealPlan; late FilterPreferences _filter; - late bool _noDataYet = false; + List _mealPlans = []; + bool _noDataYet = false; + bool _activeFilter = true; + late PriceCategory _priceCategory; @@ -189,6 +191,10 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { return Future.value(Failure(ClosedCanteenException("canteen closed"))); } + if (!_activeFilter) { + return Future.value(Success(_mealPlans)); + } + // everything is filtered if (_filteredMealPlan.isEmpty) { return Future.value(Failure(FilteredMealException("all filtered"))); @@ -270,6 +276,8 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { @override Future switchToMealPlanView() async { + await _doneInitialization; + bool changed = await _updateFavorites(); final category = await _preferences.getPriceCategory(); @@ -286,6 +294,28 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { } } + @override + Future activateFilter() async { + await _doneInitialization; + + _activeFilter = true; + notifyListeners(); + } + + @override + Future deactivateFilter() async { + await _doneInitialization; + + for (final mealPlan in _mealPlans) { + if (mealPlan.isClosed && _mealPlans.length > 1) { + _mealPlans.remove(mealPlan); + } + } + + _activeFilter = false; + notifyListeners(); + } + void _changeRatingOfMeal(Meal changedMeal, int rating) { for (final mealPlan in _mealPlans) { // check if right meal plan diff --git a/app/lib/view_model/logic/meal/IMealAccess.dart b/app/lib/view_model/logic/meal/IMealAccess.dart index 2855ca71..b64c5a2d 100644 --- a/app/lib/view_model/logic/meal/IMealAccess.dart +++ b/app/lib/view_model/logic/meal/IMealAccess.dart @@ -76,4 +76,12 @@ abstract class IMealAccess with ChangeNotifier { /// This method checks if settings or favorites are changed since the last time the mealplan was displayed. /// If they were changed it corrects the displayed data if needed. Future switchToMealPlanView(); + + /// This method activates the filter. + /// @return The result of the update + Future activateFilter(); + + /// This method deactivates the filter. + /// @return The result of the update + Future deactivateFilter(); } From b008ed51d0e4aac7785ec26bb63f166418234e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 21 Jul 2023 21:45:47 +0200 Subject: [PATCH 120/184] activate and deactivate filter implementation; change nothing if already activated or deactivated --- app/test/view-model/MealPlanAccessTest.dart | 47 +++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/app/test/view-model/MealPlanAccessTest.dart b/app/test/view-model/MealPlanAccessTest.dart index dce6cb13..b8bf33e8 100644 --- a/app/test/view-model/MealPlanAccessTest.dart +++ b/app/test/view-model/MealPlanAccessTest.dart @@ -381,6 +381,51 @@ void main() { expect(returnedMealPlan[1].meals[0].sides?.length, 4); }); + test("deactivate", () async { + filter.onlyFavorite = true; + await mealPlanAccess.changeFilterPreferences(filter); + + await mealPlanAccess.deactivateFilter(); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + expect(returnedMealPlan, mealplans); + }); + + test("activate", () async { + filter.onlyFavorite = true; + await mealPlanAccess.changeFilterPreferences(filter); + + await mealPlanAccess.activateFilter(); + + final List returnedMealPlan = switch ( + await mealPlanAccess.getMealPlan()) { + Success(value: final value) => value, + Failure(exception: _) => [] + }; + + // first meal plan + expect(returnedMealPlan[0].meals[0], meals[0]); + expect(returnedMealPlan[0].meals[1], meals[1]); + // sides first meal plan + expect(returnedMealPlan[0].meals[0].sides?.length, 1); + expect(returnedMealPlan[0].meals[1].sides?.length, 2); + + // second meal plan of [mealplans] + // -> should be emplty + expect(returnedMealPlan.length, 2); + + // third meal plan + expect(returnedMealPlan[1].meals.length, 1); + expect(returnedMealPlan[1].meals[0], meals[3]); + // sides third meal plan + expect(returnedMealPlan[1].meals[0].sides?.length, 4); + }); + test("all", () async { filter.onlyFavorite = false; await mealPlanAccess.changeFilterPreferences(filter); @@ -558,7 +603,9 @@ void main() { expect(returnedMealPlan[0].meals[0].sides?[0], sides[3]); }); }); + }); + group("reset", () { test("reset filter preferences", () async { filter = FilterPreferences(); when(() => localStorage.setFilterPreferences(filter)) From f63d51eabf29521d81143b63461fc25b53fbda97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 21 Jul 2023 21:46:15 +0200 Subject: [PATCH 121/184] activate and deactivate filter implementation; change nothing if already activated or deactivated --- app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index f3fb037c..858870a1 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -298,6 +298,10 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { Future activateFilter() async { await _doneInitialization; + if (_activeFilter) { + return; + } + _activeFilter = true; notifyListeners(); } @@ -306,6 +310,10 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { Future deactivateFilter() async { await _doneInitialization; + if (!_activeFilter) { + return; + } + for (final mealPlan in _mealPlans) { if (mealPlan.isClosed && _mealPlans.length > 1) { _mealPlans.remove(mealPlan); From 24934cdda8ddefe1fbf87185ed628e6a7405ec24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 21 Jul 2023 22:52:35 +0200 Subject: [PATCH 122/184] strings for settings --- app/assets/locales/de/mensaColorScheme.json | 5 +++++ app/assets/locales/de/priceCategory.json | 6 ++++++ app/assets/locales/de/settings.json | 11 +++++++++++ app/lib/main.dart | 13 ++++++++----- 4 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 app/assets/locales/de/mensaColorScheme.json create mode 100644 app/assets/locales/de/priceCategory.json create mode 100644 app/assets/locales/de/settings.json diff --git a/app/assets/locales/de/mensaColorScheme.json b/app/assets/locales/de/mensaColorScheme.json new file mode 100644 index 00000000..11344d5b --- /dev/null +++ b/app/assets/locales/de/mensaColorScheme.json @@ -0,0 +1,5 @@ +{ + "light": "hell", + "dark": "dunkel", + "system": "systemabhängig" +} \ No newline at end of file diff --git a/app/assets/locales/de/priceCategory.json b/app/assets/locales/de/priceCategory.json new file mode 100644 index 00000000..b6eb5281 --- /dev/null +++ b/app/assets/locales/de/priceCategory.json @@ -0,0 +1,6 @@ +{ + "student": "Studierende", + "employee": "Mitarbeitende", + "pupil": "Schüler:innen", + "guest": "Gäste" +} \ No newline at end of file diff --git a/app/assets/locales/de/settings.json b/app/assets/locales/de/settings.json new file mode 100644 index 00000000..8e64ce21 --- /dev/null +++ b/app/assets/locales/de/settings.json @@ -0,0 +1,11 @@ +{ + "colorScheme": "Farbschema", + "priceCategory": "Preiskategorie", + "about": "Über die App", + "version": "Softwareversion", + "licence": "Diese Software ist unter MIT Lizenz Open Source verfügbar.", + "gitHubLink": "Auf Github ansehen", + "legalInformation": "Rechtliche Informationen", + "privacyPolicy": "Datenschutzerklärung", + "contactDetails": "Kontakt" +} \ No newline at end of file diff --git a/app/lib/main.dart b/app/lib/main.dart index 0e105810..64cb45af 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -5,11 +5,14 @@ import 'package:flutter_localizations/flutter_localizations.dart'; void main() { final FlutterI18nDelegate delegate = FlutterI18nDelegate( - translationLoader: NamespaceFileTranslationLoader( - namespaces: ["common", "ratings"], - useCountryCode: false, - basePath: 'assets/locales', - fallbackDir: 'de'), + translationLoader: NamespaceFileTranslationLoader(namespaces: [ + "common", + "ratings", + "snackbar", + "settings", + "priceCategory", + "mensaColorScheme" + ], useCountryCode: false, basePath: 'assets/locales', fallbackDir: 'de'), missingTranslationHandler: (key, locale) { if (kDebugMode) { print("--- Missing Key: $key, languageCode: ${locale!.languageCode}"); From 3bf50224b06071e3158d76145791d959b9506eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 21 Jul 2023 23:25:25 +0200 Subject: [PATCH 123/184] strings for filter, images, report reasons, title and ratings --- app/assets/locales/de/common.json | 5 ++++- app/assets/locales/de/filter.json | 24 ++++++++++++++++++++++++ app/assets/locales/de/image.json | 13 +++++++++++++ app/assets/locales/de/ratings.json | 3 +++ app/assets/locales/de/reportReason.json | 8 ++++++++ app/lib/main.dart | 5 ++++- 6 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 app/assets/locales/de/filter.json create mode 100644 app/assets/locales/de/image.json create mode 100644 app/assets/locales/de/reportReason.json diff --git a/app/assets/locales/de/common.json b/app/assets/locales/de/common.json index 5771c35d..98adfae8 100644 --- a/app/assets/locales/de/common.json +++ b/app/assets/locales/de/common.json @@ -1,3 +1,6 @@ { - "demo": "Flutter Demo Startseite" + "demo": "Flutter Demo Startseite", + "settings": "Einstellungen", + "favorites": "Favoriten", + "mealPlan": "Speiseplan" } \ No newline at end of file diff --git a/app/assets/locales/de/filter.json b/app/assets/locales/de/filter.json new file mode 100644 index 00000000..817304f7 --- /dev/null +++ b/app/assets/locales/de/filter.json @@ -0,0 +1,24 @@ +{ + "filterTitle": "Filter", + "foodType": "Gerichtstyp", + "foodTypeSectionAll": "Alles", + "foodTypeSectionVegetarian": "Vegetarisch", + "foodTypeSectionVegan": "Vegan", + "foodTypeSelectionUnknown": "Nicht Spezifiziert", + "foodTypeSelectionPork": "Schweinefleisch", + "foodTypeSelectionCow": "Rindfleisch", + "foodTypeSelectionFisch": "Fisch", + + "allergensTitle": "Allergene", + "priceTitle": "Preis", + "ratingTitle": "Bewertung", + "favoritesOnlyTitle": "Nur Favoriten anzeigen", + "sortByTitle": "Sortiert nach", + + "frequencyTitle": "Häufigkeit", + "frequencySectionAll": "Alles", + "frequencySectionRare": "Selten", + "frequencySectionNew": "Neu", + + "storeButton": "Übernehmen" +} \ No newline at end of file diff --git a/app/assets/locales/de/image.json b/app/assets/locales/de/image.json new file mode 100644 index 00000000..ccf29fcd --- /dev/null +++ b/app/assets/locales/de/image.json @@ -0,0 +1,13 @@ +{ + "reportImageTitle": "Bild melden", + "reportDescription": "Bitte gib an, warum das Bild entfernt werder sollte.", + "reportSelectReson": "Grund wählen", + "reportButton": "Bild melden", + + "linkImageTitle": "Bild hochladen", + "linkDescription": "Bitte befolge die folgenden Schirtte, um ein Bild hinzufügen zu können.", + "linkTextFieldDescription": "Flickr Link zu Bild", + "linkSmallTextOwnImages": "Es dürfen nur eigene Bilder hochgeladen werden.", + "linkSmallTextDisplayed": "Die hochgeladenen Bilder werden anderen Nutzern angezeigt.", + "linkButton": "Hochladen" +} \ No newline at end of file diff --git a/app/assets/locales/de/ratings.json b/app/assets/locales/de/ratings.json index 10ea5760..b100bd08 100644 --- a/app/assets/locales/de/ratings.json +++ b/app/assets/locales/de/ratings.json @@ -1,4 +1,7 @@ { "titleRatings": "Bewertungen", + "usersRating": "Deine Bewertung", + "editRating": "Bewertung bearbeiten", + "saveRating": "Bewertung speichern", "labelRatingsCount": "Bewertungen" } \ No newline at end of file diff --git a/app/assets/locales/de/reportReason.json b/app/assets/locales/de/reportReason.json new file mode 100644 index 00000000..0da548ed --- /dev/null +++ b/app/assets/locales/de/reportReason.json @@ -0,0 +1,8 @@ +{ + "offensive": "Anstößig", + "advert": "Werbung", + "noMeal": "kein Gericht", + "wrongMeal": "falsches Gericht", + "violatesRights": "Verletzt meine Rechte", + "other": "Sonstiges" +} \ No newline at end of file diff --git a/app/lib/main.dart b/app/lib/main.dart index 64cb45af..b1fde064 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -11,7 +11,10 @@ void main() { "snackbar", "settings", "priceCategory", - "mensaColorScheme" + "mensaColorScheme", + "filter", + "image", + "reportReason" ], useCountryCode: false, basePath: 'assets/locales', fallbackDir: 'de'), missingTranslationHandler: (key, locale) { if (kDebugMode) { From a3816d3b41b976b5eaf5ef74c3a62717df6d41a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sat, 22 Jul 2023 07:19:00 +0200 Subject: [PATCH 124/184] strings for allergen and additive --- app/assets/locales/de/additive.json | 18 +++++++++++++++++ app/assets/locales/de/allergen.json | 31 +++++++++++++++++++++++++++++ app/lib/main.dart | 4 +++- 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 app/assets/locales/de/additive.json create mode 100644 app/assets/locales/de/allergen.json diff --git a/app/assets/locales/de/additive.json b/app/assets/locales/de/additive.json new file mode 100644 index 00000000..f5b998f8 --- /dev/null +++ b/app/assets/locales/de/additive.json @@ -0,0 +1,18 @@ +{ + "additiveTitle": "Zusatzstoffe", + "colorant": "mit Farbstoff", + "preservingAgents": "mit Konservierungsstoff", + "antioxidantAgents": "mit Antioxidationsmittel", + "flavourEnhancer": "mit Geschmacksverstärker", + "phosphate": "mit Phosphat", + "surfaceWaxed": "Oberfläache gewachst", + "sulphur": "geschwefelt", + "artificiallyBlackenedOlives": "Oliven geschwärzt", + "sweetener": "mit Süßungsmittel", + "laxativeIfOverused": "kann bei übermäßigen Verzehr abführend wirken", + "phenylalanine": "enthält eine Phenylalaninquelle", + "alcohol": "kann Restalkohol enthalten", + "pressedMeat": "aus Fleischstücken zusammengefügt", + "glazingWithCacao": "mit kakaohaltigen Fettglasur", + "pressedFish": "auf Fischstücken zusammengefügt" +} \ No newline at end of file diff --git a/app/assets/locales/de/allergen.json b/app/assets/locales/de/allergen.json new file mode 100644 index 00000000..dc207ac9 --- /dev/null +++ b/app/assets/locales/de/allergen.json @@ -0,0 +1,31 @@ +{ + "allergenTitle": "Allergene:", + "ca": "Cashewnüsse", + "di": "Dinkel und Gluten aus Dinkel", + "ei": "Eier", + "er": "Erdnüsse", + "fi": "Fisch", + "ge": "Gerste und Gluten aus Gerste", + "hf": "Hafer und Gluten aus Hafer", + "ha": "Haselnüsse", + "ka": "Kamut und Gluten aus Kamut", + "kr": "Krebstiere", + "lu": "Lupine", + "ma": "Mandeln", + "ml": "Milch / Laktose", + "pa": "Paranüsse", + "pe": "Pekannüsse", + "pl": "Pistazie", + "qu": "Queenslandnüsse / Macadamianüsse", + "ro": "Roggen und Gluten aus Roggen", + "sa": "Sesam", + "se": "Sellerie", + "sf": "Schwefeldioxid / Sulfit", + "sn": "Senf", + "so": "Soja", + "wa": "Walnüsse", + "we": "Weizen und Gluten aus Weizen", + "wt": "Weichtiere", + "la": "mit tierischem Lab", + "gl": "mit Gelatine" +} \ No newline at end of file diff --git a/app/lib/main.dart b/app/lib/main.dart index b1fde064..b0c4eca1 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -14,7 +14,9 @@ void main() { "mensaColorScheme", "filter", "image", - "reportReason" + "reportReason", + "additive", + "allergen" ], useCountryCode: false, basePath: 'assets/locales', fallbackDir: 'de'), missingTranslationHandler: (key, locale) { if (kDebugMode) { From de0256656a38814b33a5d03e3e756a4a64e9180a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Sun, 23 Jul 2023 12:15:09 +0200 Subject: [PATCH 125/184] change one value on request --- app/assets/locales/de/filter.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/locales/de/filter.json b/app/assets/locales/de/filter.json index 817304f7..17dc4f92 100644 --- a/app/assets/locales/de/filter.json +++ b/app/assets/locales/de/filter.json @@ -4,7 +4,7 @@ "foodTypeSectionAll": "Alles", "foodTypeSectionVegetarian": "Vegetarisch", "foodTypeSectionVegan": "Vegan", - "foodTypeSelectionUnknown": "Nicht Spezifiziert", + "foodTypeSelectionUnknown": "Keine Angabe", "foodTypeSelectionPork": "Schweinefleisch", "foodTypeSelectionCow": "Rindfleisch", "foodTypeSelectionFisch": "Fisch", From 94937b2e3011cfee58aba56b57c0369ec5e79ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Mon, 24 Jul 2023 16:16:25 +0200 Subject: [PATCH 126/184] implementation --- app/lib/view/mealplan/MealPlanFilter.dart | 26 +++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/app/lib/view/mealplan/MealPlanFilter.dart b/app/lib/view/mealplan/MealPlanFilter.dart index 5e6aeddf..4708a30d 100644 --- a/app/lib/view/mealplan/MealPlanFilter.dart +++ b/app/lib/view/mealplan/MealPlanFilter.dart @@ -1,10 +1,32 @@ +import 'package:app/view/core/buttons/MensaButton.dart'; +import 'package:app/view/core/icons/exceptions/ErrorExceptionIcon.dart'; +import 'package:app/view_model/logic/meal/IMealAccess.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:provider/provider.dart'; +/// This widget is used to display that no meal reaches the filter preferences. class MealPlanFilter extends StatelessWidget { + /// Creates a no filtered meal widget. + /// @return a widget that displays the exception that says that no meal reaches the filter preferences + const MealPlanFilter({super.key}); + @override Widget build(BuildContext context) { - // TODO: implement build - throw UnimplementedError(); + return Consumer( + builder: (context, mealAccess, child) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ErrorExceptionIcon(size: 48), + Text(FlutterI18n.translate( + context, "mealplanException.noConnectionException"), + style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), + textAlign: TextAlign.center, + ), + MensaButton( + onPressed: () => mealAccess.deactivateFilter(), + text: "mealplanException.noConnectionButton"), + ])); } } \ No newline at end of file From 9f884954f4f96bb5d18f4841c7b5e929744df65e Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Mon, 24 Jul 2023 23:20:59 +0200 Subject: [PATCH 127/184] implemented First Draft of Mealplan --- app/android/build.gradle | 2 +- .../icons/navigation/filter_outlined.svg | 1 + app/assets/icons/navigation/grid_outlined.svg | 1 + app/assets/icons/navigation/list_outlined.svg | 1 + app/lib/main.dart | 265 +++++++++++------ .../model/database/SQLiteDatabaseAccess.dart | 270 ++++++++---------- .../model/database/model/database_model.dart | 17 +- app/lib/model/database/model/db_canteen.dart | 2 +- app/lib/model/database/model/db_favorite.dart | 4 +- app/lib/model/database/model/db_image.dart | 2 +- app/lib/model/database/model/db_line.dart | 2 +- app/lib/model/database/model/db_meal.dart | 6 +- .../database/model/db_meal_additive.dart | 4 +- .../database/model/db_meal_allergen.dart | 4 +- .../model/database/model/db_meal_plan.dart | 2 +- app/lib/model/database/model/db_side.dart | 4 +- .../database/model/db_side_additive.dart | 4 +- .../database/model/db_side_allergen.dart | 4 +- .../local_storage/SharedPreferenceAccess.dart | 4 +- app/lib/view/core/MensaAppBar.dart | 13 +- .../NavigationFilterOutlinedIcon.dart | 23 ++ .../NavigationGridOutlinedIcon.dart | 23 ++ .../NavigationListOutlinedIcon.dart | 23 ++ .../information_display/MealPreviewImage.dart | 2 +- .../input_components/MensaRatingInput.dart | 83 +++--- .../view/core/meal_view_format/MealGrid.dart | 2 +- .../core/meal_view_format/MealGridEntry.dart | 7 +- app/lib/view/mealplan/MealPlanToolbar.dart | 30 ++ .../logic/favorite/IFavoriteMealAccess.dart | 4 +- .../view_model/logic/image/IImageAccess.dart | 3 +- .../logic/preference/IPreferenceAccess.dart | 4 +- .../Flutter/GeneratedPluginRegistrant.swift | 2 + app/pubspec.lock | 8 +- 33 files changed, 496 insertions(+), 330 deletions(-) create mode 100644 app/assets/icons/navigation/filter_outlined.svg create mode 100644 app/assets/icons/navigation/grid_outlined.svg create mode 100644 app/assets/icons/navigation/list_outlined.svg create mode 100644 app/lib/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart create mode 100644 app/lib/view/core/icons/navigation/NavigationGridOutlinedIcon.dart create mode 100644 app/lib/view/core/icons/navigation/NavigationListOutlinedIcon.dart create mode 100644 app/lib/view/mealplan/MealPlanToolbar.dart diff --git a/app/android/build.gradle b/app/android/build.gradle index 58a8c74b..713d7f6e 100644 --- a/app/android/build.gradle +++ b/app/android/build.gradle @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/app/assets/icons/navigation/filter_outlined.svg b/app/assets/icons/navigation/filter_outlined.svg new file mode 100644 index 00000000..631f0eac --- /dev/null +++ b/app/assets/icons/navigation/filter_outlined.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/navigation/grid_outlined.svg b/app/assets/icons/navigation/grid_outlined.svg new file mode 100644 index 00000000..b7680f4a --- /dev/null +++ b/app/assets/icons/navigation/grid_outlined.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/navigation/list_outlined.svg b/app/assets/icons/navigation/list_outlined.svg new file mode 100644 index 00000000..38c0f6e0 --- /dev/null +++ b/app/assets/icons/navigation/list_outlined.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/lib/main.dart b/app/lib/main.dart index b0c4eca1..c487b7b0 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,7 +1,35 @@ +import 'package:app/model/api_server/GraphQlServerAccess.dart'; +import 'package:app/model/api_server/config.dart'; +import 'package:app/model/database/SQLiteDatabaseAccess.dart'; +import 'package:app/model/local_storage/SharedPreferenceAccess.dart'; +import 'package:app/view/core/MensaAppBar.dart'; +import 'package:app/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart'; +import 'package:app/view/core/icons/navigation/NavigationListOutlinedIcon.dart'; +import 'package:app/view/core/meal_view_format/MealGrid.dart'; +import 'package:app/view/core/selection_components/MensaDropdown.dart'; +import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; +import 'package:app/view/mealplan/MealPlanDateSelect.dart'; +import 'package:app/view/mealplan/MealPlanToolbar.dart'; +import 'package:app/view_model/logic/favorite/FavoriteMealAccess.dart'; +import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; +import 'package:app/view_model/logic/image/IImageAccess.dart'; +import 'package:app/view_model/logic/image/ImageAccess.dart'; +import 'package:app/view_model/logic/meal/CombinedMealPlanAccess.dart'; +import 'package:app/view_model/logic/meal/IMealAccess.dart'; +import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; +import 'package:app/view_model/logic/preference/PreferenceAccess.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; +import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; +import 'package:app/view_model/repository/error_handling/Result.dart'; +import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; +import 'package:app/view_model/repository/interface/ILocalStorage.dart'; +import 'package:app/view_model/repository/interface/IServerAccess.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; void main() { final FlutterI18nDelegate delegate = FlutterI18nDelegate( @@ -40,49 +68,81 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Mensa App', - localizationsDelegates: [ - _delegate, - ...GlobalMaterialLocalizations.delegates, - GlobalWidgetsLocalizations.delegate, - ], - supportedLocales: const [ - Locale('de'), - ], - theme: ThemeData( - brightness: Brightness.light, - colorScheme: const ColorScheme( - brightness: Brightness.light, - primary: Color(0xFF7AAC2B), - onPrimary: Color(0xFFFFFFFF), - secondary: Color(0xFF7AAC2B), - onSecondary: Color(0xFFFFFFFF), - error: Color(0xFFD32F2F), - onError: Color(0xFFFFFFFF), - background: Color(0xFFFFFFFF), - onBackground: Color(0xFF000000), - surface: Color(0xFFF6F6F6), - onSurface: Color(0xFF000000)), - ), - darkTheme: ThemeData( - brightness: Brightness.dark, - colorScheme: const ColorScheme( - brightness: Brightness.dark, - primary: Color(0xFF7AAC2B), - onPrimary: Color(0xFFFFFFFF), - secondary: Color(0xFF7AAC2B), - onSecondary: Color(0xFFFFFFFF), - error: Color(0xFFD32F2F), - onError: Color(0xFFFFFFFF), - background: Color(0xFF1E1E1E), - onBackground: Color(0xFFFFFFFF), - surface: Color(0xFF333333), - surfaceTint: Color(0xFF202020), - onSurface: Color(0xFFFFFFFF)), - ), - home: const MyHomePage(), - ); + return FutureBuilder( + future: SharedPreferences.getInstance(), + builder: (context, sharedPreferences) { + ILocalStorage sharedPreferencesAccess = + SharedPreferenceAccess(sharedPreferences.requireData); + return FutureBuilder( + future: sharedPreferencesAccess.getClientIdentifier(), + builder: (context, clientIdentifier) { + IDatabaseAccess db = SQLiteDatabaseAccess(); + IServerAccess api = GraphQlServerAccess( + testServer, testApiKey, clientIdentifier.data.toString()); + return MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (context) => CombinedMealPlanAccess( + sharedPreferencesAccess, + api, + db, + )), + ChangeNotifierProvider( + create: (context) => FavoriteMealAccess(db)), + ChangeNotifierProvider( + create: (context) => + PreferenceAccess(sharedPreferencesAccess)), + ChangeNotifierProvider( + create: (context) => ImageAccess(api)), + ], + child: MaterialApp( + title: 'Mensa App', + localizationsDelegates: [ + _delegate, + ...GlobalMaterialLocalizations.delegates, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: const [ + Locale('de'), + ], + theme: ThemeData( + useMaterial3: true, + brightness: Brightness.light, + colorScheme: const ColorScheme( + brightness: Brightness.light, + primary: Color(0xFF7AAC2B), + onPrimary: Color(0xFFFFFFFF), + secondary: Color(0xFF7AAC2B), + onSecondary: Color(0xFFFFFFFF), + error: Color(0xFFD32F2F), + onError: Color(0xFFFFFFFF), + background: Color(0xFFFFFFFF), + onBackground: Color(0xFF000000), + surface: Color(0xFFF6F6F6), + onSurface: Color(0xFF000000)), + ), + darkTheme: ThemeData( + useMaterial3: true, + brightness: Brightness.dark, + colorScheme: const ColorScheme( + brightness: Brightness.dark, + primary: Color(0xFF7AAC2B), + onPrimary: Color(0xFFFFFFFF), + secondary: Color(0xFF7AAC2B), + onSecondary: Color(0xFFFFFFFF), + error: Color(0xFFD32F2F), + onError: Color(0xFFFFFFFF), + background: Color(0xFF1E1E1E), + onBackground: Color(0xFFFFFFFF), + surface: Color(0xFF333333), + surfaceTint: Color(0xFF202020), + onSurface: Color(0xFFFFFFFF)), + ), + home: const MyHomePage(), + ), + ); + }); + }); } } @@ -103,18 +163,6 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } @override Widget build(BuildContext context) { @@ -124,41 +172,80 @@ class _MyHomePageState extends State { // The Flutter framework has been optimized to make rerunning build methods // fast, so that you can just rebuild anything that needs updating rather // than having to individually change instances of widgets. - return Scaffold( + return SafeArea( + child: Scaffold( backgroundColor: Theme.of(context).colorScheme.background, - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - I18nText("common.demo"), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, + appBar: MensaAppBar( + bottom: MealPlanToolbar( + child: Row( + children: [ + NavigationListOutlinedIcon(), + Spacer(), + Consumer( + builder: (context, mealAccess, child) => StreamBuilder( + stream: mealAccess.getDate().asStream(), + builder: (context, date) { + return MealPlanDateSelect( + date: date.requireData, + onDateChanged: (date) => mealAccess.changeDate(date), + ); + })), + Spacer(), + NavigationFilterOutlinedIcon(), + ], + )), + child: Consumer( + builder: (context, mealAccess, child) => StreamBuilder( + stream: mealAccess.getAvailableCanteens().asStream(), + builder: (context, availableCanteens) => StreamBuilder( + stream: mealAccess.getCanteen().asStream(), + builder: (context, selectedCanteen) { + if (!availableCanteens.hasData || !selectedCanteen.hasData) + return const Text("Loading..."); + if (availableCanteens.hasError) + return Text(availableCanteens.error.toString()); + if (selectedCanteen.hasError) + return Text(selectedCanteen.error.toString()); + return MensaDropdown( + backgroundColor: + Theme.of(context).colorScheme.background, + onChanged: (v) { + mealAccess.changeCanteen(availableCanteens.requireData + .firstWhere((element) => element.id == v)); + }, + value: selectedCanteen.requireData.id, + items: availableCanteens.requireData + .map((e) => MensaDropdownEntry( + value: e.id, + label: e.name, + )) + .toList()); + }), ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); + )), + body: Consumer( + builder: (context, mealAccess, child) => RefreshIndicator( + onRefresh: () async { + String? error = await mealAccess.refreshMealplan(); + if (error != null) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: I18nText(error))); + } + }, + child: StreamBuilder( + stream: mealAccess.getMealPlan().asStream(), + builder: (context, mealPlans) { + if (!mealPlans.hasData) return const Text("Loading..."); + if (mealPlans.hasError) { + return Text(mealPlans.error.toString()); + } + switch (mealPlans.requireData) { + case Success, MealPlanException> value: + return MealGrid(mealPlans: value.value); + case Failure, MealPlanException> exception: + return Text(exception.exception.toString()); + } + }))), + )); } } diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index d7081734..d3c11c28 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -1,12 +1,13 @@ +import 'dart:async'; + import 'package:app/model/database/model/database_model.dart'; import 'package:app/view_model/repository/data_classes/meal/Additive.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; -import 'package:app/view_model/repository/data_classes/mealplan/Mealplan.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; +import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; -import 'dart:async'; - import 'package:flutter/widgets.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; @@ -14,24 +15,20 @@ import 'package:uuid/uuid.dart'; import '../../view_model/repository/data_classes/meal/Allergen.dart'; import '../../view_model/repository/data_classes/mealplan/Line.dart'; -import '../../view_model/repository/error_handling/NoDataExeption.dart'; import '../../view_model/repository/error_handling/NoMealException.dart'; +import 'model/db_canteen.dart'; import 'model/db_favorite.dart'; import 'model/db_image.dart'; +import 'model/db_line.dart'; import 'model/db_meal.dart'; import 'model/db_meal_additive.dart'; import 'model/db_meal_allergen.dart'; -import 'model/db_side.dart'; -import 'model/db_canteen.dart'; import 'model/db_meal_plan.dart'; -import 'model/db_line.dart'; +import 'model/db_side.dart'; import 'model/db_side_additive.dart'; import 'model/db_side_allergen.dart'; - - class SQLiteDatabaseAccess implements IDatabaseAccess { - static List _getDatabaseBuilder() { return [ DBCanteen.initTable(), @@ -49,7 +46,8 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } // Database access is provided by a singleton instance to prevent several databases. - static final SQLiteDatabaseAccess _databaseAccess = SQLiteDatabaseAccess._internal(); + static final SQLiteDatabaseAccess _databaseAccess = + SQLiteDatabaseAccess._internal(); factory SQLiteDatabaseAccess() { return _databaseAccess; @@ -62,16 +60,16 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { static const String _dbName = 'meal_plan.db'; late final Future database; - static Future _initiate() async { + static Future _initiate() async { WidgetsFlutterBinding.ensureInitialized(); return await openDatabase( - join(await getDatabasesPath(), _dbName), - onCreate: (db, version) { - for (String sql in _getDatabaseBuilder()) { - db.execute(sql); - } - }, - version: 1, + join(await getDatabasesPath(), _dbName), + onCreate: (db, version) { + for (String sql in _getDatabaseBuilder()) { + db.execute(sql); + } + }, + version: 1, ); } @@ -82,22 +80,25 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { var dbMealPlan = await _getDBMealPlan(dbMeal!.mealPlanID); var dbLine = await _getDBLine(dbMealPlan!.lineID); // FavoriteID is now the related mealID. Seems right. TODO: DateTime to String, if toString() isn't enough. - var favorite = DBFavorite(meal.id, dbLine!.lineID, meal.lastServed.toString(), meal.foodType, meal.price.student, meal.price.employee, meal.price.pupil, meal.price.guest); - return db.insert( - DBFavorite.tableName, - favorite.toMap() - ); + var favorite = DBFavorite( + meal.id, + dbLine!.lineID, + meal.lastServed.toString(), + meal.foodType, + meal.price.student, + meal.price.employee, + meal.price.pupil, + meal.price.guest); + return db.insert(DBFavorite.tableName, favorite.toMap()); } @override Future deleteFavorite(Meal meal) async { var db = await database; - return db.delete( - DBFavorite.tableName, - where: '${DBFavorite.columnFavoriteID} = ?', - // Same as above: mealID and favID is the same meal. - whereArgs: [meal.id] - ); + return db.delete(DBFavorite.tableName, + where: '${DBFavorite.columnFavoriteID} = ?', + // Same as above: mealID and favID is the same meal. + whereArgs: [meal.id]); } @override @@ -114,34 +115,32 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } @override - Future> getMealFavorite(String id) async { + Future> getMealFavorite(String id) async { var db = await database; - var result = await db.query( - DBFavorite.tableName, - where: '${DBFavorite.columnFavoriteID} = ?', - whereArgs: [id] - ); - if(result.isNotEmpty) { + var result = await db.query(DBFavorite.tableName, + where: '${DBFavorite.columnFavoriteID} = ?', whereArgs: [id]); + if (result.isNotEmpty) { DBFavorite dbFavorite = DBFavorite.fromMap(result.first); var meal = await _getMeal(dbFavorite.favoriteID, true); return Success(meal); } else { - return Failure(result as NoMealException); + return Failure(NoMealException("Meal not found.")); } } @override - Future>> getMealPlan(DateTime date, Canteen canteen) async { + Future, NoDataException>> getMealPlan( + DateTime date, Canteen canteen) async { var db = await database; - var result = await db.query( - DBMealPlan.tableName, - where: '${DBCanteen.tableName}.${DBCanteen.columnCanteenID} = ${DBLine.tableName}.${DBLine.columnCanteenID} AND ${DBLine.tableName}.${DBLine.columnLineID} = ${DBMealPlan.tableName}.${DBMealPlan.columnLineID} AND ${DBCanteen.tableName}.${DBCanteen.columnCanteenID} = "?" AND ${DBMealPlan.tableName}.${DBMealPlan.columnDate} = "?"', - whereArgs: [canteen.id, date.toString()] - ); - if(result.isNotEmpty) { - var mealPlans = List.empty(); - for (DBMealPlan dbMealPlan in result.map((plan) => DBMealPlan.fromMap(plan))) { - var dbLine = await _getDBLine(dbMealPlan.lineID); + var result = await db.query('${DBMealPlan.tableName}, ${DBCanteen.tableName}, ${DBLine.tableName}', + where: + '${DBCanteen.tableName}.${DBCanteen.columnCanteenID} = ${DBLine.tableName}.${DBLine.columnCanteenID} AND ${DBLine.tableName}.${DBLine.columnLineID} = ${DBMealPlan.tableName}.${DBMealPlan.columnLineID} AND ${DBCanteen.tableName}.${DBCanteen.columnCanteenID} = ? AND ${DBMealPlan.tableName}.${DBMealPlan.columnDate} = ?', + whereArgs: [canteen.id, date.toString()]); + if (result.isNotEmpty) { + var mealPlans = List.empty(); + for (DBMealPlan dbMealPlan + in result.map((plan) => DBMealPlan.fromMap(plan))) { + var dbLine = await _getDBLine(dbMealPlan.lineID); var dbCanteen = await _getDBCanteen(dbLine!.canteenID); var dbMeals = await _getDBMeals(dbMealPlan.mealPlanID); var meals = List.empty(); @@ -150,27 +149,25 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { Meal meal = await _getMeal(dbMeal.mealID, isFavorite); meals.add(meal); } - mealPlans.add(DatabaseTransformer.fromDBMealPlan(dbMealPlan, dbLine, dbCanteen!, meals)); + mealPlans.add(DatabaseTransformer.fromDBMealPlan( + dbMealPlan, dbLine, dbCanteen!, meals)); } return Success(mealPlans); } else { - return Failure(result as NoDataException); + return Failure(NoDataException("No meal plan found for this date.")); } } Future _isFavorite(String id) async { var db = await database; - var result = await db.query( - DBFavorite.tableName, - where: '${DBFavorite.columnFavoriteID} = ?', - whereArgs: [id] - ); + var result = await db.query(DBFavorite.tableName, + where: '${DBFavorite.columnFavoriteID} = ?', whereArgs: [id]); return result.isNotEmpty; } @override - Future updateAll(List mealPlans) async { - for (Mealplan mealPlan in mealPlans) { + Future updateAll(List mealPlans) async { + for (MealPlan mealPlan in mealPlans) { await _insertCanteen(mealPlan.line.canteen); await _insertLine(mealPlan.line); await _insertMealPlan(mealPlan); @@ -189,36 +186,35 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { sideAdditives[side] = (await _getSideAdditive(side.sideID))!; } var images = await _getDBImages(dbMeal.mealID); - return DatabaseTransformer.fromDBMeal(dbMeal, allergens!, additives!, sides, sideAllergens, sideAdditives, images, isFavorite); + return DatabaseTransformer.fromDBMeal(dbMeal, allergens!, additives!, sides, + sideAllergens, sideAdditives, images, isFavorite); } Future _insertLine(Line line) async { var db = await database; var dbLine = DBLine(line.id, line.canteen.id, line.name, line.position); - return db.insert(DBLine.tableName, dbLine.toMap()); + return db.insert(DBLine.tableName, dbLine.toMap(), conflictAlgorithm: ConflictAlgorithm.replace); } Future _insertCanteen(Canteen canteen) async { var db = await database; var dbCanteen = DBCanteen(canteen.id, canteen.name); - return db.insert(DBCanteen.tableName, dbCanteen.toMap()); + return db.insert(DBCanteen.tableName, dbCanteen.toMap(), conflictAlgorithm: ConflictAlgorithm.replace); } - Future _insertMealPlan(Mealplan mealPlan) async { + Future _insertMealPlan(MealPlan mealPlan) async { var db = await database; var uuid = const Uuid(); // TODO: generate UUID or get uuid from backend - var dbMealPlan = DBMealPlan(uuid.toString(), mealPlan.line.id, mealPlan.date.toString(), mealPlan.isClosed); - return db.insert(DBMealPlan.tableName, dbMealPlan.toMap()); + var dbMealPlan = DBMealPlan(uuid.toString(), mealPlan.line.id, + mealPlan.date.toString(), mealPlan.isClosed); + return db.insert(DBMealPlan.tableName, dbMealPlan.toMap(), conflictAlgorithm: ConflictAlgorithm.replace); } Future _getDBCanteen(String canteenID) async { var db = await database; - var result = await db.query( - DBCanteen.tableName, - where: '${DBCanteen.columnCanteenID} = ?', - whereArgs: [canteenID] - ); - if(result.isNotEmpty) { + var result = await db.query(DBCanteen.tableName, + where: '${DBCanteen.columnCanteenID} = ?', whereArgs: [canteenID]); + if (result.isNotEmpty) { return DBCanteen.fromMap(result.first); } else { return null; @@ -227,12 +223,9 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { Future _getDBLine(String lineID) async { var db = await database; - var result = await db.query( - DBLine.tableName, - where: '${DBLine.columnLineID} = ?', - whereArgs: [lineID] - ); - if(result.isNotEmpty) { + var result = await db.query(DBLine.tableName, + where: '${DBLine.columnLineID} = ?', whereArgs: [lineID]); + if (result.isNotEmpty) { return DBLine.fromMap(result.first); } else { return null; @@ -241,12 +234,9 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { Future _getDBMeal(String mealID) async { var db = await database; - var result = await db.query( - DBMeal.tableName, - where: '${DBMeal.columnMealID} = ?', - whereArgs: [mealID] - ); - if(result.isNotEmpty) { + var result = await db.query(DBMeal.tableName, + where: '${DBMeal.columnMealID} = ?', whereArgs: [mealID]); + if (result.isNotEmpty) { return DBMeal.fromMap(result.first); } else { return null; @@ -255,12 +245,9 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { Future _getDBSide(String sideID) async { var db = await database; - var result = await db.query( - DBSide.tableName, - where: '${DBSide.columnSideID} = ?', - whereArgs: [sideID] - ); - if(result.isNotEmpty) { + var result = await db.query(DBSide.tableName, + where: '${DBSide.columnSideID} = ?', whereArgs: [sideID]); + if (result.isNotEmpty) { return DBSide.fromMap(result.first); } else { return null; @@ -269,32 +256,23 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { Future> _getDBMeals(String mealPlanID) async { var db = await database; - var result = await db.query( - DBMeal.tableName, - where: '${DBMeal.columnMealPlanID} = ?', - whereArgs: [mealPlanID] - ); + var result = await db.query(DBMeal.tableName, + where: '${DBMeal.columnMealPlanID} = ?', whereArgs: [mealPlanID]); return result.map((mealRow) => DBMeal.fromMap(mealRow)).toList(); } Future> _getDBSides(String mealID) async { var db = await database; - var result = await db.query( - DBSide.tableName, - where: '${DBSide.columnMealID} = ?', - whereArgs: [mealID] - ); + var result = await db.query(DBSide.tableName, + where: '${DBSide.columnMealID} = ?', whereArgs: [mealID]); return result.map((sideRow) => DBSide.fromMap(sideRow)).toList(); } Future _getDBImage(String imageID) async { var db = await database; - var result = await db.query( - DBImage.tableName, - where: '${DBImage.columnImageID} = ?', - whereArgs: [imageID] - ); - if(result.isNotEmpty) { + var result = await db.query(DBImage.tableName, + where: '${DBImage.columnImageID} = ?', whereArgs: [imageID]); + if (result.isNotEmpty) { return DBImage.fromMap(result.first); } else { return null; @@ -303,22 +281,16 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { Future> _getDBImages(String mealID) async { var db = await database; - var result = await db.query( - DBImage.tableName, - where: '${DBImage.columnMealID} = ?', - whereArgs: [mealID] - ); + var result = await db.query(DBImage.tableName, + where: '${DBImage.columnMealID} = ?', whereArgs: [mealID]); return result.map((imageRow) => DBImage.fromMap(imageRow)).toList(); } Future _getDBMealPlan(String mealPlanID) async { var db = await database; - var result = await db.query( - DBMealPlan.tableName, - where: '${DBMealPlan.columnMealPlanID} = ?', - whereArgs: [mealPlanID] - ); - if(result.isNotEmpty) { + var result = await db.query(DBMealPlan.tableName, + where: '${DBMealPlan.columnMealPlanID} = ?', whereArgs: [mealPlanID]); + if (result.isNotEmpty) { return DBMealPlan.fromMap(result.first); } else { return null; @@ -327,12 +299,9 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { Future _getDBFavorite(String favoriteID) async { var db = await database; - var result = await db.query( - DBFavorite.tableName, - where: '${DBFavorite.columnFavoriteID} = ?', - whereArgs: [favoriteID] - ); - if(result.isNotEmpty) { + var result = await db.query(DBFavorite.tableName, + where: '${DBFavorite.columnFavoriteID} = ?', whereArgs: [favoriteID]); + if (result.isNotEmpty) { return DBFavorite.fromMap(result.first); } else { return null; @@ -341,41 +310,52 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { Future?> _getMealAllergens(String id) async { var db = await database; - var result = await db.query( - DBMealAllergen.tableName, - where: '${DBMealAllergen.columnMealID} = ?', - whereArgs: [id] - ); - return result.map((allergenMap) => DBMealAllergen.fromMap(allergenMap).allergen).toList(); + var result = await db.query(DBMealAllergen.tableName, + where: '${DBMealAllergen.columnMealID} = ?', whereArgs: [id]); + return result + .map((allergenMap) => DBMealAllergen.fromMap(allergenMap).allergen) + .toList(); } Future?> _getSideAllergens(String id) async { var db = await database; - var result = await db.query( - DBSideAllergen.tableName, - where: '${DBSideAllergen.columnSideID} = ?', - whereArgs: [id] - ); - return result.map((allergenMap) => DBMealAllergen.fromMap(allergenMap).allergen).toList(); + var result = await db.query(DBSideAllergen.tableName, + where: '${DBSideAllergen.columnSideID} = ?', whereArgs: [id]); + return result + .map((allergenMap) => DBMealAllergen.fromMap(allergenMap).allergen) + .toList(); } Future?> _getMealAdditive(String id) async { var db = await database; - var result = await db.query( - DBMealAdditive.tableName, - where: '${DBMealAdditive.columnMealID} = ?', - whereArgs: [id] - ); - return result.map((allergenMap) => DBMealAdditive.fromMap(allergenMap).additive).toList(); + var result = await db.query(DBMealAdditive.tableName, + where: '${DBMealAdditive.columnMealID} = ?', whereArgs: [id]); + return result + .map((allergenMap) => DBMealAdditive.fromMap(allergenMap).additive) + .toList(); } Future?> _getSideAdditive(String id) async { var db = await database; - var result = await db.query( - DBSideAdditive.tableName, - where: '${DBSideAdditive.columnSideID} = ?', - whereArgs: [id] - ); - return result.map((allergenMap) => DBSideAdditive.fromMap(allergenMap).additive).toList(); + var result = await db.query(DBSideAdditive.tableName, + where: '${DBSideAdditive.columnSideID} = ?', whereArgs: [id]); + return result + .map((allergenMap) => DBSideAdditive.fromMap(allergenMap).additive) + .toList(); + } + + @override + Future getCanteenById(String id) { + return Future.value(Canteen(id: id, name: 'Canteen $id')); + } + + @override + Future getFavoriteMealsDate(Meal meal) { + return Future.value(DateTime.now()); + } + + @override + Future getFavoriteMealsLine(Meal meal) { + return Future.value(null); } -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/database_model.dart b/app/lib/model/database/model/database_model.dart index 1a24df10..a5117f87 100644 --- a/app/lib/model/database/model/database_model.dart +++ b/app/lib/model/database/model/database_model.dart @@ -1,13 +1,12 @@ -import 'package:app/model/database/SQLiteDatabaseAccess.dart'; import 'package:app/model/database/model/db_canteen.dart'; import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; +import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/meal/Price.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; -import 'package:app/view_model/repository/data_classes/mealplan/Mealplan.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; import '../../../view_model/repository/data_classes/meal/Additive.dart'; -import '../../../view_model/repository/data_classes/meal/Image.dart'; import '../../../view_model/repository/data_classes/meal/Side.dart'; import '../../../view_model/repository/data_classes/mealplan/Line.dart'; import 'db_image.dart'; @@ -15,10 +14,6 @@ import 'db_line.dart'; import 'db_meal.dart'; import 'db_meal_plan.dart'; import 'db_side.dart'; -import 'db_side_additive.dart'; -import 'db_side_allergen.dart'; -import 'db_meal_additive.dart'; -import 'db_meal_allergen.dart'; abstract class DatabaseModel { Map toMap(); @@ -75,8 +70,8 @@ class DatabaseTransformer { ); } - static Image fromDBImage(DBImage image) { - return Image( + static ImageData fromDBImage(DBImage image) { + return ImageData( id: image.imageID, url: image.url, imageRank: image.imageRank, @@ -102,8 +97,8 @@ class DatabaseTransformer { ); } - static Mealplan fromDBMealPlan(DBMealPlan plan, DBLine line, DBCanteen canteen, List meals) { - return Mealplan( + static MealPlan fromDBMealPlan(DBMealPlan plan, DBLine line, DBCanteen canteen, List meals) { + return MealPlan( date: DateTime.tryParse(plan.date)!, line: DatabaseTransformer.fromDBLine(line, canteen), isClosed: plan.isClosed, diff --git a/app/lib/model/database/model/db_canteen.dart b/app/lib/model/database/model/db_canteen.dart index 0e6a9afe..d2094177 100644 --- a/app/lib/model/database/model/db_canteen.dart +++ b/app/lib/model/database/model/db_canteen.dart @@ -26,7 +26,7 @@ class DBCanteen implements DatabaseModel { /// The string to create a table for the canteen. static String initTable() { return ''' - CREATE TABLE $tableName( + CREATE TABLE $tableName ( $columnCanteenID TEXT PRIMARY KEY, $columnName TEXT NOT NULL ) diff --git a/app/lib/model/database/model/db_favorite.dart b/app/lib/model/database/model/db_favorite.dart index 4384b7e9..548e3b9d 100644 --- a/app/lib/model/database/model/db_favorite.dart +++ b/app/lib/model/database/model/db_favorite.dart @@ -48,11 +48,11 @@ class DBFavorite implements DatabaseModel { /// The string to create a table for a favorite. static String initTable() { return ''' - CREATE TABLE $tableName( + CREATE TABLE $tableName ( $columnFavoriteID TEXT PRIMARY KEY, $columnLineID TEXT NOT NULL, $columnLastDate TEXT NOT NULL, - $columnFoodType TEXT CHECK($columnFoodType IN (${FoodType.values.map((type) => "'$type'").join(', ')})), + $columnFoodType TEXT, $columnPriceStudent INTEGER CHECK($columnPriceStudent > 0), $columnPriceEmployee INTEGER CHECK($columnPriceEmployee > 0), $columnPricePupil INTEGER CHECK($columnPricePupil > 0), diff --git a/app/lib/model/database/model/db_image.dart b/app/lib/model/database/model/db_image.dart index 370445b8..66557f2f 100644 --- a/app/lib/model/database/model/db_image.dart +++ b/app/lib/model/database/model/db_image.dart @@ -44,7 +44,7 @@ class DBImage implements DatabaseModel { /// The string to create a table for an image. static String initTable() { return ''' - CREATE TABLE $tableName( + CREATE TABLE $tableName ( $columnImageID TEXT PRIMARY KEY, $columnMealID TEXT NOT NULL, $columnUrl TEXT NOT NULL, diff --git a/app/lib/model/database/model/db_line.dart b/app/lib/model/database/model/db_line.dart index d1cc460c..cfe275ba 100644 --- a/app/lib/model/database/model/db_line.dart +++ b/app/lib/model/database/model/db_line.dart @@ -35,7 +35,7 @@ class DBLine implements DatabaseModel { /// The string to create a table for a line of a canteen. static String initTable() { return ''' - CREATE TABLE $tableName( + CREATE TABLE $tableName ( $columnLineID TEXT PRIMARY KEY, $columnCanteenID TEXT NOT NULL, $columnName TEXT NOT NULL, diff --git a/app/lib/model/database/model/db_meal.dart b/app/lib/model/database/model/db_meal.dart index 8dc21466..9443c482 100644 --- a/app/lib/model/database/model/db_meal.dart +++ b/app/lib/model/database/model/db_meal.dart @@ -67,11 +67,11 @@ class DBMeal implements DatabaseModel { static String initTable() { /// The string to create a table for a meal. return ''' - CREATE TABLE $tableName( + CREATE TABLE $tableName ( $columnMealID TEXT PRIMARY KEY, $columnMealPlanID TEXT NOT NULL, $columnName TEXT NOT NULL, - $columnFoodType TEXT NOT NULL CHECK($columnFoodType IN (${FoodType.values.map((type) => "'$type'").join(', ')})), + $columnFoodType TEXT NOT NULL, $columnPriceStudent INTEGER NOT NULL CHECK($columnPriceStudent >= 0), $columnPriceEmployee INTEGER NOT NULL CHECK($columnPriceEmployee >= 0), $columnPricePupil INTEGER NOT NULL CHECK($columnPricePupil >= 0), @@ -81,7 +81,7 @@ class DBMeal implements DatabaseModel { $columnAverageRating DECIMAL(1,1), $columnLastServed TEXT NOT NULL, $columnNextServed TEXT, - $columnRelativeFrequency TEXT CHECK IN (${Frequency.values.map((frequency) => "'$frequency'").join(', ')}), + $columnRelativeFrequency TEXT, FOREIGN KEY($columnMealPlanID) REFERENCES ${DBMealPlan.tableName}(${DBMealPlan.columnMealPlanID}) ) '''; diff --git a/app/lib/model/database/model/db_meal_additive.dart b/app/lib/model/database/model/db_meal_additive.dart index e81de746..7a5862f1 100644 --- a/app/lib/model/database/model/db_meal_additive.dart +++ b/app/lib/model/database/model/db_meal_additive.dart @@ -30,9 +30,9 @@ class DBMealAdditive implements DatabaseModel { /// The string to create a table for an additive of a meal. static String initTable() { return ''' - CREATE TABLE $tableName( + CREATE TABLE $tableName ( $columnMealID TEXT, - $columnAdditive TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), + $columnAdditive TEXT, FOREIGN KEY($columnMealID) REFERENCES ${DBMeal.tableName}(${DBMeal.columnMealID}), PRIMARY KEY($columnMealID, $columnAdditive) ) diff --git a/app/lib/model/database/model/db_meal_allergen.dart b/app/lib/model/database/model/db_meal_allergen.dart index dfd240ca..5797d22e 100644 --- a/app/lib/model/database/model/db_meal_allergen.dart +++ b/app/lib/model/database/model/db_meal_allergen.dart @@ -30,9 +30,9 @@ class DBMealAllergen implements DatabaseModel { /// The string to create a table for an allergen of a meal. static String initTable() { return ''' - CREATE TABLE $tableName( + CREATE TABLE $tableName ( $columnMealID TEXT, - $columnAllergen TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), + $columnAllergen TEXT, FOREIGN KEY($columnMealID) REFERENCES ${DBMeal.tableName}(${DBMeal.columnMealID}), PRIMARY KEY($columnMealID, $columnAllergen) ) diff --git a/app/lib/model/database/model/db_meal_plan.dart b/app/lib/model/database/model/db_meal_plan.dart index ff53faf0..82d7ee91 100644 --- a/app/lib/model/database/model/db_meal_plan.dart +++ b/app/lib/model/database/model/db_meal_plan.dart @@ -34,7 +34,7 @@ class DBMealPlan implements DatabaseModel { /// The string to create a table for a meal plan. static String initTable() { return ''' - CREATE TABLE $tableName( + CREATE TABLE $tableName ( $columnMealPlanID TEXT NOT NULL, $columnLineID TEXT NOT NULL, $columnDate TEXT, diff --git a/app/lib/model/database/model/db_side.dart b/app/lib/model/database/model/db_side.dart index 27433c71..e26196a8 100644 --- a/app/lib/model/database/model/db_side.dart +++ b/app/lib/model/database/model/db_side.dart @@ -48,11 +48,11 @@ class DBSide implements DatabaseModel { /// The string to create a table for a side. static String initTable() { return ''' - CREATE TABLE $tableName( + CREATE TABLE $tableName ( $columnSideID TEXT PRIMARY KEY, $columnMealID TEXT NOT NULL, $columnName TEXT NOT NULL, - $columnFoodType TEXT NOT NULL CHECK($columnFoodType IN (${FoodType.values.map((type) => "'$type'").join(', ')})), + $columnFoodType TEXT NOT NULL, $columnPriceStudent INTEGER NOT NULL CHECK($columnPriceStudent >= 0), $columnPriceEmployee INTEGER NOT NULL CHECK($columnPriceEmployee >= 0), $columnPricePupil INTEGER NOT NULL CHECK($columnPricePupil >= 0), diff --git a/app/lib/model/database/model/db_side_additive.dart b/app/lib/model/database/model/db_side_additive.dart index e2ae1692..0a069e98 100644 --- a/app/lib/model/database/model/db_side_additive.dart +++ b/app/lib/model/database/model/db_side_additive.dart @@ -30,9 +30,9 @@ class DBSideAdditive implements DatabaseModel { /// The string to create a table for an additive of a side. static String initTable() { return ''' - CREATE TABLE $tableName( + CREATE TABLE $tableName ( $columnSideID TEXT, - $columnAdditive TEXT CHECK IN (${Additive.values.map((additive) => "'$additive'").join(', ')}), + $columnAdditive TEXT, FOREIGN KEY($columnSideID) REFERENCES ${DBSide.tableName}(${DBSide.columnSideID}), PRIMARY KEY($columnSideID, $columnAdditive) ) diff --git a/app/lib/model/database/model/db_side_allergen.dart b/app/lib/model/database/model/db_side_allergen.dart index 72455b43..e9ee6af7 100644 --- a/app/lib/model/database/model/db_side_allergen.dart +++ b/app/lib/model/database/model/db_side_allergen.dart @@ -30,9 +30,9 @@ class DBSideAllergen implements DatabaseModel { /// The string to create a table for an allergen of a side. static String initTable() { return ''' - CREATE TABLE $tableName( + CREATE TABLE $tableName ( $columnSideID TEXT, - $columnAllergen TEXT CHECK IN (${Allergen.values.map((allergen) => "'$allergen'").join(', ')}), + $columnAllergen TEXT, FOREIGN KEY($columnSideID) REFERENCES ${DBSide.tableName}(${DBSide.columnSideID}), PRIMARY KEY($columnSideID, $columnAllergen) ) diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index ac81c7ab..9df83ca4 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -87,8 +87,8 @@ class SharedPreferenceAccess implements ILocalStorage { @override Future getPriceCategory() async { - final priceCategory = _pref.getString('priceCategory'); - return Future.value(PriceCategory.values.firstWhere((e) => e.toString() == priceCategory)); + final String? priceCategory = _pref.getString('priceCategory'); + return Future.value(PriceCategory.values.firstWhere((e) => e.toString() == priceCategory, orElse: () => PriceCategory.student)); } @override diff --git a/app/lib/view/core/MensaAppBar.dart b/app/lib/view/core/MensaAppBar.dart index d5e81ef5..5cc28462 100644 --- a/app/lib/view/core/MensaAppBar.dart +++ b/app/lib/view/core/MensaAppBar.dart @@ -1,19 +1,14 @@ -import 'package:app/view/core/selection_components/MensaDropdown.dart'; -import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; -import 'package:app/view_model/logic/meal/IMealAccess.dart'; -import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class MensaAppBar extends StatelessWidget implements PreferredSizeWidget { final PreferredSizeWidget? _bottom; final Widget _child; - final double? _appBarHeight; + final double _appBarHeight; MensaAppBar( {super.key, PreferredSizeWidget? bottom, - double? appBarHeight, + double appBarHeight = kToolbarHeight, required Widget child}) : _appBarHeight = appBarHeight, _bottom = bottom, @@ -38,8 +33,8 @@ class MensaAppBar extends StatelessWidget implements PreferredSizeWidget { class _PreferredAppBarSize extends Size { _PreferredAppBarSize(this.appBarHeight, this.bottomHeight) : super.fromHeight( - (appBarHeight ?? kToolbarHeight) + (bottomHeight ?? 0)); + appBarHeight + (bottomHeight ?? 0)); - final double? appBarHeight; + final double appBarHeight; final double? bottomHeight; } diff --git a/app/lib/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart b/app/lib/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart new file mode 100644 index 00000000..ab287e56 --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationFilterOutlinedIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationFilterOutlinedIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/filter_outlined.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/navigation/NavigationGridOutlinedIcon.dart b/app/lib/view/core/icons/navigation/NavigationGridOutlinedIcon.dart new file mode 100644 index 00000000..62030d9b --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationGridOutlinedIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationGridOutlinedIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationGridOutlinedIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/grid_outlined.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/navigation/NavigationListOutlinedIcon.dart b/app/lib/view/core/icons/navigation/NavigationListOutlinedIcon.dart new file mode 100644 index 00000000..1ff1ff91 --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationListOutlinedIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationListOutlinedIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationListOutlinedIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/list_outlined.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/information_display/MealPreviewImage.dart b/app/lib/view/core/information_display/MealPreviewImage.dart index a6a1622a..2f90f3ed 100644 --- a/app/lib/view/core/information_display/MealPreviewImage.dart +++ b/app/lib/view/core/information_display/MealPreviewImage.dart @@ -41,7 +41,7 @@ class MealPreviewImage extends StatelessWidget { @override Widget build(BuildContext context) { - if (_meal.images == null || _meal.images!.isEmpty) { + if (_meal.images == null || _meal.images!.isEmpty || _meal.images!.first.url.isEmpty) { return Container( width: _width, height: _height, diff --git a/app/lib/view/core/input_components/MensaRatingInput.dart b/app/lib/view/core/input_components/MensaRatingInput.dart index 5cd56f5e..b6cbc63a 100644 --- a/app/lib/view/core/input_components/MensaRatingInput.dart +++ b/app/lib/view/core/input_components/MensaRatingInput.dart @@ -43,24 +43,48 @@ class MensaRatingInput extends StatelessWidget { for (int i = 0; i < _max; i++) AbsorbPointer( absorbing: _disabled, - child: IconButton( - padding: const EdgeInsets.symmetric(horizontal: 0), - constraints: const BoxConstraints(), - onPressed: () => {if (!_disabled) _onChanged(i + 1)}, - icon: i < _value.floor() - ? SvgPicture.asset( - 'assets/icons/star_filled.svg', - colorFilter: ColorFilter.mode( - _color ?? Theme.of(context).colorScheme.primary, - BlendMode.srcIn), - width: _size, - height: _size, - ) - : i < _value && _value < i + 1 - ? Stack( - //fit: StackFit.expand, - children: [ - SvgPicture.asset( + child: Padding( + padding: EdgeInsets.all(_size * .1), + child: GestureDetector( + onTap: () => {if (!_disabled) _onChanged(i + 1)}, + child: i < _value.floor() + ? SvgPicture.asset( + 'assets/icons/star_filled.svg', + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.primary, + BlendMode.srcIn), + width: _size, + height: _size, + ) + : i < _value && _value < i + 1 + ? Stack( + //fit: StackFit.expand, + children: [ + SvgPicture.asset( + 'assets/icons/star_outlined.svg', + colorFilter: ColorFilter.mode( + _color ?? + Theme.of(context).colorScheme.primary, + BlendMode.srcIn), + width: _size, + height: _size, + ), + ClipRect( + clipper: _Clipper(part: _value - i), + child: SvgPicture.asset( + 'assets/icons/star_filled.svg', + colorFilter: ColorFilter.mode( + _color ?? + Theme.of(context) + .colorScheme + .primary, + BlendMode.srcIn), + width: _size, + height: _size, + )) + ], + ) + : SvgPicture.asset( 'assets/icons/star_outlined.svg', colorFilter: ColorFilter.mode( _color ?? @@ -69,28 +93,7 @@ class MensaRatingInput extends StatelessWidget { width: _size, height: _size, ), - ClipRect( - clipper: _Clipper(part: _value - i), - child: SvgPicture.asset( - 'assets/icons/star_filled.svg', - colorFilter: ColorFilter.mode( - _color ?? - Theme.of(context).colorScheme.primary, - BlendMode.srcIn), - width: _size, - height: _size, - )) - ], - ) - : SvgPicture.asset( - 'assets/icons/star_outlined.svg', - colorFilter: ColorFilter.mode( - _color ?? Theme.of(context).colorScheme.primary, - BlendMode.srcIn), - width: _size, - height: _size, - ), - ), + )), ) ], ); diff --git a/app/lib/view/core/meal_view_format/MealGrid.dart b/app/lib/view/core/meal_view_format/MealGrid.dart index 872eea9f..8f3cb7ed 100644 --- a/app/lib/view/core/meal_view_format/MealGrid.dart +++ b/app/lib/view/core/meal_view_format/MealGrid.dart @@ -16,7 +16,7 @@ class MealGrid extends StatelessWidget { @override Widget build(BuildContext context) { return ListView.builder( - physics: const AlwaysScrollableScrollPhysics(), + //physics: const AlwaysScrollableScrollPhysics(), scrollDirection: Axis.vertical, shrinkWrap: true, itemBuilder: (ctx, index) { diff --git a/app/lib/view/core/meal_view_format/MealGridEntry.dart b/app/lib/view/core/meal_view_format/MealGridEntry.dart index bc133a47..25e856e2 100644 --- a/app/lib/view/core/meal_view_format/MealGridEntry.dart +++ b/app/lib/view/core/meal_view_format/MealGridEntry.dart @@ -54,13 +54,12 @@ class MealGridEntry extends StatelessWidget { borderRadius: const BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8))), - Padding( - padding: EdgeInsets.symmetric(vertical: 4), - child: MealMainEntry(meal: _meal)), + SizedBox(height: 4), + MealMainEntry(meal: _meal), Padding( padding: EdgeInsets.symmetric(horizontal: 4), child: Row(children: [ - SizedBox(width: 36), + SizedBox(width: 32), MensaRatingInput( disabled: true, onChanged: (v) {}, diff --git a/app/lib/view/mealplan/MealPlanToolbar.dart b/app/lib/view/mealplan/MealPlanToolbar.dart new file mode 100644 index 00000000..03bb65c3 --- /dev/null +++ b/app/lib/view/mealplan/MealPlanToolbar.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +class MealPlanToolbar extends StatelessWidget implements PreferredSizeWidget { + final Widget _child; + final double _toolBarHeight; + + MealPlanToolbar({super.key, double toolBarHeight = kToolbarHeight, required Widget child}) + : _toolBarHeight = toolBarHeight, + preferredSize = _PreferredAppBarSize(toolBarHeight), + _child = child; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: preferredSize.height, + child: Column(children: [ + SizedBox(height: _toolBarHeight, child: _child), + ])); + } + + @override + final Size preferredSize; +} + +class _PreferredAppBarSize extends Size { + _PreferredAppBarSize(this.appBarHeight) + : super.fromHeight(appBarHeight); + + final double appBarHeight; +} diff --git a/app/lib/view_model/logic/favorite/IFavoriteMealAccess.dart b/app/lib/view_model/logic/favorite/IFavoriteMealAccess.dart index b81d58e4..a07c6347 100644 --- a/app/lib/view_model/logic/favorite/IFavoriteMealAccess.dart +++ b/app/lib/view_model/logic/favorite/IFavoriteMealAccess.dart @@ -1,8 +1,10 @@ +import 'package:flutter/material.dart'; + import '../../repository/data_classes/meal/Meal.dart'; /// This class is the interface for the access to the favorite meals data. -abstract class IFavoriteMealAccess { +abstract class IFavoriteMealAccess with ChangeNotifier { /// This method adds the committed meal to the favorite meals in the database. /// @param meal The meal that should be added diff --git a/app/lib/view_model/logic/image/IImageAccess.dart b/app/lib/view_model/logic/image/IImageAccess.dart index 65ca2ce4..b4d102e0 100644 --- a/app/lib/view_model/logic/image/IImageAccess.dart +++ b/app/lib/view_model/logic/image/IImageAccess.dart @@ -1,11 +1,12 @@ import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; +import 'package:flutter/material.dart'; import '../../repository/data_classes/meal/Meal.dart'; /// This class is the interface for the access to the image data. -abstract class IImageAccess { +abstract class IImageAccess with ChangeNotifier { /// This method links the committed url to the committed meal on the server. /// It returns a string that should be displayed in a temporal message. /// @param url The url of the image diff --git a/app/lib/view_model/logic/preference/IPreferenceAccess.dart b/app/lib/view_model/logic/preference/IPreferenceAccess.dart index 35d18ac3..386a9f85 100644 --- a/app/lib/view_model/logic/preference/IPreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/IPreferenceAccess.dart @@ -1,9 +1,11 @@ +import 'package:flutter/material.dart'; + import '../../repository/data_classes/settings/MensaColorScheme.dart'; import '../../repository/data_classes/settings/MealPlanFormat.dart'; import '../../repository/data_classes/settings/PriceCategory.dart'; /// This is an interface for accessing the preferences. -abstract class IPreferenceAccess { +abstract class IPreferenceAccess with ChangeNotifier { /// The client identifier is returned. /// @return The client identifier. Future getClientIdentifier(); diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift index 37769918..4008a7f1 100644 --- a/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,9 +8,11 @@ import Foundation import connectivity_plus import path_provider_foundation import shared_preferences_foundation +import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/app/pubspec.lock b/app/pubspec.lock index c0ad5225..bafac636 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -509,8 +509,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" - path: - dependency: "direct main" mime: dependency: transitive description: @@ -568,7 +566,7 @@ packages: source: hosted version: "2.1.0" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" @@ -651,10 +649,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" pool: dependency: transitive description: From 823a252e45e0c49fe2c4b43b2c04c96199b42746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 25 Jul 2023 16:13:54 +0200 Subject: [PATCH 128/184] changes in init-order --- .../logic/meal/CombinedMealPlanAccess.dart | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 858870a1..8c8d7b4d 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -44,17 +44,6 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { _priceCategory = await _preferences.getPriceCategory() ?? PriceCategory.student; - // get meal plans form server - List mealPlans = switch (await _api.updateAll()) { - Success(value: final mealplan) => mealplan, - Failure() => [] - }; - - // update all if connection to server is successful - if (mealPlans.isNotEmpty) { - _database.updateAll(mealPlans); - } - // get canteen from string // get canteen id from local storage final canteenString = await _preferences.getCanteen(); @@ -91,6 +80,17 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { Failure() => [] }; + // get meal plans form server + List mealPlans = switch (await _api.updateAll()) { + Success(value: final mealplan) => mealplan, + Failure(exception: final exception) => _convertMealPlanExceptionToMealPlan(exception) + }; + + // update all if connection to server is successful + if (mealPlans.isNotEmpty) { + _database.updateAll(mealPlans); + } + // filter meal plans await _filterMealPlans(); } From a506bb6ac4604b5b8dacfe89bd1c7c9edec484fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 25 Jul 2023 16:50:40 +0200 Subject: [PATCH 129/184] implement dropdowns --- app/lib/view/settings/Settings.dart | 88 +++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 5 deletions(-) diff --git a/app/lib/view/settings/Settings.dart b/app/lib/view/settings/Settings.dart index be36bdff..72d66e4e 100644 --- a/app/lib/view/settings/Settings.dart +++ b/app/lib/view/settings/Settings.dart @@ -1,19 +1,97 @@ +import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; +import 'package:app/view/settings/SettingsDropdownEntry.dart'; +import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; +import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; +import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class Settings extends StatefulWidget { - const Settings({super.key}); + final String _version; + + const Settings({super.key, required version}) : _version = version; @override State createState() => _SettingsState(); } class _SettingsState extends State { - - @override Widget build(BuildContext context) { - // TODO: implement build - throw UnimplementedError(); + return Consumer( + builder: (context, storage, child) => + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 12), + child: SingleChildScrollView( + child: Column(children: [ + FutureBuilder(future: Future.wait([ + storage.getColorScheme(), + storage.getPriceCategory() + ]), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Column(children: [ + SettingsDropdownEntry(onChanged: (value) {}, + value: "", + items: const [], + // todo right string? + heading: "settings.colorScheme"), + SettingsDropdownEntry(onChanged: (value) {}, + value: "", + items: const [], + heading: "settings.priceCategory"), + ],); + } + + if (snapshot.hasError) { + // todo input from above + } + + return Column(children: [ + SettingsDropdownEntry(onChanged: (value) { + if (value != null && value != snapshot.requireData[0] as MensaColorScheme) { + storage.setColorScheme(value); + } + }, + value: snapshot.requireData[0] as MensaColorScheme, + items: _getColorSchemeEntries(), + // todo right string? + heading: "settings.colorScheme"), + SettingsDropdownEntry(onChanged: (value) { + if (value != null && value != snapshot.data?[0]) { + storage.setPriceCategory(value); + } + }, + value: snapshot.requireData[1] as PriceCategory, + items: _getPriceCategoryEntries(), + heading: "settings.priceCategory"), + ],); + }), + + ]), + ))); } + List> _getColorSchemeEntries() { + List> entries = []; + + for (final value in MensaColorScheme.values) { + entries.add( + MensaDropdownEntry(value: value, label: "mensaColorScheme.$value")); + } + + return entries; + } + + List> _getPriceCategoryEntries() { + List> entries = []; + + for (final value in PriceCategory.values) { + entries.add( + MensaDropdownEntry(value: value, label: "priceCategory.$value")); + } + + return entries; + } } \ No newline at end of file From ee09c93832e57584a480a9c9bbc42e035401b225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 25 Jul 2023 22:41:05 +0200 Subject: [PATCH 130/184] add packages for clicking on urls and getting version of app --- app/android/app/build.gradle | 2 +- .../flutter/generated_plugin_registrant.cc | 4 + app/linux/flutter/generated_plugins.cmake | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 4 + app/pubspec.lock | 1047 ----------------- app/pubspec.yaml | 2 + .../flutter/generated_plugin_registrant.cc | 3 + app/windows/flutter/generated_plugins.cmake | 1 + 8 files changed, 16 insertions(+), 1048 deletions(-) delete mode 100644 app/pubspec.lock diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index 6faabd7e..d66764a8 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -47,7 +47,7 @@ android { applicationId "com.example.app" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion + minSdkVersion 19 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/app/linux/flutter/generated_plugin_registrant.cc b/app/linux/flutter/generated_plugin_registrant.cc index e71a16d2..f6f23bfe 100644 --- a/app/linux/flutter/generated_plugin_registrant.cc +++ b/app/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/app/linux/flutter/generated_plugins.cmake b/app/linux/flutter/generated_plugins.cmake index 2e1de87a..f16b4c34 100644 --- a/app/linux/flutter/generated_plugins.cmake +++ b/app/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift index 4008a7f1..0f23c08d 100644 --- a/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,13 +6,17 @@ import FlutterMacOS import Foundation import connectivity_plus +import package_info_plus import path_provider_foundation import shared_preferences_foundation import sqflite +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/app/pubspec.lock b/app/pubspec.lock deleted file mode 100644 index bafac636..00000000 --- a/app/pubspec.lock +++ /dev/null @@ -1,1047 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a - url: "https://pub.dev" - source: hosted - version: "61.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 - url: "https://pub.dev" - source: hosted - version: "5.13.0" - args: - dependency: transitive - description: - name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 - url: "https://pub.dev" - source: hosted - version: "2.4.2" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - build: - dependency: transitive - description: - name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" - source: hosted - version: "1.1.1" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20" - url: "https://pub.dev" - source: hosted - version: "2.2.1" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" - url: "https://pub.dev" - source: hosted - version: "2.4.6" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" - url: "https://pub.dev" - source: hosted - version: "7.2.10" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" - url: "https://pub.dev" - source: hosted - version: "8.6.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" - source: hosted - version: "2.0.3" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189" - url: "https://pub.dev" - source: hosted - version: "4.5.0" - collection: - dependency: transitive - description: - name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" - url: "https://pub.dev" - source: hosted - version: "1.17.1" - connectivity_plus: - dependency: transitive - description: - name: connectivity_plus - sha256: b74247fad72c171381dbe700ca17da24deac637ab6d43c343b42867acb95c991 - url: "https://pub.dev" - source: hosted - version: "3.0.6" - connectivity_plus_platform_interface: - dependency: transitive - description: - name: connectivity_plus_platform_interface - sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a - url: "https://pub.dev" - source: hosted - version: "1.2.4" - convert: - dependency: "direct main" - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - coverage: - dependency: transitive - description: - name: coverage - sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" - url: "https://pub.dev" - source: hosted - version: "1.6.3" - crypto: - dependency: "direct main" - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be - url: "https://pub.dev" - source: hosted - version: "1.0.5" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - dbus: - dependency: transitive - description: - name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" - url: "https://pub.dev" - source: hosted - version: "0.7.8" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 - url: "https://pub.dev" - source: hosted - version: "2.0.2" - file: - dependency: transitive - description: - name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" - url: "https://pub.dev" - source: hosted - version: "6.1.4" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_hooks: - dependency: transitive - description: - name: flutter_hooks - sha256: "6a126f703b89499818d73305e4ce1e3de33b4ae1c5512e3b8eab4b986f46774c" - url: "https://pub.dev" - source: hosted - version: "0.18.6" - flutter_i18n: - dependency: "direct main" - description: - name: flutter_i18n - sha256: b71fe887697686368c93e4d2257ecdb3c35fe38686a6fbf3902d6355c3f20363 - url: "https://pub.dev" - source: hosted - version: "0.33.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - flutter_localizations: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_svg: - dependency: "direct main" - description: - name: flutter_svg - sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" - url: "https://pub.dev" - source: hosted - version: "2.0.7" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - gql: - dependency: transitive - description: - name: gql - sha256: "998304fbb88a3956cfea10cd27a56f8e5d4b3bc110f03c952c18a9310774e8bb" - url: "https://pub.dev" - source: hosted - version: "0.14.0" - gql_code_builder: - dependency: transitive - description: - name: gql_code_builder - sha256: "66fc942416d9703e59ac732b2cb4e8442261fc90e1fcf89881b5b796d0803d02" - url: "https://pub.dev" - source: hosted - version: "0.7.1" - gql_dedupe_link: - dependency: transitive - description: - name: gql_dedupe_link - sha256: "89681048cf956348e865da872a40081499b8c087fc84dd4d4b9c134bd70d27b3" - url: "https://pub.dev" - source: hosted - version: "2.0.3+1" - gql_error_link: - dependency: transitive - description: - name: gql_error_link - sha256: e7bfdd2b6232f3e15861cd96c2ad6b7c9c94693843b3dea18295136a5fb5b534 - url: "https://pub.dev" - source: hosted - version: "0.2.3+1" - gql_exec: - dependency: transitive - description: - name: gql_exec - sha256: "0d1fdb2e4154efbfc1dcf3f35ec36d19c8428ff0d560eb4c45b354f8f871dc50" - url: "https://pub.dev" - source: hosted - version: "0.4.3" - gql_http_link: - dependency: transitive - description: - name: gql_http_link - sha256: "89ef87b32947acf4189f564c095f1148b0ab9bb9996fe518716dbad66708b834" - url: "https://pub.dev" - source: hosted - version: "0.4.5" - gql_link: - dependency: transitive - description: - name: gql_link - sha256: f7973279126bc922d465c4f4da6ed93d187085e597b3480f5e14e74d28fe14bd - url: "https://pub.dev" - source: hosted - version: "0.5.1" - gql_transform_link: - dependency: transitive - description: - name: gql_transform_link - sha256: b1735a9a92d25a92960002a8b40dfaede95ec1e5ed848906125d69efd878661f - url: "https://pub.dev" - source: hosted - version: "0.2.2+1" - graphql: - dependency: transitive - description: - name: graphql - sha256: bda5b794345087ccbd16942045be8091e2ac4619285bb22e73555d5fd88c4043 - url: "https://pub.dev" - source: hosted - version: "5.2.0-beta.1" - graphql_codegen: - dependency: "direct dev" - description: - name: graphql_codegen - sha256: "572cfa1ca69050bf8763e8b9e151df6240f08b41e961916a2f88882b4ff897f5" - url: "https://pub.dev" - source: hosted - version: "0.12.2" - graphql_flutter: - dependency: "direct main" - description: - name: graphql_flutter - sha256: "06059ac9e8417c71582f05e28a59b1416d43959d34a6a0d9565341e3a362e117" - url: "https://pub.dev" - source: hosted - version: "5.1.2" - graphs: - dependency: transitive - description: - name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 - url: "https://pub.dev" - source: hosted - version: "2.3.1" - hive: - dependency: transitive - description: - name: hive - sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" - url: "https://pub.dev" - source: hosted - version: "2.2.3" - http: - dependency: transitive - description: - name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" - url: "https://pub.dev" - source: hosted - version: "0.13.6" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - intl: - dependency: "direct main" - description: - name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 - url: "https://pub.dev" - source: hosted - version: "0.18.0" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 - url: "https://pub.dev" - source: hosted - version: "4.8.1" - lints: - dependency: transitive - description: - name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" - url: "https://pub.dev" - source: hosted - version: "0.12.15" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - meta: - dependency: transitive - description: - name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - mime: - dependency: transitive - description: - name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e - url: "https://pub.dev" - source: hosted - version: "1.0.4" - mocktail: - dependency: "direct main" - description: - name: mocktail - sha256: "80a996cd9a69284b3dc521ce185ffe9150cde69767c2d3a0720147d93c0cef53" - url: "https://pub.dev" - source: hosted - version: "0.3.0" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - nm: - dependency: transitive - description: - name: nm - sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" - url: "https://pub.dev" - source: hosted - version: "0.5.0" - node_preamble: - dependency: transitive - description: - name: node_preamble - sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - normalize: - dependency: transitive - description: - name: normalize - sha256: "8a60e37de5b608eeaf9b839273370c71ebba445e9f73b08eee7725e0d92dbc43" - url: "https://pub.dev" - source: hosted - version: "0.8.2+1" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - path: - dependency: "direct main" - description: - name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" - url: "https://pub.dev" - source: hosted - version: "1.8.3" - path_parsing: - dependency: transitive - description: - name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf - url: "https://pub.dev" - source: hosted - version: "1.0.1" - path_provider: - dependency: "direct main" - description: - name: path_provider - sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" - url: "https://pub.dev" - source: hosted - version: "2.0.15" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" - url: "https://pub.dev" - source: hosted - version: "2.0.27" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" - url: "https://pub.dev" - source: hosted - version: "2.2.4" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 - url: "https://pub.dev" - source: hosted - version: "2.1.11" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" - url: "https://pub.dev" - source: hosted - version: "2.0.6" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" - url: "https://pub.dev" - source: hosted - version: "2.1.7" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 - url: "https://pub.dev" - source: hosted - version: "5.4.0" - platform: - dependency: transitive - description: - name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" - url: "https://pub.dev" - source: hosted - version: "3.1.0" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" - url: "https://pub.dev" - source: hosted - version: "2.1.5" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - provider: - dependency: "direct main" - description: - name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f - url: "https://pub.dev" - source: hosted - version: "6.0.5" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 - url: "https://pub.dev" - source: hosted - version: "1.2.3" - recase: - dependency: transitive - description: - name: recase - sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 - url: "https://pub.dev" - source: hosted - version: "4.1.0" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" - url: "https://pub.dev" - source: hosted - version: "0.27.7" - shared_preferences: - dependency: "direct main" - description: - name: shared_preferences - sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076 - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: f39696b83e844923b642ce9dd4bd31736c17e697f6731a5adf445b1274cf3cd4 - url: "https://pub.dev" - source: hosted - version: "2.3.2" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - shelf_static: - dependency: transitive - description: - name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e - url: "https://pub.dev" - source: hosted - version: "1.1.2" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - source_maps: - dependency: transitive - description: - name: source_maps - sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" - url: "https://pub.dev" - source: hosted - version: "0.10.12" - source_span: - dependency: transitive - description: - name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 - url: "https://pub.dev" - source: hosted - version: "1.9.1" - sqflite: - dependency: "direct main" - description: - name: sqflite - sha256: b4d6710e1200e96845747e37338ea8a819a12b51689a3bcf31eff0003b37a0b9 - url: "https://pub.dev" - source: hosted - version: "2.2.8+4" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "8f7603f3f8f126740bc55c4ca2d1027aab4b74a1267a3e31ce51fe40e3b65b8f" - url: "https://pub.dev" - source: hosted - version: "2.4.5+1" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 - url: "https://pub.dev" - source: hosted - version: "1.11.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" - url: "https://pub.dev" - source: hosted - version: "3.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test: - dependency: transitive - description: - name: test - sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" - url: "https://pub.dev" - source: hosted - version: "1.24.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb - url: "https://pub.dev" - source: hosted - version: "0.5.1" - test_core: - dependency: transitive - description: - name: test_core - sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - toml: - dependency: transitive - description: - name: toml - sha256: "157c5dca5160fced243f3ce984117f729c788bb5e475504f3dbcda881accee44" - url: "https://pub.dev" - source: hosted - version: "0.14.0" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - uuid: - dependency: "direct main" - description: - name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" - url: "https://pub.dev" - source: hosted - version: "3.0.7" - vector_graphics: - dependency: transitive - description: - name: vector_graphics - sha256: "670f6e07aca990b4a2bcdc08a784193c4ccdd1932620244c3a86bb72a0eac67f" - url: "https://pub.dev" - source: hosted - version: "1.1.7" - vector_graphics_codec: - dependency: transitive - description: - name: vector_graphics_codec - sha256: "7451721781d967db9933b63f5733b1c4533022c0ba373a01bdd79d1a5457f69f" - url: "https://pub.dev" - source: hosted - version: "1.1.7" - vector_graphics_compiler: - dependency: transitive - description: - name: vector_graphics_compiler - sha256: "80a13c613c8bde758b1464a1755a7b3a8f2b6cec61fbf0f5a53c94c30f03ba2e" - url: "https://pub.dev" - source: hosted - version: "1.1.7" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: ada49637c27973c183dad90beb6bd781eea4c9f5f955d35da172de0af7bd3440 - url: "https://pub.dev" - source: hosted - version: "11.8.0" - watcher: - dependency: transitive - description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.dev" - source: hosted - version: "2.4.0" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - win32: - dependency: transitive - description: - name: win32 - sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee - url: "https://pub.dev" - source: hosted - version: "5.0.5" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff - url: "https://pub.dev" - source: hosted - version: "1.0.1" - xml: - dependency: transitive - description: - name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" - url: "https://pub.dev" - source: hosted - version: "6.3.0" - xml2json: - dependency: transitive - description: - name: xml2json - sha256: c8cb35b83cce879c2ea86951fd257f4e765b0030a0298b35cf94f2b3d0f32095 - url: "https://pub.dev" - source: hosted - version: "5.3.6" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" -sdks: - dart: ">=3.0.0 <4.0.0" - flutter: ">=3.7.0-0" diff --git a/app/pubspec.yaml b/app/pubspec.yaml index f5be0662..eb4cb2dd 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -54,6 +54,8 @@ dependencies: crypto: ^3.0.3 convert: ^3.1.1 uuid: ^3.0.7 + package_info_plus: ^4.0.2 + url_launcher: ^6.1.12 dev_dependencies: flutter_test: diff --git a/app/windows/flutter/generated_plugin_registrant.cc b/app/windows/flutter/generated_plugin_registrant.cc index 8777c93d..5777988d 100644 --- a/app/windows/flutter/generated_plugin_registrant.cc +++ b/app/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,11 @@ #include "generated_plugin_registrant.h" #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/app/windows/flutter/generated_plugins.cmake b/app/windows/flutter/generated_plugins.cmake index cc1361d8..31032063 100644 --- a/app/windows/flutter/generated_plugins.cmake +++ b/app/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From 008f9ee678a6a6d331371c18a0e22029bc0d9589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 25 Jul 2023 22:41:22 +0200 Subject: [PATCH 131/184] spelling --- app/assets/locales/de/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/locales/de/settings.json b/app/assets/locales/de/settings.json index 8e64ce21..ec9ec65f 100644 --- a/app/assets/locales/de/settings.json +++ b/app/assets/locales/de/settings.json @@ -2,7 +2,7 @@ "colorScheme": "Farbschema", "priceCategory": "Preiskategorie", "about": "Über die App", - "version": "Softwareversion", + "version": "Softwareversion:", "licence": "Diese Software ist unter MIT Lizenz Open Source verfügbar.", "gitHubLink": "Auf Github ansehen", "legalInformation": "Rechtliche Informationen", From f3363bc7d83762c2581f668c2c29a74da5789281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Tue, 25 Jul 2023 23:11:04 +0200 Subject: [PATCH 132/184] initialization --- app/lib/view/favorites/Favorites.dart | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/app/lib/view/favorites/Favorites.dart b/app/lib/view/favorites/Favorites.dart index e69de29b..83e04601 100644 --- a/app/lib/view/favorites/Favorites.dart +++ b/app/lib/view/favorites/Favorites.dart @@ -0,0 +1,45 @@ +import 'package:app/view/core/meal_view_format/MealListEntry.dart'; +import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class Favorites extends StatefulWidget { + const Favorites({super.key}); + + @override + State createState() => _FavoritesState(); +} + +class _FavoritesState extends State{ + @override + Widget build(BuildContext context) { + return Consumer(builder: (context, favoriteAccess, child) => Padding( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + child: FutureBuilder(future: Future.wait([favoriteAccess.getFavoriteMeals()]), + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.hasError) { + return const Column(); + } + + final mealPlan = snapshot.requireData as List; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: mealPlan.length, + itemBuilder: (context, index) { + return MealListEntry(meal: mealPlan[index]); + }, + ), + ], + ); + }, + ), + )); + } + +} \ No newline at end of file From 1facd862bd96368e7fadc9e22fb1de79bbbbac6c Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Wed, 26 Jul 2023 09:26:39 +0200 Subject: [PATCH 133/184] Many Changes but a (almost) working app with mealplan --- app/assets/icons/navigation/arrow_down.svg | 1 + app/assets/icons/navigation/nav_favorites.svg | 1 + app/assets/icons/navigation/nav_mealplan.svg | 1 + app/assets/icons/navigation/nav_settings.svg | 1 + app/lib/main.dart | 245 +++++++----------- .../local_storage/SharedPreferenceAccess.dart | 24 +- app/lib/view/core/MainPage.dart | 54 ++++ .../navigation/NavigationArrowDownIcon.dart | 23 ++ .../navigation/NavigationFavoritesIcon.dart | 23 ++ .../navigation/NavigationMealPlanIcon.dart | 23 ++ .../navigation/NavigationSettingsIcon.dart | 23 ++ app/lib/view/mealplan/MealPlanView.dart | 183 +++++++++++++ app/lib/view/mealplan/Mealplan.dart | 0 app/lib/view/mealplan/MensaCanteenSelect.dart | 50 ++++ .../logic/meal/CombinedMealPlanAccess.dart | 7 +- .../logic/preference/IPreferenceAccess.dart | 2 +- .../logic/preference/PreferenceAccess.dart | 36 +-- .../repository/interface/ILocalStorage.dart | 10 +- app/test/view-model/MealPlanAccessTest.dart | 8 +- app/test/view-model/PreferenceAccessTest.dart | 22 +- 20 files changed, 532 insertions(+), 205 deletions(-) create mode 100644 app/assets/icons/navigation/arrow_down.svg create mode 100644 app/assets/icons/navigation/nav_favorites.svg create mode 100644 app/assets/icons/navigation/nav_mealplan.svg create mode 100644 app/assets/icons/navigation/nav_settings.svg create mode 100644 app/lib/view/core/icons/navigation/NavigationArrowDownIcon.dart create mode 100644 app/lib/view/core/icons/navigation/NavigationFavoritesIcon.dart create mode 100644 app/lib/view/core/icons/navigation/NavigationMealPlanIcon.dart create mode 100644 app/lib/view/core/icons/navigation/NavigationSettingsIcon.dart create mode 100644 app/lib/view/mealplan/MealPlanView.dart delete mode 100644 app/lib/view/mealplan/Mealplan.dart create mode 100644 app/lib/view/mealplan/MensaCanteenSelect.dart diff --git a/app/assets/icons/navigation/arrow_down.svg b/app/assets/icons/navigation/arrow_down.svg new file mode 100644 index 00000000..335a37b4 --- /dev/null +++ b/app/assets/icons/navigation/arrow_down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/navigation/nav_favorites.svg b/app/assets/icons/navigation/nav_favorites.svg new file mode 100644 index 00000000..56c4823a --- /dev/null +++ b/app/assets/icons/navigation/nav_favorites.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/navigation/nav_mealplan.svg b/app/assets/icons/navigation/nav_mealplan.svg new file mode 100644 index 00000000..99b372c4 --- /dev/null +++ b/app/assets/icons/navigation/nav_mealplan.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/navigation/nav_settings.svg b/app/assets/icons/navigation/nav_settings.svg new file mode 100644 index 00000000..02b87555 --- /dev/null +++ b/app/assets/icons/navigation/nav_settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/lib/main.dart b/app/lib/main.dart index c487b7b0..cd3b2916 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -2,14 +2,20 @@ import 'package:app/model/api_server/GraphQlServerAccess.dart'; import 'package:app/model/api_server/config.dart'; import 'package:app/model/database/SQLiteDatabaseAccess.dart'; import 'package:app/model/local_storage/SharedPreferenceAccess.dart'; +import 'package:app/view/core/MainPage.dart'; import 'package:app/view/core/MensaAppBar.dart'; import 'package:app/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart'; import 'package:app/view/core/icons/navigation/NavigationListOutlinedIcon.dart'; import 'package:app/view/core/meal_view_format/MealGrid.dart'; import 'package:app/view/core/selection_components/MensaDropdown.dart'; import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; +import 'package:app/view/mealplan/MealPlanClosed.dart'; import 'package:app/view/mealplan/MealPlanDateSelect.dart'; +import 'package:app/view/mealplan/MealPlanError.dart'; +import 'package:app/view/mealplan/MealPlanFilter.dart'; +import 'package:app/view/mealplan/MealPlanNoData.dart'; import 'package:app/view/mealplan/MealPlanToolbar.dart'; +import 'package:app/view/mealplan/MealPlanView.dart'; import 'package:app/view_model/logic/favorite/FavoriteMealAccess.dart'; import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; import 'package:app/view_model/logic/image/IImageAccess.dart'; @@ -19,6 +25,7 @@ import 'package:app/view_model/logic/meal/IMealAccess.dart'; import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; import 'package:app/view_model/logic/preference/PreferenceAccess.dart'; import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; +import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; @@ -54,15 +61,15 @@ void main() { ); WidgetsFlutterBinding.ensureInitialized(); - runApp(MyApp( + runApp(MensaApp( delegate: delegate, )); } -class MyApp extends StatelessWidget { +class MensaApp extends StatelessWidget { final FlutterI18nDelegate _delegate; - const MyApp({super.key, required FlutterI18nDelegate delegate}) + const MensaApp({super.key, required FlutterI18nDelegate delegate}) : _delegate = delegate; // This widget is the root of your application. @@ -71,77 +78,92 @@ class MyApp extends StatelessWidget { return FutureBuilder( future: SharedPreferences.getInstance(), builder: (context, sharedPreferences) { + if (!sharedPreferences.hasData) { + return const Center( + child: CircularProgressIndicator(), + ); + } + if (sharedPreferences.hasError) { + return Center(child: Text(sharedPreferences.error.toString())); + } ILocalStorage sharedPreferencesAccess = SharedPreferenceAccess(sharedPreferences.requireData); - return FutureBuilder( - future: sharedPreferencesAccess.getClientIdentifier(), - builder: (context, clientIdentifier) { - IDatabaseAccess db = SQLiteDatabaseAccess(); - IServerAccess api = GraphQlServerAccess( - testServer, testApiKey, clientIdentifier.data.toString()); - return MultiProvider( - providers: [ - ChangeNotifierProvider( - create: (context) => CombinedMealPlanAccess( - sharedPreferencesAccess, - api, - db, - )), - ChangeNotifierProvider( - create: (context) => FavoriteMealAccess(db)), - ChangeNotifierProvider( - create: (context) => - PreferenceAccess(sharedPreferencesAccess)), - ChangeNotifierProvider( - create: (context) => ImageAccess(api)), + IDatabaseAccess db = SQLiteDatabaseAccess(); + IServerAccess api = GraphQlServerAccess(testServer, testApiKey, + sharedPreferencesAccess.getClientIdentifier() ?? ""); + return MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (context) => CombinedMealPlanAccess( + sharedPreferencesAccess, + api, + db, + )), + ChangeNotifierProvider( + create: (context) => FavoriteMealAccess(db)), + ChangeNotifierProvider( + create: (context) => + PreferenceAccess(sharedPreferencesAccess)), + ChangeNotifierProvider( + create: (context) => ImageAccess(api)), + ], + child: Consumer( + builder: (context, preferenceAccess, child) => MaterialApp( + title: 'Mensa App', + themeMode: (() { + switch (preferenceAccess.getColorScheme()) { + case MensaColorScheme.light: + return ThemeMode.light; + case MensaColorScheme.dark: + return ThemeMode.dark; + case MensaColorScheme.system: + return ThemeMode.system; + } + }()), + localizationsDelegates: [ + _delegate, + ...GlobalMaterialLocalizations.delegates, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: const [ + Locale('de'), ], - child: MaterialApp( - title: 'Mensa App', - localizationsDelegates: [ - _delegate, - ...GlobalMaterialLocalizations.delegates, - GlobalWidgetsLocalizations.delegate, - ], - supportedLocales: const [ - Locale('de'), - ], - theme: ThemeData( - useMaterial3: true, - brightness: Brightness.light, - colorScheme: const ColorScheme( - brightness: Brightness.light, - primary: Color(0xFF7AAC2B), - onPrimary: Color(0xFFFFFFFF), - secondary: Color(0xFF7AAC2B), - onSecondary: Color(0xFFFFFFFF), - error: Color(0xFFD32F2F), - onError: Color(0xFFFFFFFF), - background: Color(0xFFFFFFFF), - onBackground: Color(0xFF000000), - surface: Color(0xFFF6F6F6), - onSurface: Color(0xFF000000)), - ), - darkTheme: ThemeData( - useMaterial3: true, - brightness: Brightness.dark, - colorScheme: const ColorScheme( - brightness: Brightness.dark, - primary: Color(0xFF7AAC2B), - onPrimary: Color(0xFFFFFFFF), - secondary: Color(0xFF7AAC2B), - onSecondary: Color(0xFFFFFFFF), - error: Color(0xFFD32F2F), - onError: Color(0xFFFFFFFF), - background: Color(0xFF1E1E1E), - onBackground: Color(0xFFFFFFFF), - surface: Color(0xFF333333), - surfaceTint: Color(0xFF202020), - onSurface: Color(0xFFFFFFFF)), - ), - home: const MyHomePage(), + theme: ThemeData( + useMaterial3: true, + brightness: Brightness.light, + colorScheme: const ColorScheme( + brightness: Brightness.light, + primary: Color(0xFF7AAC2B), + onPrimary: Color(0xFFFFFFFF), + secondary: Color(0xFF7AAC2B), + onSecondary: Color(0xFFFFFFFF), + error: Color(0xFFD32F2F), + onError: Color(0xFFFFFFFF), + background: Color(0xFFFFFFFF), + onBackground: Color(0xFF000000), + surface: Color(0xFFF6F6F6), + onSurface: Color(0xFF000000)), + ), + darkTheme: ThemeData( + useMaterial3: true, + brightness: Brightness.dark, + colorScheme: const ColorScheme( + brightness: Brightness.dark, + primary: Color(0xFF7AAC2B), + onPrimary: Color(0xFFFFFFFF), + secondary: Color(0xFF7AAC2B), + onSecondary: Color(0xFFFFFFFF), + error: Color(0xFFD32F2F), + onError: Color(0xFFFFFFFF), + background: Color(0xFF1E1E1E), + onBackground: Color(0xFFFFFFFF), + surface: Color(0xFF333333), + surfaceTint: Color(0xFF202020), + onSurface: Color(0xFFFFFFFF)), ), - ); - }); + home: MainPage() + ), + )); }); } } @@ -163,7 +185,6 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - @override Widget build(BuildContext context) { // This method is rerun every time setState is called, for instance as done @@ -172,80 +193,12 @@ class _MyHomePageState extends State { // The Flutter framework has been optimized to make rerunning build methods // fast, so that you can just rebuild anything that needs updating rather // than having to individually change instances of widgets. - return SafeArea( - child: Scaffold( - backgroundColor: Theme.of(context).colorScheme.background, - appBar: MensaAppBar( - bottom: MealPlanToolbar( - child: Row( - children: [ - NavigationListOutlinedIcon(), - Spacer(), - Consumer( - builder: (context, mealAccess, child) => StreamBuilder( - stream: mealAccess.getDate().asStream(), - builder: (context, date) { - return MealPlanDateSelect( - date: date.requireData, - onDateChanged: (date) => mealAccess.changeDate(date), - ); - })), - Spacer(), - NavigationFilterOutlinedIcon(), - ], - )), - child: Consumer( - builder: (context, mealAccess, child) => StreamBuilder( - stream: mealAccess.getAvailableCanteens().asStream(), - builder: (context, availableCanteens) => StreamBuilder( - stream: mealAccess.getCanteen().asStream(), - builder: (context, selectedCanteen) { - if (!availableCanteens.hasData || !selectedCanteen.hasData) - return const Text("Loading..."); - if (availableCanteens.hasError) - return Text(availableCanteens.error.toString()); - if (selectedCanteen.hasError) - return Text(selectedCanteen.error.toString()); - return MensaDropdown( - backgroundColor: - Theme.of(context).colorScheme.background, - onChanged: (v) { - mealAccess.changeCanteen(availableCanteens.requireData - .firstWhere((element) => element.id == v)); - }, - value: selectedCanteen.requireData.id, - items: availableCanteens.requireData - .map((e) => MensaDropdownEntry( - value: e.id, - label: e.name, - )) - .toList()); - }), - ), - )), - body: Consumer( - builder: (context, mealAccess, child) => RefreshIndicator( - onRefresh: () async { - String? error = await mealAccess.refreshMealplan(); - if (error != null) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: I18nText(error))); - } - }, - child: StreamBuilder( - stream: mealAccess.getMealPlan().asStream(), - builder: (context, mealPlans) { - if (!mealPlans.hasData) return const Text("Loading..."); - if (mealPlans.hasError) { - return Text(mealPlans.error.toString()); - } - switch (mealPlans.requireData) { - case Success, MealPlanException> value: - return MealGrid(mealPlans: value.value); - case Failure, MealPlanException> exception: - return Text(exception.exception.toString()); - } - }))), - )); + return Container( + color: Theme.of(context).colorScheme.background, + child: SafeArea( + child: Scaffold( + backgroundColor: Theme.of(context).colorScheme.background, + body: MealPlanView(), + ))); } } diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index 9df83ca4..d9a65e97 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -22,15 +22,15 @@ class SharedPreferenceAccess implements ILocalStorage { SharedPreferenceAccess(this._pref); @override - Future getClientIdentifier() async { - final clientIdentifier = _pref.getString('clientIdentifier') ?? ""; - return Future.value(clientIdentifier); + String? getClientIdentifier() { + final clientIdentifier = _pref.getString('clientIdentifier') ?? "1"; + return clientIdentifier; } @override - Future getColorScheme() async { + MensaColorScheme? getColorScheme() { final colorScheme = _pref.getString('colorScheme'); - return Future.value(MensaColorScheme.values.firstWhere((e) => e.toString() == colorScheme)); + return MensaColorScheme.values.firstWhere((e) => e.toString() == colorScheme, orElse: () => MensaColorScheme.system); } @override @@ -80,15 +80,15 @@ class SharedPreferenceAccess implements ILocalStorage { } @override - Future getMealPlanFormat() async { + MealPlanFormat? getMealPlanFormat() { final mealPlanFormat = _pref.getString('mealPlanFormat'); - return Future.value(MealPlanFormat.values.firstWhere((e) => e.toString() == mealPlanFormat)); + return MealPlanFormat.values.firstWhere((e) => e.toString() == mealPlanFormat, orElse: () => MealPlanFormat.grid); } @override - Future getPriceCategory() async { + PriceCategory? getPriceCategory() { final String? priceCategory = _pref.getString('priceCategory'); - return Future.value(PriceCategory.values.firstWhere((e) => e.toString() == priceCategory, orElse: () => PriceCategory.student)); + return PriceCategory.values.firstWhere((e) => e.toString() == priceCategory, orElse: () => PriceCategory.student); } @override @@ -124,9 +124,9 @@ class SharedPreferenceAccess implements ILocalStorage { } @override - Future getCanteen() async { - final canteen = _pref.getString('canteen') ?? ""; - return Future.value(canteen); + String? getCanteen() { + final canteen = _pref.getString('canteen'); + return canteen; } @override diff --git a/app/lib/view/core/MainPage.dart b/app/lib/view/core/MainPage.dart index e69de29b..2341a4f1 100644 --- a/app/lib/view/core/MainPage.dart +++ b/app/lib/view/core/MainPage.dart @@ -0,0 +1,54 @@ +import 'package:app/view/core/icons/navigation/NavigationFavoritesIcon.dart'; +import 'package:app/view/core/icons/navigation/NavigationMealPlanIcon.dart'; +import 'package:app/view/core/icons/navigation/NavigationSettingsIcon.dart'; +import 'package:app/view/mealplan/MealPlanView.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; + +class MainPage extends StatefulWidget { + @override + State createState() => _MainPageState(); +} + +class _MainPageState extends State { + int _selectedIndex = 0; + + @override + Widget build(BuildContext context) { + return Container( + color: Theme.of(context).colorScheme.background, + child: SafeArea( + child: Scaffold( + body: IndexedStack( + index: _selectedIndex, + children: [ + MealPlanView(), + Container(color: Colors.green), + Container(color: Colors.blue), + ], + ), + bottomNavigationBar: BottomNavigationBar( + showUnselectedLabels: false, + elevation: 2, + currentIndex: _selectedIndex, + onTap: (index) => setState(() => _selectedIndex = index), + items: [ + BottomNavigationBarItem( + icon: NavigationMealPlanIcon(), + label: FlutterI18n.translate(context, "common.mealPlan"), + ), + BottomNavigationBarItem( + icon: NavigationFavoritesIcon(), + label: FlutterI18n.translate(context, "common.favorites"), + ), + BottomNavigationBarItem( + icon: NavigationSettingsIcon(), + label: FlutterI18n.translate(context, "common.settings"), + ), + ], + ), + ), + ), + ); + } +} diff --git a/app/lib/view/core/icons/navigation/NavigationArrowDownIcon.dart b/app/lib/view/core/icons/navigation/NavigationArrowDownIcon.dart new file mode 100644 index 00000000..e5b4bf28 --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationArrowDownIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationArrowDownIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationArrowDownIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/arrow_down.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/navigation/NavigationFavoritesIcon.dart b/app/lib/view/core/icons/navigation/NavigationFavoritesIcon.dart new file mode 100644 index 00000000..bd0ced56 --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationFavoritesIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationFavoritesIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationFavoritesIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/nav_favorites.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/navigation/NavigationMealPlanIcon.dart b/app/lib/view/core/icons/navigation/NavigationMealPlanIcon.dart new file mode 100644 index 00000000..49475afd --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationMealPlanIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationMealPlanIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationMealPlanIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/nav_mealplan.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/navigation/NavigationSettingsIcon.dart b/app/lib/view/core/icons/navigation/NavigationSettingsIcon.dart new file mode 100644 index 00000000..95fa8f01 --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationSettingsIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationSettingsIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationSettingsIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/nav_settings.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/mealplan/MealPlanView.dart b/app/lib/view/mealplan/MealPlanView.dart new file mode 100644 index 00000000..40a4308d --- /dev/null +++ b/app/lib/view/mealplan/MealPlanView.dart @@ -0,0 +1,183 @@ +import 'package:app/view/core/MensaAppBar.dart'; +import 'package:app/view/core/buttons/MensaTapable.dart'; +import 'package:app/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart'; +import 'package:app/view/core/icons/navigation/NavigationGridOutlinedIcon.dart'; +import 'package:app/view/core/icons/navigation/NavigationListOutlinedIcon.dart'; +import 'package:app/view/core/meal_view_format/MealGrid.dart'; +import 'package:app/view/core/meal_view_format/MealList.dart'; +import 'package:app/view/core/selection_components/MensaDropdown.dart'; +import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; +import 'package:app/view/mealplan/MealPlanClosed.dart'; +import 'package:app/view/mealplan/MealPlanDateSelect.dart'; +import 'package:app/view/mealplan/MealPlanError.dart'; +import 'package:app/view/mealplan/MealPlanFilter.dart'; +import 'package:app/view/mealplan/MealPlanNoData.dart'; +import 'package:app/view/mealplan/MealPlanToolbar.dart'; +import 'package:app/view/mealplan/MensaCanteenSelect.dart'; +import 'package:app/view_model/logic/meal/IMealAccess.dart'; +import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; +import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; +import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; +import 'package:app/view_model/repository/error_handling/Result.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/widgets/I18nText.dart'; +import 'package:provider/provider.dart'; + +class MealPlanView extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, preferenceAccess, child) => + Consumer(builder: (context, mealAccess, child) { + return FutureBuilder( + future: Future.wait([ + preferenceAccess.getMealPlanFormat(), + mealAccess.getCanteen(), + mealAccess.getAvailableCanteens(), + mealAccess.getDate(), + mealAccess.getMealPlan(), + ], eagerError: true), + builder: (context, snapshot) { + print(snapshot.data.toString()); + if (!snapshot.hasData) { + return const Center( + child: CircularProgressIndicator(), + ); + } + if (snapshot.hasError) return const MealPlanError(); + MealPlanFormat mealPlanFormat = + snapshot.requireData[0] as MealPlanFormat; + Canteen selectedCanteen = + snapshot.requireData[1] as Canteen; + List availableCanteens = + snapshot.requireData[2] as List; + DateTime date = snapshot.requireData[3] as DateTime; + Result, MealPlanException> mealPlans = + snapshot.requireData[4] + as Result, MealPlanException>; + return Scaffold( + appBar: MensaAppBar( + appBarHeight: kToolbarHeight * 1.25, + bottom: MealPlanToolbar( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8), + child: Row( + children: [ + MensaTapable( + child: Padding( + padding: const EdgeInsets.all(8), + child: mealPlanFormat == + MealPlanFormat.grid + ? const NavigationListOutlinedIcon() + : const NavigationGridOutlinedIcon()), + onTap: () { + preferenceAccess.setMealPlanFormat( + mealPlanFormat == + MealPlanFormat.grid + ? MealPlanFormat.list + : MealPlanFormat.grid); + }), + const Spacer(), + MealPlanDateSelect( + date: date, + onDateChanged: (date) => + mealAccess.changeDate(date), + ), + const Spacer(), + MensaTapable( + child: const Padding( + padding: EdgeInsets.all(8), + child: + NavigationFilterOutlinedIcon()), + onTap: () => {}) + ], + ))), + child: Padding( + padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), + child: Row(children: [ + Expanded( + child: MensaCanteenSelect( + selectedCanteen: selectedCanteen, + availableCanteens: availableCanteens, + onCanteenSelected: (canteen) => + mealAccess.changeCanteen(canteen))) + ])), + ), + body: RefreshIndicator( + onRefresh: () async { + String? error = await mealAccess.refreshMealplan(); + if (error != null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: I18nText(error))); + } + }, + child: (() { + switch (mealPlans) { + case Success, MealPlanException> + value: + return mealPlanFormat == MealPlanFormat.grid + ? MealGrid(mealPlans: value.value) + : MealList(mealPlans: value.value); + case Failure, MealPlanException> + exception: + if (exception.exception + is NoConnectionException) { + return SingleChildScrollView( + physics: + const AlwaysScrollableScrollPhysics(), + child: SizedBox( + height: + MediaQuery.of(context).size.height, + child: const MealPlanError()), + ); + } + if (exception.exception is NoDataException) { + return SingleChildScrollView( + physics: + const AlwaysScrollableScrollPhysics(), + child: SizedBox( + height: + MediaQuery.of(context).size.height, + child: const MealPlanNoData()), + ); + } + if (exception.exception + is ClosedCanteenException) { + return SingleChildScrollView( + physics: + const AlwaysScrollableScrollPhysics(), + child: SizedBox( + height: + MediaQuery.of(context).size.height, + child: const MealPlanClosed()), + ); + } + if (exception.exception + is FilteredMealException) { + return SingleChildScrollView( + physics: + const AlwaysScrollableScrollPhysics(), + child: SizedBox( + height: + MediaQuery.of(context).size.height, + child: const MealPlanFilter()), + ); + } + return SingleChildScrollView( + physics: + const AlwaysScrollableScrollPhysics(), + child: SizedBox( + height: + MediaQuery.of(context).size.height, + child: const MealPlanError()), + ); + } + }()), + )); + }); + })); + } +} diff --git a/app/lib/view/mealplan/Mealplan.dart b/app/lib/view/mealplan/Mealplan.dart deleted file mode 100644 index e69de29b..00000000 diff --git a/app/lib/view/mealplan/MensaCanteenSelect.dart b/app/lib/view/mealplan/MensaCanteenSelect.dart new file mode 100644 index 00000000..634a1f6e --- /dev/null +++ b/app/lib/view/mealplan/MensaCanteenSelect.dart @@ -0,0 +1,50 @@ +import 'package:app/view/core/icons/navigation/NavigationArrowDownIcon.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; +import 'package:flutter/material.dart'; + +class MensaCanteenSelect extends StatelessWidget { + final List _availableCanteens; + final Canteen _selectedCanteen; + final Function(Canteen) _onCanteenSelected; + + const MensaCanteenSelect( + {super.key, + required List availableCanteens, + required Canteen selectedCanteen, + required Function(Canteen) onCanteenSelected}) + : _availableCanteens = availableCanteens, + _selectedCanteen = selectedCanteen, + _onCanteenSelected = onCanteenSelected; + + @override + Widget build(BuildContext context) { + return DropdownButtonHideUnderline( + child: DropdownButton( + selectedItemBuilder: (context) => _availableCanteens + .map((e) => Row(children: [ + SizedBox( + width: 40, + ), + Container( + alignment: Alignment.center, + constraints: BoxConstraints( + minWidth: MediaQuery.of(context).size.width - + 2 * 8 - + 2 * 40), + child: Text( + e.name, + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold), + ), + ) + ])) + .toList(), + icon: const NavigationArrowDownIcon(size: 40), + value: _selectedCanteen.id, + items: _availableCanteens + .map((e) => DropdownMenuItem(value: e.id, child: Center(child: Text(e.name)))) + .toList(), + onChanged: (value) => _onCanteenSelected(_availableCanteens + .firstWhere((element) => element.id == value)))); + } +} diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 858870a1..961f9ef0 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -41,8 +41,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { Future _init() async { _displayedDate = DateTime.timestamp(); _filter = await _preferences.getFilterPreferences() ?? FilterPreferences(); - _priceCategory = - await _preferences.getPriceCategory() ?? PriceCategory.student; + _priceCategory = _preferences.getPriceCategory() ?? PriceCategory.student; // get meal plans form server List mealPlans = switch (await _api.updateAll()) { @@ -57,10 +56,10 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { // get canteen from string // get canteen id from local storage - final canteenString = await _preferences.getCanteen(); + final canteenString = _preferences.getCanteen(); Canteen? canteen; // get default canteen from server if canteen id not saved in local storage - if (canteenString == null) { + if (canteenString == null || canteenString.isEmpty) { canteen = await _api.getDefaultCanteen(); // save canteen id in local storage diff --git a/app/lib/view_model/logic/preference/IPreferenceAccess.dart b/app/lib/view_model/logic/preference/IPreferenceAccess.dart index 386a9f85..47c55128 100644 --- a/app/lib/view_model/logic/preference/IPreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/IPreferenceAccess.dart @@ -17,7 +17,7 @@ abstract class IPreferenceAccess with ChangeNotifier { /// The saved ColorScheme is returned. /// @return The saved ColorScheme. - Future getColorScheme(); + MensaColorScheme getColorScheme(); /// The committed ColorScheme is set. /// @param scheme The new ColorScheme. diff --git a/app/lib/view_model/logic/preference/PreferenceAccess.dart b/app/lib/view_model/logic/preference/PreferenceAccess.dart index 97c03421..e0949730 100644 --- a/app/lib/view_model/logic/preference/PreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/PreferenceAccess.dart @@ -1,6 +1,6 @@ import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; -import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; +import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:app/view_model/repository/interface/ILocalStorage.dart'; @@ -15,19 +15,20 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { late PriceCategory _priceCategory; late MealPlanFormat _mealPlanFormat; - // waits until _init() is finished initializing - late Future _doneInitialization; - PreferenceAccess(this._access) { - _doneInitialization = _init(); + _init(); } - Future _init() async { - _clientIdentifier = await _access.getClientIdentifier() ?? ""; - _colorScheme = await _access.getColorScheme() ?? MensaColorScheme.system; - _mealPlanFormat = await _access.getMealPlanFormat() ?? MealPlanFormat.grid; + void _init() { + _clientIdentifier = _access.getClientIdentifier() ?? ""; + print("cidd: $_clientIdentifier"); + _colorScheme = _access.getColorScheme() ?? MensaColorScheme.system; + print("cs: $_colorScheme"); + _mealPlanFormat = _access.getMealPlanFormat() ?? MealPlanFormat.grid; + print("mpf: $_mealPlanFormat"); - PriceCategory? category = await _access.getPriceCategory(); + PriceCategory? category = _access.getPriceCategory(); + print("pc: $category"); if (category == null) { category = PriceCategory.student; _access.setPriceCategory(category); @@ -38,31 +39,26 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { @override Future getClientIdentifier() async { - await _doneInitialization; return Future.value(_clientIdentifier); } @override - Future getColorScheme() async { - await _doneInitialization; - return Future.value(_colorScheme); + MensaColorScheme getColorScheme() { + return _colorScheme; } @override Future getMealPlanFormat() async { - await _doneInitialization; return Future.value(_mealPlanFormat); } @override Future getPriceCategory() async { - await _doneInitialization; return Future.value(_priceCategory); } @override Future setClientIdentifier(String identifier) async { - await _doneInitialization; _clientIdentifier = identifier; await _access.setClientIdentifier(identifier); notifyListeners(); @@ -70,7 +66,6 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { @override Future setColorScheme(MensaColorScheme scheme) async { - await _doneInitialization; _colorScheme = scheme; await _access.setColorScheme(scheme); notifyListeners(); @@ -78,7 +73,6 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { @override Future setMealPlanFormat(MealPlanFormat format) async { - await _doneInitialization; _mealPlanFormat = format; await _access.setMealPlanFormat(format); notifyListeners(); @@ -86,10 +80,8 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { @override Future setPriceCategory(PriceCategory category) async { - await _doneInitialization; _priceCategory = category; await _access.setPriceCategory(category); notifyListeners(); } - -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/interface/ILocalStorage.dart b/app/lib/view_model/repository/interface/ILocalStorage.dart index b63358ea..a6789923 100644 --- a/app/lib/view_model/repository/interface/ILocalStorage.dart +++ b/app/lib/view_model/repository/interface/ILocalStorage.dart @@ -8,7 +8,7 @@ import '../data_classes/settings/PriceCategory.dart'; abstract class ILocalStorage { /// The device identifier is returned. /// @return The device identifier. - Future getClientIdentifier(); + String? getClientIdentifier(); /// The device identifier is set. /// @param identifier The new device identifier. @@ -26,7 +26,7 @@ abstract class ILocalStorage { /// The saved canteen id is returned. /// @return The saved canteen id. - Future getCanteen(); + String? getCanteen(); /// The committed id of the canteen is set. /// @param canteen The id of the new canteen. @@ -35,7 +35,7 @@ abstract class ILocalStorage { /// The saved ColorScheme is returned. /// @return The saved ColorScheme. - Future getColorScheme(); + MensaColorScheme? getColorScheme(); /// The committed ColorScheme is set. /// @param scheme The new ColorScheme. @@ -45,7 +45,7 @@ abstract class ILocalStorage { /// The saved PriceCategory is returned. /// @return The saved PriceCategory. /// @return The result of the update. - Future getPriceCategory(); + PriceCategory? getPriceCategory(); /// The committed PriceCategory is set. /// @param category The new PriceCategory. @@ -54,7 +54,7 @@ abstract class ILocalStorage { /// The saved MealPlanFormat is returned. /// @return The saved MealPlanFormat. - Future getMealPlanFormat(); + MealPlanFormat? getMealPlanFormat(); /// The committed MealPlanFormat is set. /// @param format The new MealPlanFormat. diff --git a/app/test/view-model/MealPlanAccessTest.dart b/app/test/view-model/MealPlanAccessTest.dart index b8bf33e8..b322a6eb 100644 --- a/app/test/view-model/MealPlanAccessTest.dart +++ b/app/test/view-model/MealPlanAccessTest.dart @@ -168,9 +168,9 @@ void main() { setUp(() { when(() => localStorage.getFilterPreferences()) .thenAnswer((_) async => null); - when(() => localStorage.getCanteen()).thenAnswer((_) async => canteenID); + when(() => localStorage.getCanteen()).thenAnswer((_) => canteenID); when(() => localStorage.getPriceCategory()) - .thenAnswer((_) async => PriceCategory.student); + .thenAnswer((_) => PriceCategory.student); when(() => api.updateAll()) .thenAnswer((_) async => Failure(NoConnectionException("error"))); @@ -211,7 +211,7 @@ void main() { .thenAnswer((_) async {}); when(() => database.getFavorites()).thenAnswer((_) async => favorites); when(() => localStorage.getPriceCategory()) - .thenAnswer((_) async => PriceCategory.student); + .thenAnswer((_) => PriceCategory.student); group("allergens", () { test("change allergens er", () async { @@ -578,7 +578,7 @@ void main() { test("price limit employee", () async { when(() => localStorage.getPriceCategory()) - .thenAnswer((_) async => PriceCategory.employee); + .thenAnswer((_) => PriceCategory.employee); when(() => database.getFavorites()).thenAnswer((_) async => favorites); mealPlanAccess.switchToMealPlanView(); diff --git a/app/test/view-model/PreferenceAccessTest.dart b/app/test/view-model/PreferenceAccessTest.dart index 70163a83..0eac7549 100644 --- a/app/test/view-model/PreferenceAccessTest.dart +++ b/app/test/view-model/PreferenceAccessTest.dart @@ -14,10 +14,10 @@ void main () { late PreferenceAccess preferencesPredefined; setUp(() async { - when(() => localStorage.getClientIdentifier()).thenAnswer((_) => Future.value(null)); - when(() => localStorage.getColorScheme()).thenAnswer((_) => Future.value(null)); - when(() => localStorage.getPriceCategory()).thenAnswer((_) => Future.value(null)); - when(() => localStorage.getMealPlanFormat()).thenAnswer((_) => Future.value(null)); + when(() => localStorage.getClientIdentifier()).thenAnswer((_) => null); + when(() => localStorage.getColorScheme()).thenAnswer((_) => null); + when(() => localStorage.getPriceCategory()).thenAnswer((_) => null); + when(() => localStorage.getMealPlanFormat()).thenAnswer((_) => null); preferences = PreferenceAccess(localStorage); }); @@ -28,7 +28,7 @@ void main () { }); test("color scheme", () async { - expect(await preferences.getColorScheme(), MensaColorScheme.system); + expect(preferences.getColorScheme(), MensaColorScheme.system); }); test("meal plan format", () async { @@ -56,7 +56,7 @@ void main () { await preferences.setColorScheme(scheme); verify(() => localStorage.setColorScheme(scheme)).called(1); - expect(await preferences.getColorScheme(), scheme); + expect(preferences.getColorScheme(), scheme); }); test("set Meal Plan Format", () async { @@ -79,10 +79,10 @@ void main () { }); group("initialization with non standard values", () { - when(() => localStorage.getClientIdentifier()).thenAnswer((_) => Future.value("42")); - when(() => localStorage.getColorScheme()).thenAnswer((_) => Future.value(MensaColorScheme.light)); - when(() => localStorage.getPriceCategory()).thenAnswer((_) => Future.value(PriceCategory.employee)); - when(() => localStorage.getMealPlanFormat()).thenAnswer((_) => Future.value(MealPlanFormat.list)); + when(() => localStorage.getClientIdentifier()).thenAnswer((_) => "42"); + when(() => localStorage.getColorScheme()).thenAnswer((_) => MensaColorScheme.light); + when(() => localStorage.getPriceCategory()).thenAnswer((_) => PriceCategory.employee); + when(() => localStorage.getMealPlanFormat()).thenAnswer((_) => MealPlanFormat.list); preferencesPredefined = PreferenceAccess(localStorage); @@ -91,7 +91,7 @@ void main () { }); test("color scheme", () async { - expect(await preferencesPredefined.getColorScheme(), MensaColorScheme.light); + expect(preferencesPredefined.getColorScheme(), MensaColorScheme.light); }); test("meal plan format", () async { From 74bbb9a5be16c6af7de9b6c374958cc35d0d8cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 10:13:29 +0200 Subject: [PATCH 134/184] make access to all shared preferences synchronous --- .../local_storage/SharedPreferenceAccess.dart | 6 ++-- .../information_display/MealMainEntry.dart | 28 +++++-------------- app/lib/view/mealplan/MealPlanView.dart | 14 ++++------ .../logic/preference/IPreferenceAccess.dart | 6 ++-- .../logic/preference/PreferenceAccess.dart | 16 ++++------- .../repository/interface/ILocalStorage.dart | 2 +- app/test/view-model/MealPlanAccessTest.dart | 2 +- 7 files changed, 26 insertions(+), 48 deletions(-) diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index d9a65e97..19c513e8 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -34,7 +34,7 @@ class SharedPreferenceAccess implements ILocalStorage { } @override - Future getFilterPreferences() async { + FilterPreferences? getFilterPreferences() { // get data from shared preferences final categories = _pref.getStringList('filterCategories'); final allergens = _pref.getStringList('filterAllergens'); @@ -67,7 +67,7 @@ class SharedPreferenceAccess implements ILocalStorage { } // return filter preferences - return Future.value(FilterPreferences( + return FilterPreferences( categories: foodTypeEnum, allergens: allergensEnum, price: price, @@ -76,7 +76,7 @@ class SharedPreferenceAccess implements ILocalStorage { onlyFavorite: onlyFavorites, sortedBy: sortedByEnum, ascending: ascending - )); + ); } @override diff --git a/app/lib/view/core/information_display/MealMainEntry.dart b/app/lib/view/core/information_display/MealMainEntry.dart index eeda61be..020919af 100644 --- a/app/lib/view/core/information_display/MealMainEntry.dart +++ b/app/lib/view/core/information_display/MealMainEntry.dart @@ -1,7 +1,6 @@ import 'package:app/view/core/icons/MealIcon.dart'; import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; -import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; @@ -32,26 +31,13 @@ class MealMainEntry extends StatelessWidget { fontWeight: FontWeight.bold, fontSize: 14, height: 1.5)), ), const SizedBox(width: 8), - FutureBuilder( - future: preferences.getPriceCategory(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Text( - _priceFormat.format( - _meal.price.getPrice(snapshot.requireData) / 100), - style: const TextStyle( - fontSize: 14, - height: 1.5, - )); - } else { - return const Text('', - style: TextStyle( - fontSize: 14, - height: 1.5, - )); - } - }, - ) + Text( + _priceFormat.format( + _meal.price.getPrice(preferences.getPriceCategory()) / 100), + style: const TextStyle( + fontSize: 14, + height: 1.5, + )) ], ), ); diff --git a/app/lib/view/mealplan/MealPlanView.dart b/app/lib/view/mealplan/MealPlanView.dart index 40a4308d..1707926d 100644 --- a/app/lib/view/mealplan/MealPlanView.dart +++ b/app/lib/view/mealplan/MealPlanView.dart @@ -5,8 +5,6 @@ import 'package:app/view/core/icons/navigation/NavigationGridOutlinedIcon.dart'; import 'package:app/view/core/icons/navigation/NavigationListOutlinedIcon.dart'; import 'package:app/view/core/meal_view_format/MealGrid.dart'; import 'package:app/view/core/meal_view_format/MealList.dart'; -import 'package:app/view/core/selection_components/MensaDropdown.dart'; -import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; import 'package:app/view/mealplan/MealPlanClosed.dart'; import 'package:app/view/mealplan/MealPlanDateSelect.dart'; import 'package:app/view/mealplan/MealPlanError.dart'; @@ -33,7 +31,6 @@ class MealPlanView extends StatelessWidget { Consumer(builder: (context, mealAccess, child) { return FutureBuilder( future: Future.wait([ - preferenceAccess.getMealPlanFormat(), mealAccess.getCanteen(), mealAccess.getAvailableCanteens(), mealAccess.getDate(), @@ -47,15 +44,14 @@ class MealPlanView extends StatelessWidget { ); } if (snapshot.hasError) return const MealPlanError(); - MealPlanFormat mealPlanFormat = - snapshot.requireData[0] as MealPlanFormat; + MealPlanFormat mealPlanFormat = preferenceAccess.getMealPlanFormat(); Canteen selectedCanteen = - snapshot.requireData[1] as Canteen; + snapshot.requireData[0] as Canteen; List availableCanteens = - snapshot.requireData[2] as List; - DateTime date = snapshot.requireData[3] as DateTime; + snapshot.requireData[1] as List; + DateTime date = snapshot.requireData[2] as DateTime; Result, MealPlanException> mealPlans = - snapshot.requireData[4] + snapshot.requireData[3] as Result, MealPlanException>; return Scaffold( appBar: MensaAppBar( diff --git a/app/lib/view_model/logic/preference/IPreferenceAccess.dart b/app/lib/view_model/logic/preference/IPreferenceAccess.dart index 47c55128..e4df0266 100644 --- a/app/lib/view_model/logic/preference/IPreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/IPreferenceAccess.dart @@ -8,7 +8,7 @@ import '../../repository/data_classes/settings/PriceCategory.dart'; abstract class IPreferenceAccess with ChangeNotifier { /// The client identifier is returned. /// @return The client identifier. - Future getClientIdentifier(); + String getClientIdentifier(); /// The client identifier is set. /// @param identifier The new client identifier. @@ -26,7 +26,7 @@ abstract class IPreferenceAccess with ChangeNotifier { /// The saved PriceCategory is returned. /// @return The saved PriceCategory. - Future getPriceCategory(); + PriceCategory getPriceCategory(); /// The committed PriceCategory is set. /// @param category The new PriceCategory. @@ -35,7 +35,7 @@ abstract class IPreferenceAccess with ChangeNotifier { /// The saved MealPlanFormat is returned. /// @return The saved MealPlanFormat. - Future getMealPlanFormat(); + MealPlanFormat getMealPlanFormat(); /// The committed MealPlanFormat is set. /// @param format The new MealPlanFormat. diff --git a/app/lib/view_model/logic/preference/PreferenceAccess.dart b/app/lib/view_model/logic/preference/PreferenceAccess.dart index e0949730..0e9ecc4c 100644 --- a/app/lib/view_model/logic/preference/PreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/PreferenceAccess.dart @@ -21,14 +21,10 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { void _init() { _clientIdentifier = _access.getClientIdentifier() ?? ""; - print("cidd: $_clientIdentifier"); _colorScheme = _access.getColorScheme() ?? MensaColorScheme.system; - print("cs: $_colorScheme"); _mealPlanFormat = _access.getMealPlanFormat() ?? MealPlanFormat.grid; - print("mpf: $_mealPlanFormat"); PriceCategory? category = _access.getPriceCategory(); - print("pc: $category"); if (category == null) { category = PriceCategory.student; _access.setPriceCategory(category); @@ -38,8 +34,8 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { } @override - Future getClientIdentifier() async { - return Future.value(_clientIdentifier); + String getClientIdentifier() { + return _clientIdentifier; } @override @@ -48,13 +44,13 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { } @override - Future getMealPlanFormat() async { - return Future.value(_mealPlanFormat); + MealPlanFormat getMealPlanFormat() { + return _mealPlanFormat; } @override - Future getPriceCategory() async { - return Future.value(_priceCategory); + PriceCategory getPriceCategory() { + return _priceCategory; } @override diff --git a/app/lib/view_model/repository/interface/ILocalStorage.dart b/app/lib/view_model/repository/interface/ILocalStorage.dart index a6789923..26d83034 100644 --- a/app/lib/view_model/repository/interface/ILocalStorage.dart +++ b/app/lib/view_model/repository/interface/ILocalStorage.dart @@ -17,7 +17,7 @@ abstract class ILocalStorage { /// The saved FilterPreferences is returned. /// @return The saved FilterPreferences. - Future getFilterPreferences(); + FilterPreferences? getFilterPreferences(); /// The committed FilterPreferences is set. /// @param filter The new FilterPreferences. diff --git a/app/test/view-model/MealPlanAccessTest.dart b/app/test/view-model/MealPlanAccessTest.dart index b322a6eb..f3758460 100644 --- a/app/test/view-model/MealPlanAccessTest.dart +++ b/app/test/view-model/MealPlanAccessTest.dart @@ -167,7 +167,7 @@ void main() { setUp(() { when(() => localStorage.getFilterPreferences()) - .thenAnswer((_) async => null); + .thenAnswer((_) => null); when(() => localStorage.getCanteen()).thenAnswer((_) => canteenID); when(() => localStorage.getPriceCategory()) .thenAnswer((_) => PriceCategory.student); From 70fb72f0446e45daf1d44c937d633827bd751d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 10:14:44 +0200 Subject: [PATCH 135/184] delete unused class --- .../repository/data_classes/GraphQLDataConverter.dart | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 app/lib/view_model/repository/data_classes/GraphQLDataConverter.dart diff --git a/app/lib/view_model/repository/data_classes/GraphQLDataConverter.dart b/app/lib/view_model/repository/data_classes/GraphQLDataConverter.dart deleted file mode 100644 index 27c27018..00000000 --- a/app/lib/view_model/repository/data_classes/GraphQLDataConverter.dart +++ /dev/null @@ -1,5 +0,0 @@ - -class GraphQLDataConverter { - // TODO @Jonatan: write method signatures - // TODO @Elena: implement methods specified by Jonatan -} \ No newline at end of file From a99a1d8e3dc5427ee3126066fb62722d35aac945 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Wed, 26 Jul 2023 10:40:51 +0200 Subject: [PATCH 136/184] Implemented Dialogs --- app/lib/view/core/dialogs/MensaDialog.dart | 27 +++++++++++++++++++ .../core/dialogs/MensaFullscreenDialog.dart | 24 +++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/app/lib/view/core/dialogs/MensaDialog.dart b/app/lib/view/core/dialogs/MensaDialog.dart index e69de29b..a405eb61 100644 --- a/app/lib/view/core/dialogs/MensaDialog.dart +++ b/app/lib/view/core/dialogs/MensaDialog.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +class MensaDialog { + static void show( + {required BuildContext context, + required String title, + Widget? content, + Widget? actions, + bool? barrierDismissible}) { + showDialog( + context: context, + useSafeArea: true, + barrierDismissible: barrierDismissible ?? true, + builder: (BuildContext context) { + return Dialog( + backgroundColor: Theme.of(context).colorScheme.background, + child: Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ + Padding(padding: EdgeInsets.all(16), child: Text(title, + style: const TextStyle( + fontSize: 20, fontWeight: FontWeight.bold))), + if (content != null) content, + if (actions != null) actions + ])); + }, + ); + } +} diff --git a/app/lib/view/core/dialogs/MensaFullscreenDialog.dart b/app/lib/view/core/dialogs/MensaFullscreenDialog.dart index e69de29b..9e6db726 100644 --- a/app/lib/view/core/dialogs/MensaFullscreenDialog.dart +++ b/app/lib/view/core/dialogs/MensaFullscreenDialog.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class MensaFullscreenDialog { + static void show( + {required BuildContext context, + PreferredSizeWidget? appBar, + Widget? content, + Widget? actions}) { + showDialog( + context: context, + builder: (BuildContext context) { + return Dialog.fullscreen( + backgroundColor: Theme.of(context).colorScheme.background, + child: SafeArea( + child: Scaffold( + backgroundColor: Theme.of(context).colorScheme.background, + appBar: appBar, + body: content, + bottomNavigationBar: actions), + )); + }, + ); + } +} From e39b91ab16b168347e7fa4548bfb5c6ba95d0651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 10:51:56 +0200 Subject: [PATCH 137/184] make access to all shared preferences synchronous forgotten class --- .../information_display/MealSideEntry.dart | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/app/lib/view/core/information_display/MealSideEntry.dart b/app/lib/view/core/information_display/MealSideEntry.dart index 1b4aae5d..584af73e 100644 --- a/app/lib/view/core/information_display/MealSideEntry.dart +++ b/app/lib/view/core/information_display/MealSideEntry.dart @@ -1,7 +1,6 @@ import 'package:app/view/core/icons/MealIcon.dart'; import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; import 'package:app/view_model/repository/data_classes/meal/Side.dart'; -import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; @@ -37,26 +36,13 @@ class MealSideEntry extends StatelessWidget { fontWeight: FontWeight.normal, fontSize: 14, height: 1.5)), ), const SizedBox(width: 8), - FutureBuilder( - future: preferences.getPriceCategory(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Text( - _priceFormat.format( - _side.price.getPrice(snapshot.requireData) / 100), - style: const TextStyle( - fontSize: 14, - height: 1.5, - )); - } else { - return const Text('', - style: TextStyle( - fontSize: 14, - height: 1.5, - )); - } - }, - ) + Text( + _priceFormat.format( + _side.price.getPrice(preferences.getPriceCategory()) / 100), + style: const TextStyle( + fontSize: 14, + height: 1.5, + )) ], ), ); From 66dce0c782b5b7b8d42e18571e659de6b9107068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 11:39:33 +0200 Subject: [PATCH 138/184] add Favorites --- app/assets/locales/de/common.json | 3 +- app/lib/view/core/MainPage.dart | 3 +- app/lib/view/favorites/Favorites.dart | 69 +++++++++++++++++++++------ 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/app/assets/locales/de/common.json b/app/assets/locales/de/common.json index 98adfae8..c224d4d2 100644 --- a/app/assets/locales/de/common.json +++ b/app/assets/locales/de/common.json @@ -2,5 +2,6 @@ "demo": "Flutter Demo Startseite", "settings": "Einstellungen", "favorites": "Favoriten", - "mealPlan": "Speiseplan" + "mealPlan": "Speiseplan", + "noFavorites": "Es sind keine Favoriten vorhanden." } \ No newline at end of file diff --git a/app/lib/view/core/MainPage.dart b/app/lib/view/core/MainPage.dart index 2341a4f1..8b27210f 100644 --- a/app/lib/view/core/MainPage.dart +++ b/app/lib/view/core/MainPage.dart @@ -1,6 +1,7 @@ import 'package:app/view/core/icons/navigation/NavigationFavoritesIcon.dart'; import 'package:app/view/core/icons/navigation/NavigationMealPlanIcon.dart'; import 'package:app/view/core/icons/navigation/NavigationSettingsIcon.dart'; +import 'package:app/view/favorites/Favorites.dart'; import 'package:app/view/mealplan/MealPlanView.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; @@ -23,7 +24,7 @@ class _MainPageState extends State { index: _selectedIndex, children: [ MealPlanView(), - Container(color: Colors.green), + Favorites(), Container(color: Colors.blue), ], ), diff --git a/app/lib/view/favorites/Favorites.dart b/app/lib/view/favorites/Favorites.dart index 83e04601..55c38d36 100644 --- a/app/lib/view/favorites/Favorites.dart +++ b/app/lib/view/favorites/Favorites.dart @@ -1,7 +1,8 @@ +import 'package:app/view/core/MensaAppBar.dart'; import 'package:app/view/core/meal_view_format/MealListEntry.dart'; import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; -import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; class Favorites extends StatefulWidget { @@ -19,23 +20,61 @@ class _FavoritesState extends State{ child: FutureBuilder(future: Future.wait([favoriteAccess.getFavoriteMeals()]), builder: (context, snapshot) { if (!snapshot.hasData || snapshot.hasError) { - return const Column(); + return Scaffold( + appBar: MensaAppBar( + appBarHeight: kToolbarHeight * 1.25, + child: Text(FlutterI18n.translate(context, "common.favorites")), + ), + body: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ], + ) + ); } - final mealPlan = snapshot.requireData as List; + final mealPlan = snapshot.requireData[0]; + MensaAppBar appBar = MensaAppBar( + appBarHeight: kToolbarHeight * 1.25, + child: Text(FlutterI18n.translate(context, "common.favorites"), + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold),), + ); + + if (mealPlan.isEmpty) { + return Scaffold( + appBar: appBar, + body: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + FlutterI18n.translate( + context, "common.noFavorites"), + style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), + textAlign: TextAlign.center, + ) + ], + ), + ) + ); + } - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ListView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: mealPlan.length, - itemBuilder: (context, index) { - return MealListEntry(meal: mealPlan[index]); - }, - ), - ], + return Scaffold( + appBar: appBar, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: mealPlan.length, + itemBuilder: (context, index) { + return MealListEntry(meal: mealPlan[index]); + }, + ) + ], + ) ); }, ), From 1bf4b011572c6081a601b7bc665ed464fffd8710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 11:41:25 +0200 Subject: [PATCH 139/184] make Favorites stateless --- app/lib/view/favorites/Favorites.dart | 114 ++++++++++++-------------- 1 file changed, 54 insertions(+), 60 deletions(-) diff --git a/app/lib/view/favorites/Favorites.dart b/app/lib/view/favorites/Favorites.dart index 55c38d36..0faf230c 100644 --- a/app/lib/view/favorites/Favorites.dart +++ b/app/lib/view/favorites/Favorites.dart @@ -5,80 +5,74 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; -class Favorites extends StatefulWidget { +class Favorites extends StatelessWidget { const Favorites({super.key}); - @override - State createState() => _FavoritesState(); -} - -class _FavoritesState extends State{ @override Widget build(BuildContext context) { return Consumer(builder: (context, favoriteAccess, child) => Padding( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), child: FutureBuilder(future: Future.wait([favoriteAccess.getFavoriteMeals()]), - builder: (context, snapshot) { - if (!snapshot.hasData || snapshot.hasError) { - return Scaffold( - appBar: MensaAppBar( - appBarHeight: kToolbarHeight * 1.25, - child: Text(FlutterI18n.translate(context, "common.favorites")), - ), - body: const Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ], - ) + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.hasError) { + return Scaffold( + appBar: MensaAppBar( + appBarHeight: kToolbarHeight * 1.25, + child: Text(FlutterI18n.translate(context, "common.favorites")), + ), + body: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ], + ) + ); + } + + final mealPlan = snapshot.requireData[0]; + MensaAppBar appBar = MensaAppBar( + appBarHeight: kToolbarHeight * 1.25, + child: Text(FlutterI18n.translate(context, "common.favorites"), + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold),), ); - } - final mealPlan = snapshot.requireData[0]; - MensaAppBar appBar = MensaAppBar( - appBarHeight: kToolbarHeight * 1.25, - child: Text(FlutterI18n.translate(context, "common.favorites"), - style: const TextStyle( - fontSize: 24, fontWeight: FontWeight.bold),), - ); + if (mealPlan.isEmpty) { + return Scaffold( + appBar: appBar, + body: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + FlutterI18n.translate( + context, "common.noFavorites"), + style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), + textAlign: TextAlign.center, + ) + ], + ), + ) + ); + } - if (mealPlan.isEmpty) { return Scaffold( appBar: appBar, - body: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - FlutterI18n.translate( - context, "common.noFavorites"), - style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), - textAlign: TextAlign.center, - ) - ], - ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: mealPlan.length, + itemBuilder: (context, index) { + return MealListEntry(meal: mealPlan[index]); + }, + ) + ], ) ); - } - - return Scaffold( - appBar: appBar, - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ListView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: mealPlan.length, - itemBuilder: (context, index) { - return MealListEntry(meal: mealPlan[index]); - }, - ) - ], - ) - ); - }, + }, ), )); } - -} \ No newline at end of file +} From 46644ef5f87a9eaa21e30b4501c284837d29d4f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 11:43:48 +0200 Subject: [PATCH 140/184] formatting --- app/lib/view/favorites/Favorites.dart | 125 +++++++++++++------------- 1 file changed, 64 insertions(+), 61 deletions(-) diff --git a/app/lib/view/favorites/Favorites.dart b/app/lib/view/favorites/Favorites.dart index 0faf230c..4a260295 100644 --- a/app/lib/view/favorites/Favorites.dart +++ b/app/lib/view/favorites/Favorites.dart @@ -10,69 +10,72 @@ class Favorites extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer(builder: (context, favoriteAccess, child) => Padding( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), - child: FutureBuilder(future: Future.wait([favoriteAccess.getFavoriteMeals()]), - builder: (context, snapshot) { - if (!snapshot.hasData || snapshot.hasError) { - return Scaffold( - appBar: MensaAppBar( - appBarHeight: kToolbarHeight * 1.25, - child: Text(FlutterI18n.translate(context, "common.favorites")), - ), - body: const Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ], - ) - ); - } + return Consumer( + builder: (context, favoriteAccess, child) => Padding( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + child: FutureBuilder( + future: Future.wait([favoriteAccess.getFavoriteMeals()]), + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.hasError) { + return Scaffold( + appBar: MensaAppBar( + appBarHeight: kToolbarHeight * 1.25, + child: Text(FlutterI18n.translate( + context, "common.favorites")), + ), + body: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [], + )); + } - final mealPlan = snapshot.requireData[0]; - MensaAppBar appBar = MensaAppBar( - appBarHeight: kToolbarHeight * 1.25, - child: Text(FlutterI18n.translate(context, "common.favorites"), - style: const TextStyle( - fontSize: 24, fontWeight: FontWeight.bold),), - ); + final mealPlan = snapshot.requireData[0]; + MensaAppBar appBar = MensaAppBar( + appBarHeight: kToolbarHeight * 1.25, + child: Text( + FlutterI18n.translate(context, "common.favorites"), + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold), + ), + ); - if (mealPlan.isEmpty) { - return Scaffold( - appBar: appBar, - body: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - FlutterI18n.translate( - context, "common.noFavorites"), - style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), - textAlign: TextAlign.center, - ) - ], - ), - ) - ); - } + if (mealPlan.isEmpty) { + return Scaffold( + appBar: appBar, + body: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + FlutterI18n.translate( + context, "common.noFavorites"), + style: DefaultTextStyle.of(context) + .style + .apply(fontSizeFactor: 1.5), + textAlign: TextAlign.center, + ) + ], + ), + )); + } - return Scaffold( - appBar: appBar, - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ListView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: mealPlan.length, - itemBuilder: (context, index) { - return MealListEntry(meal: mealPlan[index]); - }, - ) - ], - ) - ); - }, - ), - )); + return Scaffold( + appBar: appBar, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: mealPlan.length, + itemBuilder: (context, index) { + return MealListEntry(meal: mealPlan[index]); + }, + ) + ], + )); + }, + ), + )); } } From 3fed248fffa33ff6176eeae6ac8a964d58c4ee46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 12:03:13 +0200 Subject: [PATCH 141/184] add file for localization --- app/lib/main.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/lib/main.dart b/app/lib/main.dart index cd3b2916..f77c200d 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -51,7 +51,8 @@ void main() { "image", "reportReason", "additive", - "allergen" + "allergen", + "mealplanException" ], useCountryCode: false, basePath: 'assets/locales', fallbackDir: 'de'), missingTranslationHandler: (key, locale) { if (kDebugMode) { From e3c5804c2fef1635427d6bd547adb4f90017c580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 12:04:23 +0200 Subject: [PATCH 142/184] add settings --- app/lib/view/core/MainPage.dart | 3 +- app/lib/view/settings/Settings.dart | 169 ++++++++++++++++++---------- 2 files changed, 112 insertions(+), 60 deletions(-) diff --git a/app/lib/view/core/MainPage.dart b/app/lib/view/core/MainPage.dart index 8b27210f..daf74dfd 100644 --- a/app/lib/view/core/MainPage.dart +++ b/app/lib/view/core/MainPage.dart @@ -3,6 +3,7 @@ import 'package:app/view/core/icons/navigation/NavigationMealPlanIcon.dart'; import 'package:app/view/core/icons/navigation/NavigationSettingsIcon.dart'; import 'package:app/view/favorites/Favorites.dart'; import 'package:app/view/mealplan/MealPlanView.dart'; +import 'package:app/view/settings/Settings.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; @@ -25,7 +26,7 @@ class _MainPageState extends State { children: [ MealPlanView(), Favorites(), - Container(color: Colors.blue), + Settings(), ], ), bottomNavigationBar: BottomNavigationBar( diff --git a/app/lib/view/settings/Settings.dart b/app/lib/view/settings/Settings.dart index 72d66e4e..35786b81 100644 --- a/app/lib/view/settings/Settings.dart +++ b/app/lib/view/settings/Settings.dart @@ -1,15 +1,22 @@ +import 'package:app/view/core/MensaAppBar.dart'; +import 'package:app/view/core/buttons/MensaLink.dart'; import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; import 'package:app/view/settings/SettingsDropdownEntry.dart'; +import 'package:app/view/settings/SettingsSection.dart'; import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; -class Settings extends StatefulWidget { - final String _version; +import 'package:url_launcher/url_launcher.dart'; - const Settings({super.key, required version}) : _version = version; +class Settings extends StatefulWidget { + Settings({super.key}) { + WidgetsFlutterBinding.ensureInitialized(); + } @override State createState() => _SettingsState(); @@ -19,79 +26,123 @@ class _SettingsState extends State { @override Widget build(BuildContext context) { return Consumer( - builder: (context, storage, child) => - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 12), - child: SingleChildScrollView( - child: Column(children: [ - FutureBuilder(future: Future.wait([ - storage.getColorScheme(), - storage.getPriceCategory() - ]), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return Column(children: [ - SettingsDropdownEntry(onChanged: (value) {}, - value: "", - items: const [], - // todo right string? - heading: "settings.colorScheme"), - SettingsDropdownEntry(onChanged: (value) {}, - value: "", - items: const [], - heading: "settings.priceCategory"), - ],); - } - - if (snapshot.hasError) { - // todo input from above - } - - return Column(children: [ - SettingsDropdownEntry(onChanged: (value) { - if (value != null && value != snapshot.requireData[0] as MensaColorScheme) { - storage.setColorScheme(value); - } - }, - value: snapshot.requireData[0] as MensaColorScheme, - items: _getColorSchemeEntries(), - // todo right string? - heading: "settings.colorScheme"), - SettingsDropdownEntry(onChanged: (value) { - if (value != null && value != snapshot.data?[0]) { - storage.setPriceCategory(value); + builder: (context, storage, child) => Padding( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + child: Scaffold( + appBar: MensaAppBar( + appBarHeight: kToolbarHeight * 1.25, + child: Text( + FlutterI18n.translate(context, "common.settings"), + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold), + ), + ), + body: SingleChildScrollView( + child: Column(children: [ + SettingsDropdownEntry( + onChanged: (value) { + if (value != null && + value != storage.getColorScheme()) { + storage.setColorScheme(value); + } + }, + value: storage.getColorScheme(), + items: _getColorSchemeEntries(context), + heading: "settings.colorScheme"), + SettingsDropdownEntry( + onChanged: (value) { + if (value != null && + value != storage.getPriceCategory()) { + storage.setPriceCategory(value); + } + }, + value: storage.getPriceCategory(), + items: _getPriceCategoryEntries(context), + heading: "settings.priceCategory"), + SettingsSection(heading: "settings.about", children: [ + Row( + children: [ + Text( + FlutterI18n.translate(context, "settings.version")), + const Spacer(), + FutureBuilder( + future: Future.wait([PackageInfo.fromPlatform()]), + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.hasError) { + return const Text("42.3.141"); } - }, - value: snapshot.requireData[1] as PriceCategory, - items: _getPriceCategoryEntries(), - heading: "settings.priceCategory"), - ],); - }), - + final PackageInfo info = snapshot.requireData[0]; + return Text(info.version); + }) + ], + ), + Text(FlutterI18n.translate(context, "settings.licence")), + Row( + children: [ + Expanded(child: MensaLink( + onPressed: () => _launchUrl(Uri.parse( + 'https://github.com/kronos-et-al/MensaApp')), + text: FlutterI18n.translate( + context, "settings.gitHubLink")),) + ], + ) ]), - ))); + SettingsSection( + heading: "settings.legalInformation", + children: [ + Row( + children: [ + Expanded( + child: MensaLink( + onPressed: () => _launchUrl(Uri.parse( + 'https://docs.flutter.io/flutter/services/UrlLauncher-class.html')), + text: FlutterI18n.translate( + context, "settings.privacyPolicy")), + ) + ], + ), + Row( + children: [ + Expanded(child: MensaLink( + onPressed: () => _launchUrl(Uri.parse( + 'https://docs.flutter.io/flutter/services/UrlLauncher-class.html')), + text: FlutterI18n.translate( + context, "settings.contactDetails")),) + ], + ) + ]) + ]), + ), + ))); } - List> _getColorSchemeEntries() { + // todo add padding + + Future _launchUrl(Uri url) async { + if (!await launchUrl(url)) { + throw Exception('Could not launch $url'); + } + } + + List> _getColorSchemeEntries(BuildContext context) { List> entries = []; for (final value in MensaColorScheme.values) { entries.add( - MensaDropdownEntry(value: value, label: "mensaColorScheme.$value")); + MensaDropdownEntry(value: value, label: FlutterI18n.translate(context, "mensaColorScheme.${value.name}"))); } return entries; } - List> _getPriceCategoryEntries() { + List> _getPriceCategoryEntries(BuildContext context) { List> entries = []; for (final value in PriceCategory.values) { - entries.add( - MensaDropdownEntry(value: value, label: "priceCategory.$value")); + entries + .add(MensaDropdownEntry(value: value, label: FlutterI18n.translate(context, "priceCategory.${value.name}"))); } return entries; } -} \ No newline at end of file +} From b9f9cce0c71078bfcce62424145757ddf25bd00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 12:15:05 +0200 Subject: [PATCH 143/184] Fixes in meal plan exceptions --- app/lib/view/mealplan/MealPlanError.dart | 55 +++++++++++++---------- app/lib/view/mealplan/MealPlanFilter.dart | 4 +- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/app/lib/view/mealplan/MealPlanError.dart b/app/lib/view/mealplan/MealPlanError.dart index d6449e51..dcd95aa4 100644 --- a/app/lib/view/mealplan/MealPlanError.dart +++ b/app/lib/view/mealplan/MealPlanError.dart @@ -6,36 +6,43 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; class MealPlanError extends StatelessWidget { - const MealPlanError({super.key}); @override Widget build(BuildContext context) { return Consumer( - builder: (context, mealAccess, child) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const ErrorExceptionIcon(size: 48), - Text(FlutterI18n.translate( - context, "mealplanException.noConnectionException"), - style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), + builder: (context, mealAccess, child) => Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const ErrorExceptionIcon(size: 48), + Text( + FlutterI18n.translate( + context, "mealplanException.noConnectionException"), + style: DefaultTextStyle.of(context) + .style + .apply(fontSizeFactor: 1.5), textAlign: TextAlign.center, - ), - MensaButton( - onPressed: () async { - // Mach das einfach als lokale Variable - final temporalMessage = - await mealAccess.refreshMealplan() ?? ""; - if (temporalMessage.isNotEmpty) { - final snackBar = SnackBar( - content: Text(FlutterI18n.translate( - context, temporalMessage)), - ); + ), + MensaButton( + onPressed: () async { + // Mach das einfach als lokale Variable + final temporalMessage = + await mealAccess.refreshMealplan() ?? ""; + if (temporalMessage.isNotEmpty) { + final snackBar = SnackBar( + content: Text(FlutterI18n.translate( + context, temporalMessage)), + backgroundColor: Theme.of(context).colorScheme.onError, + ); - ScaffoldMessenger.of(context).showSnackBar(snackBar); - } - }, - text: "mealplanException.noConnectionButton"), - ])); + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + } + }, + text: FlutterI18n.translate(context, "mealplanException.noConnectionButton")), + ]), + )); } } diff --git a/app/lib/view/mealplan/MealPlanFilter.dart b/app/lib/view/mealplan/MealPlanFilter.dart index 4708a30d..1cff41de 100644 --- a/app/lib/view/mealplan/MealPlanFilter.dart +++ b/app/lib/view/mealplan/MealPlanFilter.dart @@ -19,13 +19,13 @@ class MealPlanFilter extends StatelessWidget { children: [ const ErrorExceptionIcon(size: 48), Text(FlutterI18n.translate( - context, "mealplanException.noConnectionException"), + context, "mealplanException.filterException"), style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), textAlign: TextAlign.center, ), MensaButton( onPressed: () => mealAccess.deactivateFilter(), - text: "mealplanException.noConnectionButton"), + text: FlutterI18n.translate(context, "mealplanException.filterButton")), ])); } From 976721499455e81166eed29a74545976d491b1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 12:15:33 +0200 Subject: [PATCH 144/184] make settings stateless --- app/lib/view/settings/Settings.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/lib/view/settings/Settings.dart b/app/lib/view/settings/Settings.dart index 35786b81..46283c66 100644 --- a/app/lib/view/settings/Settings.dart +++ b/app/lib/view/settings/Settings.dart @@ -13,16 +13,11 @@ import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; -class Settings extends StatefulWidget { +class Settings extends StatelessWidget { Settings({super.key}) { WidgetsFlutterBinding.ensureInitialized(); } - @override - State createState() => _SettingsState(); -} - -class _SettingsState extends State { @override Widget build(BuildContext context) { return Consumer( @@ -146,3 +141,4 @@ class _SettingsState extends State { return entries; } } + From 544fe8ca808deac73ede57526ff570d73f9bccb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 12:45:54 +0200 Subject: [PATCH 145/184] fixes scrollable --- app/lib/view/favorites/Favorites.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/app/lib/view/favorites/Favorites.dart b/app/lib/view/favorites/Favorites.dart index 4a260295..d7b77b1c 100644 --- a/app/lib/view/favorites/Favorites.dart +++ b/app/lib/view/favorites/Favorites.dart @@ -65,7 +65,6 @@ class Favorites extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ ListView.builder( - physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: mealPlan.length, itemBuilder: (context, index) { From ca8e1b93d8346e2bcb3ba82ed9b41cbe78b13d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 12:59:04 +0200 Subject: [PATCH 146/184] remove unused imports --- app/lib/main.dart | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/app/lib/main.dart b/app/lib/main.dart index f77c200d..76004b32 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -3,18 +3,6 @@ import 'package:app/model/api_server/config.dart'; import 'package:app/model/database/SQLiteDatabaseAccess.dart'; import 'package:app/model/local_storage/SharedPreferenceAccess.dart'; import 'package:app/view/core/MainPage.dart'; -import 'package:app/view/core/MensaAppBar.dart'; -import 'package:app/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart'; -import 'package:app/view/core/icons/navigation/NavigationListOutlinedIcon.dart'; -import 'package:app/view/core/meal_view_format/MealGrid.dart'; -import 'package:app/view/core/selection_components/MensaDropdown.dart'; -import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; -import 'package:app/view/mealplan/MealPlanClosed.dart'; -import 'package:app/view/mealplan/MealPlanDateSelect.dart'; -import 'package:app/view/mealplan/MealPlanError.dart'; -import 'package:app/view/mealplan/MealPlanFilter.dart'; -import 'package:app/view/mealplan/MealPlanNoData.dart'; -import 'package:app/view/mealplan/MealPlanToolbar.dart'; import 'package:app/view/mealplan/MealPlanView.dart'; import 'package:app/view_model/logic/favorite/FavoriteMealAccess.dart'; import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; @@ -24,10 +12,7 @@ import 'package:app/view_model/logic/meal/CombinedMealPlanAccess.dart'; import 'package:app/view_model/logic/meal/IMealAccess.dart'; import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; import 'package:app/view_model/logic/preference/PreferenceAccess.dart'; -import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; -import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; -import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; import 'package:app/view_model/repository/interface/ILocalStorage.dart'; import 'package:app/view_model/repository/interface/IServerAccess.dart'; From 78aae12bbadc45defda72ec6ccf8e843f07bc8e7 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Wed, 26 Jul 2023 15:45:14 +0200 Subject: [PATCH 147/184] many changes again --- .../icons/favorites/favorite_filled.svg | 1 + .../icons/favorites/favorite_outlined.svg | 1 + app/assets/icons/meal/meal_line.svg | 1 + app/assets/icons/navigation/add_image.svg | 1 + app/assets/icons/navigation/nav_back.svg | 1 + app/assets/locales/de/allergen.json | 6 +- app/assets/locales/de/ratings.json | 1 + app/lib/main.dart | 15 -- .../requests/mutations.graphql.dart | 2 + .../view/core/buttons/MensaIconButton.dart | 4 +- .../icons/favorites/FavoriteFilledIcon.dart | 23 ++ .../icons/favorites/FavoriteOutlinedIcon.dart | 23 ++ .../view/core/icons/meal/MealLineIcon.dart | 23 ++ .../navigation/NavigationAddImageIcon.dart | 23 ++ .../icons/navigation/NavigationBackIcon.dart | 23 ++ .../core/meal_view_format/MealGridEntry.dart | 17 +- .../core/meal_view_format/MealGridLine.dart | 1 + .../core/meal_view_format/MealListEntry.dart | 18 +- .../core/meal_view_format/MealListLine.dart | 2 +- app/lib/view/detail_view/DetailsPage.dart | 205 ++++++++++++++++++ app/lib/view/detail_view/MealAccordion.dart | 18 +- .../view/detail_view/MealAccordionInfo.dart | 7 +- app/lib/view/detail_view/RatingsOverview.dart | 8 +- app/pubspec.yaml | 2 + 24 files changed, 379 insertions(+), 47 deletions(-) create mode 100644 app/assets/icons/favorites/favorite_filled.svg create mode 100644 app/assets/icons/favorites/favorite_outlined.svg create mode 100644 app/assets/icons/meal/meal_line.svg create mode 100644 app/assets/icons/navigation/add_image.svg create mode 100644 app/assets/icons/navigation/nav_back.svg create mode 100644 app/lib/view/core/icons/favorites/FavoriteFilledIcon.dart create mode 100644 app/lib/view/core/icons/favorites/FavoriteOutlinedIcon.dart create mode 100644 app/lib/view/core/icons/meal/MealLineIcon.dart create mode 100644 app/lib/view/core/icons/navigation/NavigationAddImageIcon.dart create mode 100644 app/lib/view/core/icons/navigation/NavigationBackIcon.dart diff --git a/app/assets/icons/favorites/favorite_filled.svg b/app/assets/icons/favorites/favorite_filled.svg new file mode 100644 index 00000000..56c4823a --- /dev/null +++ b/app/assets/icons/favorites/favorite_filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/favorites/favorite_outlined.svg b/app/assets/icons/favorites/favorite_outlined.svg new file mode 100644 index 00000000..b4e30b10 --- /dev/null +++ b/app/assets/icons/favorites/favorite_outlined.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/meal/meal_line.svg b/app/assets/icons/meal/meal_line.svg new file mode 100644 index 00000000..831793ff --- /dev/null +++ b/app/assets/icons/meal/meal_line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/navigation/add_image.svg b/app/assets/icons/navigation/add_image.svg new file mode 100644 index 00000000..6f94713e --- /dev/null +++ b/app/assets/icons/navigation/add_image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/navigation/nav_back.svg b/app/assets/icons/navigation/nav_back.svg new file mode 100644 index 00000000..dda697dc --- /dev/null +++ b/app/assets/icons/navigation/nav_back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/locales/de/allergen.json b/app/assets/locales/de/allergen.json index dc207ac9..559e737d 100644 --- a/app/assets/locales/de/allergen.json +++ b/app/assets/locales/de/allergen.json @@ -15,7 +15,7 @@ "ml": "Milch / Laktose", "pa": "Paranüsse", "pe": "Pekannüsse", - "pl": "Pistazie", + "pi": "Pistazie", "qu": "Queenslandnüsse / Macadamianüsse", "ro": "Roggen und Gluten aus Roggen", "sa": "Sesam", @@ -26,6 +26,6 @@ "wa": "Walnüsse", "we": "Weizen und Gluten aus Weizen", "wt": "Weichtiere", - "la": "mit tierischem Lab", - "gl": "mit Gelatine" + "la": "tierisches Lab", + "gl": "Gelatine" } \ No newline at end of file diff --git a/app/assets/locales/de/ratings.json b/app/assets/locales/de/ratings.json index b100bd08..19b71d3c 100644 --- a/app/assets/locales/de/ratings.json +++ b/app/assets/locales/de/ratings.json @@ -1,6 +1,7 @@ { "titleRatings": "Bewertungen", "usersRating": "Deine Bewertung", + "titlePersonalRating": "Deine Bewertung", "editRating": "Bewertung bearbeiten", "saveRating": "Bewertung speichern", "labelRatingsCount": "Bewertungen" diff --git a/app/lib/main.dart b/app/lib/main.dart index f77c200d..76004b32 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -3,18 +3,6 @@ import 'package:app/model/api_server/config.dart'; import 'package:app/model/database/SQLiteDatabaseAccess.dart'; import 'package:app/model/local_storage/SharedPreferenceAccess.dart'; import 'package:app/view/core/MainPage.dart'; -import 'package:app/view/core/MensaAppBar.dart'; -import 'package:app/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart'; -import 'package:app/view/core/icons/navigation/NavigationListOutlinedIcon.dart'; -import 'package:app/view/core/meal_view_format/MealGrid.dart'; -import 'package:app/view/core/selection_components/MensaDropdown.dart'; -import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; -import 'package:app/view/mealplan/MealPlanClosed.dart'; -import 'package:app/view/mealplan/MealPlanDateSelect.dart'; -import 'package:app/view/mealplan/MealPlanError.dart'; -import 'package:app/view/mealplan/MealPlanFilter.dart'; -import 'package:app/view/mealplan/MealPlanNoData.dart'; -import 'package:app/view/mealplan/MealPlanToolbar.dart'; import 'package:app/view/mealplan/MealPlanView.dart'; import 'package:app/view_model/logic/favorite/FavoriteMealAccess.dart'; import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; @@ -24,10 +12,7 @@ import 'package:app/view_model/logic/meal/CombinedMealPlanAccess.dart'; import 'package:app/view_model/logic/meal/IMealAccess.dart'; import 'package:app/view_model/logic/preference/IPreferenceAccess.dart'; import 'package:app/view_model/logic/preference/PreferenceAccess.dart'; -import 'package:app/view_model/repository/data_classes/mealplan/MealPlan.dart'; import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; -import 'package:app/view_model/repository/error_handling/MealPlanException.dart'; -import 'package:app/view_model/repository/error_handling/Result.dart'; import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; import 'package:app/view_model/repository/interface/ILocalStorage.dart'; import 'package:app/view_model/repository/interface/IServerAccess.dart'; diff --git a/app/lib/model/api_server/requests/mutations.graphql.dart b/app/lib/model/api_server/requests/mutations.graphql.dart index ac011252..f6056c93 100644 --- a/app/lib/model/api_server/requests/mutations.graphql.dart +++ b/app/lib/model/api_server/requests/mutations.graphql.dart @@ -1,8 +1,10 @@ import 'dart:async'; + import 'package:flutter/widgets.dart' as widgets; import 'package:gql/ast.dart'; import 'package:graphql/client.dart' as graphql; import 'package:graphql_flutter/graphql_flutter.dart' as graphql_flutter; + import 'schema.graphql.dart'; class Variables$Mutation$RemoveDownvote { diff --git a/app/lib/view/core/buttons/MensaIconButton.dart b/app/lib/view/core/buttons/MensaIconButton.dart index 69ec3502..181daefe 100644 --- a/app/lib/view/core/buttons/MensaIconButton.dart +++ b/app/lib/view/core/buttons/MensaIconButton.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; /// A icon button that is used in the Mensa app. class MensaIconButton extends StatelessWidget { final void Function() _onPressed; - final Icon _icon; + final Widget _icon; /// Creates a new MensaIconButton. /// @param key The key to identify this widget. @@ -11,7 +11,7 @@ class MensaIconButton extends StatelessWidget { /// @param icon The icon that is displayed on the button. /// @returns A new MensaIconButton. const MensaIconButton( - {super.key, required void Function() onPressed, required Icon icon}) + {super.key, required void Function() onPressed, required Widget icon}) : _onPressed = onPressed, _icon = icon; diff --git a/app/lib/view/core/icons/favorites/FavoriteFilledIcon.dart b/app/lib/view/core/icons/favorites/FavoriteFilledIcon.dart new file mode 100644 index 00000000..27dc93f0 --- /dev/null +++ b/app/lib/view/core/icons/favorites/FavoriteFilledIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class FavoriteFilledIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const FavoriteFilledIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/favorites/favorite_filled.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/favorites/FavoriteOutlinedIcon.dart b/app/lib/view/core/icons/favorites/FavoriteOutlinedIcon.dart new file mode 100644 index 00000000..081834ec --- /dev/null +++ b/app/lib/view/core/icons/favorites/FavoriteOutlinedIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class FavoriteOutlinedIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const FavoriteOutlinedIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/favorites/favorite_outlined.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/meal/MealLineIcon.dart b/app/lib/view/core/icons/meal/MealLineIcon.dart new file mode 100644 index 00000000..437bba88 --- /dev/null +++ b/app/lib/view/core/icons/meal/MealLineIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class MealLineIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const MealLineIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/meal/meal_line.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/navigation/NavigationAddImageIcon.dart b/app/lib/view/core/icons/navigation/NavigationAddImageIcon.dart new file mode 100644 index 00000000..ac01af59 --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationAddImageIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationAddImageIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationAddImageIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/add_image.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/navigation/NavigationBackIcon.dart b/app/lib/view/core/icons/navigation/NavigationBackIcon.dart new file mode 100644 index 00000000..52c19751 --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationBackIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationBackIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationBackIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/nav_back.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/meal_view_format/MealGridEntry.dart b/app/lib/view/core/meal_view_format/MealGridEntry.dart index 25e856e2..07236775 100644 --- a/app/lib/view/core/meal_view_format/MealGridEntry.dart +++ b/app/lib/view/core/meal_view_format/MealGridEntry.dart @@ -2,12 +2,15 @@ import 'package:app/view/core/information_display/MealMainEntry.dart'; import 'package:app/view/core/information_display/MealPreviewImage.dart'; import 'package:app/view/core/information_display/MealSideEntry.dart'; import 'package:app/view/core/input_components/MensaRatingInput.dart'; +import 'package:app/view/detail_view/DetailsPage.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:flutter/material.dart'; /// Displays a Meal as a Gallery Entry. class MealGridEntry extends StatelessWidget { final Meal _meal; + final Line? _line; final double _width; /// Creates a MealGridEntry. @@ -15,8 +18,10 @@ class MealGridEntry extends StatelessWidget { /// @param width The width of the entry. /// @param key The key to use for this widget. /// @return A MealGridEntry. - const MealGridEntry({super.key, required Meal meal, required double width}) + const MealGridEntry( + {super.key, required Meal meal, Line? line, required double width}) : _meal = meal, + _line = line, _width = width; @override @@ -27,14 +32,8 @@ class MealGridEntry extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), child: GestureDetector( onTap: () => { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - backgroundColor: Theme.of(context).colorScheme.surface, - content: Text( - 'MealGridEntry: ${_meal.name}', - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface), - ), - )) + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => DetailsPage(meal: _meal, line: _line,))) }, child: Container( decoration: BoxDecoration( diff --git a/app/lib/view/core/meal_view_format/MealGridLine.dart b/app/lib/view/core/meal_view_format/MealGridLine.dart index ec723c46..1f76e11e 100644 --- a/app/lib/view/core/meal_view_format/MealGridLine.dart +++ b/app/lib/view/core/meal_view_format/MealGridLine.dart @@ -33,6 +33,7 @@ class MealGridLine extends StatelessWidget { children: _mealPlan.meals .map((e) => MealGridEntry( meal: e, + line: _mealPlan.line, width: constraints.maxWidth * 0.9, )) .toList(), diff --git a/app/lib/view/core/meal_view_format/MealListEntry.dart b/app/lib/view/core/meal_view_format/MealListEntry.dart index 48d93a51..2d7ce145 100644 --- a/app/lib/view/core/meal_view_format/MealListEntry.dart +++ b/app/lib/view/core/meal_view_format/MealListEntry.dart @@ -1,7 +1,9 @@ import 'package:app/view/core/icons/MealIcon.dart'; import 'package:app/view/core/information_display/MealPreviewImage.dart'; import 'package:app/view/core/input_components/MensaRatingInput.dart'; +import 'package:app/view/detail_view/DetailsPage.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; @@ -9,6 +11,8 @@ import 'package:intl/intl.dart'; /// Displays a Meal as a List Entry. class MealListEntry extends StatelessWidget { final Meal _meal; + final Line? _line; + // TODO use locale final NumberFormat _priceFormat = NumberFormat.currency(locale: 'de_DE', symbol: '€'); @@ -17,7 +21,9 @@ class MealListEntry extends StatelessWidget { /// @param meal The Meal to display. /// @param key The key to use for this widget. /// @return A MealListEntry.flutter - MealListEntry({super.key, required Meal meal}) : _meal = meal; + MealListEntry({super.key, required Meal meal, Line? line}) + : _meal = meal, + _line = line; @override Widget build(BuildContext context) { @@ -25,12 +31,10 @@ class MealListEntry extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), child: GestureDetector( onTap: () => { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - backgroundColor: Theme.of(context).colorScheme.surface, - content: Text( - 'MealListEntry: ${_meal.name}', - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface), + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => DetailsPage( + meal: _meal, + line: _line, ), )) }, diff --git a/app/lib/view/core/meal_view_format/MealListLine.dart b/app/lib/view/core/meal_view_format/MealListLine.dart index fcc1d1ef..c2761b7e 100644 --- a/app/lib/view/core/meal_view_format/MealListLine.dart +++ b/app/lib/view/core/meal_view_format/MealListLine.dart @@ -29,7 +29,7 @@ class MealListLine extends StatelessWidget { shrinkWrap: true, itemCount: _mealPlan.meals.length, itemBuilder: (context, index) { - return MealListEntry(meal: _mealPlan.meals[index]); + return MealListEntry(meal: _mealPlan.meals[index], line: _mealPlan.line,); }, ), ], diff --git a/app/lib/view/detail_view/DetailsPage.dart b/app/lib/view/detail_view/DetailsPage.dart index e69de29b..73b9f1e4 100644 --- a/app/lib/view/detail_view/DetailsPage.dart +++ b/app/lib/view/detail_view/DetailsPage.dart @@ -0,0 +1,205 @@ +import 'package:app/view/core/MensaAppBar.dart'; +import 'package:app/view/core/buttons/MensaButton.dart'; +import 'package:app/view/core/buttons/MensaCtaButton.dart'; +import 'package:app/view/core/buttons/MensaIconButton.dart'; +import 'package:app/view/core/icons/favorites/FavoriteFilledIcon.dart'; +import 'package:app/view/core/icons/favorites/FavoriteOutlinedIcon.dart'; +import 'package:app/view/core/icons/meal/MealLineIcon.dart'; +import 'package:app/view/core/icons/navigation/NavigationAddImageIcon.dart'; +import 'package:app/view/core/icons/navigation/NavigationBackIcon.dart'; +import 'package:app/view/core/information_display/MealMainEntry.dart'; +import 'package:app/view/core/information_display/MealPreviewImage.dart'; +import 'package:app/view/core/information_display/MealSideEntry.dart'; +import 'package:app/view/core/input_components/MensaRatingInput.dart'; +import 'package:app/view/detail_view/MealAccordion.dart'; +import 'package:app/view/detail_view/MealAccordionInfo.dart'; +import 'package:app/view/detail_view/RatingsOverview.dart'; +import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; +import 'package:app/view_model/logic/meal/IMealAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:provider/provider.dart'; + +class DetailsPage extends StatefulWidget { + final Meal _meal; + final Line? _line; + + DetailsPage({super.key, required Meal meal, Line? line}) + : _meal = meal, + _line = line; + + @override + State createState() => DetailsPageState(); +} + +class DetailsPageState extends State { + int? expandedAccordionIndex; + + @override + Widget build(BuildContext context) { + ThemeData themeData = Theme.of(context); + return Container( + color: themeData.brightness == Brightness.light + ? themeData.colorScheme.background + : themeData.colorScheme.background, + child: SafeArea( + child: Scaffold( + appBar: MensaAppBar( + appBarHeight: kToolbarHeight * 1.25, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + MensaIconButton( + onPressed: () => Navigator.of(context).pop(), + icon: NavigationBackIcon()), + Spacer(), + Consumer(builder: (context, favoriteMealAccess, child) => MensaIconButton( + onPressed: () => { + favoriteMealAccess.addFavoriteMeal(widget._meal) + }, + icon: widget._meal.isFavorite ? FavoriteFilledIcon() : FavoriteOutlinedIcon())), + MensaIconButton( + onPressed: () => Navigator.of(context).pop(), + icon: NavigationAddImageIcon()), + ], + )), + ), + body: Column( + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 28), + child: Row(children: [ + Expanded( + child: Text( + widget._meal.name, + style: + TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + )) + ])), + SizedBox(height: 16), + Expanded( + child: Container( + color: themeData.brightness == Brightness.light + ? themeData.colorScheme.background + : themeData.colorScheme.surface, + child: SingleChildScrollView( + child: Padding( + padding: + EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + widget._line != null + ? Row( + children: [ + Padding( + padding: EdgeInsets.all(8), + child: MealLineIcon(), + ), + Text( + widget._line!.name, + style: TextStyle(fontSize: 16), + ) + ], + ) + : SizedBox(), + SizedBox(height: 8), + MealPreviewImage( + borderRadius: BorderRadius.circular(4), + meal: widget._meal, + height: 250), + SizedBox(height: 8), + MealAccordion( + backgroundColor: + themeData.brightness == Brightness.light + ? themeData.colorScheme.background + : themeData.colorScheme.surface, + expandedColor: + themeData.brightness == Brightness.light + ? themeData.colorScheme.surface + : themeData.colorScheme.background, + mainEntry: MealMainEntry(meal: widget._meal), + info: MealAccordionInfo( + additives: widget._meal.additives ?? [], + allergens: widget._meal.allergens ?? []), + isExpanded: expandedAccordionIndex == 0, + onTap: () => setState(() => + expandedAccordionIndex = + expandedAccordionIndex == 0 ? null : 0), + ), + ...?widget._meal.sides + ?.map((e) => MealAccordion( + backgroundColor: themeData.brightness == + Brightness.light + ? themeData.colorScheme.background + : themeData.colorScheme.surface, + expandedColor: themeData.brightness == + Brightness.light + ? themeData.colorScheme.surface + : themeData.colorScheme.background, + sideEntry: MealSideEntry(side: e), + info: MealAccordionInfo( + additives: e.additives ?? [], + allergens: e.allergens ?? []), + isExpanded: expandedAccordionIndex == + widget._meal.sides!.indexOf(e) + 1, + onTap: () => setState(() => + expandedAccordionIndex = + expandedAccordionIndex == + widget._meal.sides! + .indexOf(e) + + 1 + ? null + : widget._meal.sides! + .indexOf(e) + + 1), + )) + .toList(), + SizedBox(height: 16), + RatingsOverview( + meal: widget._meal, + backgroundColor: + themeData.brightness == Brightness.light + ? themeData.colorScheme.surface + : themeData.colorScheme.background, + ), + SizedBox(height: 16), + Text( + FlutterI18n.translate( + context, "ratings.titlePersonalRating"), + textAlign: TextAlign.left, + style: TextStyle( + color: + Theme.of(context).colorScheme.onSurface, + fontSize: 20, + fontWeight: FontWeight.bold, + )), + Row(children: [ + MensaRatingInput( + value: widget._meal.individualRating + ?.toDouble() ?? 0, + disabled: true, + color: + Theme.of(context).colorScheme.onSurface, + size: 20, + max: 5, + onChanged: (int) {}, + ), + Spacer(), + MensaButton(text: FlutterI18n.translate(context, "ratings.editRating"), onPressed: null,) + ]) + ], + ), + ), + ))) + ], + ), + ))); + } +} diff --git a/app/lib/view/detail_view/MealAccordion.dart b/app/lib/view/detail_view/MealAccordion.dart index c0c5672e..a66996c5 100644 --- a/app/lib/view/detail_view/MealAccordion.dart +++ b/app/lib/view/detail_view/MealAccordion.dart @@ -10,6 +10,8 @@ class MealAccordion extends StatelessWidget { final MealSideEntry? _sideEntry; final MealAccordionInfo _info; final Function()? _onTap; + final Color? _backgroundColor; + final Color? _expandedColor; /// Creates a new MealAccordion. /// @param key The key to identify this widget. @@ -25,12 +27,16 @@ class MealAccordion extends StatelessWidget { MealMainEntry? mainEntry, MealSideEntry? sideEntry, required MealAccordionInfo info, - Function()? onTap}) + Function()? onTap, + Color? backgroundColor, + Color? expandedColor}) : _isExpanded = isExpanded, _mainEntry = mainEntry, _sideEntry = sideEntry, _info = info, - _onTap = onTap; + _onTap = onTap, + _backgroundColor = backgroundColor, + _expandedColor = expandedColor; @override Widget build(BuildContext context) { @@ -38,18 +44,20 @@ class MealAccordion extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(4.0), color: _isExpanded - ? Theme.of(context).colorScheme.surface - : Theme.of(context).colorScheme.background, + ? _expandedColor ?? Theme.of(context).colorScheme.surface + : _backgroundColor ?? Theme.of(context).colorScheme.background, ), child: Material( color: Colors.transparent, borderRadius: BorderRadius.circular(4.0), child: InkWell( + splashColor: + Theme.of(context).colorScheme.background.withOpacity(0.1), borderRadius: BorderRadius.circular(4.0), onTap: _onTap, child: Padding( padding: - const EdgeInsets.symmetric(horizontal: 0, vertical: 8), + const EdgeInsets.symmetric(horizontal: 0, vertical: 4), child: Column( children: [ _mainEntry ?? _sideEntry ?? Container(), diff --git a/app/lib/view/detail_view/MealAccordionInfo.dart b/app/lib/view/detail_view/MealAccordionInfo.dart index 6856add4..01281b11 100644 --- a/app/lib/view/detail_view/MealAccordionInfo.dart +++ b/app/lib/view/detail_view/MealAccordionInfo.dart @@ -1,6 +1,7 @@ import 'package:app/view_model/repository/data_classes/meal/Additive.dart'; import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; /// This class is used to display the allergens and additives of a meal. class MealAccordionInfo extends StatelessWidget { @@ -27,21 +28,23 @@ class MealAccordionInfo extends StatelessWidget { SizedBox(height: 8), const Text( "Allergene:", + style: TextStyle(fontWeight: FontWeight.bold), ), ..._allergens.map((e) => Row( children: [ const Text("• "), - Expanded(child: Text(e.name)), + Expanded(child: I18nText("allergen.${e.name}")), ], )), SizedBox(height: 8), const Text( "Zusatzstoffe:", + style: TextStyle(fontWeight: FontWeight.bold), ), ..._additives.map((e) => Row( children: [ const Text("• "), - Expanded(child: Text(e.name)), + Expanded(child: I18nText("additive.${e.name}")), ], )), ], diff --git a/app/lib/view/detail_view/RatingsOverview.dart b/app/lib/view/detail_view/RatingsOverview.dart index 1e95a3ec..ef69dce2 100644 --- a/app/lib/view/detail_view/RatingsOverview.dart +++ b/app/lib/view/detail_view/RatingsOverview.dart @@ -8,12 +8,13 @@ import 'package:intl/intl.dart'; class RatingsOverview extends StatelessWidget { final Meal _meal; final NumberFormat _numberFormat = NumberFormat("#0.0#", "de_DE"); + final Color? _backgroundColor; /// Creates a new RatingsOverview. /// @param key The key to identify this widget. /// @param meal The meal to display the ratings for. /// @return A new RatingsOverview. - RatingsOverview({super.key, required Meal meal}) : _meal = meal; + RatingsOverview({super.key, required Meal meal, Color? backgroundColor}) : _meal = meal, _backgroundColor = backgroundColor; @override Widget build(BuildContext context) { @@ -22,13 +23,14 @@ class RatingsOverview extends StatelessWidget { textAlign: TextAlign.left, style: TextStyle( color: Theme.of(context).colorScheme.onSurface, - fontSize: 18, + fontSize: 20, + fontWeight: FontWeight.bold, )), const SizedBox(height: 8), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, + color: _backgroundColor ?? Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(4), ), child: Column( diff --git a/app/pubspec.yaml b/app/pubspec.yaml index eb4cb2dd..4a13b1e7 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -87,6 +87,8 @@ flutter: - assets/icons/allergens/ - assets/icons/exceptions/ - assets/icons/navigation/ + - assets/icons/favorites/ + - assets/icons/meal/ - assets/locales/en/ - assets/locales/de/ # To add assets to your application, add an assets section, like this: From 47e07f0225e6e56b4f2feac663a1d80eee86ebb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 17:03:24 +0200 Subject: [PATCH 148/184] add strings --- app/assets/locales/de/ratings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/locales/de/ratings.json b/app/assets/locales/de/ratings.json index b100bd08..2bf71505 100644 --- a/app/assets/locales/de/ratings.json +++ b/app/assets/locales/de/ratings.json @@ -1,5 +1,6 @@ { "titleRatings": "Bewertungen", + "dialogTitle": "bewerten", "usersRating": "Deine Bewertung", "editRating": "Bewertung bearbeiten", "saveRating": "Bewertung speichern", From 64131aa04ec4c57349895fb35c40eaaebbcc2e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 17:03:58 +0200 Subject: [PATCH 149/184] formating --- app/lib/view/core/dialogs/MensaDialog.dart | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/app/lib/view/core/dialogs/MensaDialog.dart b/app/lib/view/core/dialogs/MensaDialog.dart index a405eb61..6edfcd50 100644 --- a/app/lib/view/core/dialogs/MensaDialog.dart +++ b/app/lib/view/core/dialogs/MensaDialog.dart @@ -14,13 +14,18 @@ class MensaDialog { builder: (BuildContext context) { return Dialog( backgroundColor: Theme.of(context).colorScheme.background, - child: Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - Padding(padding: EdgeInsets.all(16), child: Text(title, - style: const TextStyle( - fontSize: 20, fontWeight: FontWeight.bold))), - if (content != null) content, - if (actions != null) actions - ])); + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.all(16), + child: Text(title, + style: const TextStyle( + fontSize: 20, fontWeight: FontWeight.bold))), + if (content != null) content, + if (actions != null) actions + ])); }, ); } From 26d22458d4d5188ac6c9c929fbcab80d62502bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 17:04:16 +0200 Subject: [PATCH 150/184] more strings --- app/assets/locales/de/image.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/locales/de/image.json b/app/assets/locales/de/image.json index ccf29fcd..e093a859 100644 --- a/app/assets/locales/de/image.json +++ b/app/assets/locales/de/image.json @@ -6,6 +6,12 @@ "linkImageTitle": "Bild hochladen", "linkDescription": "Bitte befolge die folgenden Schirtte, um ein Bild hinzufügen zu können.", + "linkFirstPoint": "1. Bild bei ", + "linkFirstLink": "Flickr", + "linkFirstPointSecondText": "hochladen", + "linkSecondPoint": "2. Bild unter Creative Commonce-Lizenz hochladen", + "linkThirdPoint": "3. Link zu Bild kopieren", + "linkFourthPoint": "Link unten einfügen", "linkTextFieldDescription": "Flickr Link zu Bild", "linkSmallTextOwnImages": "Es dürfen nur eigene Bilder hochgeladen werden.", "linkSmallTextDisplayed": "Die hochgeladenen Bilder werden anderen Nutzern angezeigt.", From 133907ad32d36770a04ec7e562856d33bb8a14ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 17:04:47 +0200 Subject: [PATCH 151/184] remove todo --- app/lib/view_model/logic/image/ImageAccess.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/lib/view_model/logic/image/ImageAccess.dart b/app/lib/view_model/logic/image/ImageAccess.dart index bc28ef50..658ba348 100644 --- a/app/lib/view_model/logic/image/ImageAccess.dart +++ b/app/lib/view_model/logic/image/ImageAccess.dart @@ -70,8 +70,6 @@ class ImageAccess extends ChangeNotifier implements IImageAccess { return "snackbar.reportImageError"; } - // todo wie wird es nicht mehr angezeigt - notifyListeners(); return "snackbar.reportImageSuccess"; } From 61484eab3b47dd2286b89e82fba8c2671d8bd83d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 17:05:09 +0200 Subject: [PATCH 152/184] image report dialog --- app/lib/view/images/ImageReportDialog.dart | 63 ++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/app/lib/view/images/ImageReportDialog.dart b/app/lib/view/images/ImageReportDialog.dart index e69de29b..381a9beb 100644 --- a/app/lib/view/images/ImageReportDialog.dart +++ b/app/lib/view/images/ImageReportDialog.dart @@ -0,0 +1,63 @@ +import 'package:app/view/core/buttons/MensaButton.dart'; +import 'package:app/view/core/dialogs/MensaDialog.dart'; +import 'package:app/view/core/selection_components/MensaDropdown.dart'; +import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; +import 'package:app/view_model/logic/image/IImageAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; +import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:provider/provider.dart'; + +class ImageReportDialog { + static ReportCategory _reason = ReportCategory.other; + + static void show(BuildContext context, ImageData image) async { + MensaDialog.show(context: context, + title: FlutterI18n.translate(context, "image.reportImageTitle"), + content: Consumer(builder: (context, imageAccess, child) => Column(children: [ + Text(FlutterI18n.translate(context, "image.reportDescription")), + Row(children: [ + Expanded(child: MensaDropdown(onChanged: (value) { + if (value != null) { + _reason = value; + } + }, + value: _reason, + items: _getReportCategoryEntries(context),)) + ],), + Row(children: [ + const Spacer(), + MensaButton(onPressed: () async { + final temporalMessage = await imageAccess.reportImage(image, _reason); + Navigator.pop(context); + + if (temporalMessage.isNotEmpty) { + final snackBar = SnackBar( + content: Text(FlutterI18n.translate( + context, temporalMessage)), + backgroundColor: Theme.of(context).colorScheme.onError, + ); + + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + } + }, + text: FlutterI18n.translate(context, "image.reportButton")), + ],) + ],)) + ); + } + + static List> _getReportCategoryEntries(BuildContext context) { + List> entries = []; + + for (final value in ReportCategory.values) { + entries + .add(MensaDropdownEntry(value: value, label: FlutterI18n.translate(context, "reportReason.${value.name}"))); + } + + return entries; + } +} From 604448725e327f95f9f7ce08ee84cdc00ae61cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 17:05:56 +0200 Subject: [PATCH 153/184] meal rating dialog --- app/lib/view/detail_view/MealRaingDialog.dart | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 app/lib/view/detail_view/MealRaingDialog.dart diff --git a/app/lib/view/detail_view/MealRaingDialog.dart b/app/lib/view/detail_view/MealRaingDialog.dart new file mode 100644 index 00000000..a92cdec3 --- /dev/null +++ b/app/lib/view/detail_view/MealRaingDialog.dart @@ -0,0 +1,45 @@ +import 'package:app/view/core/buttons/MensaButton.dart'; +import 'package:app/view/core/dialogs/MensaDialog.dart'; +import 'package:app/view/core/input_components/MensaRatingInput.dart'; +import 'package:app/view_model/logic/meal/IMealAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:provider/provider.dart'; + +class MealRatingDialog { + static void show(BuildContext context, String mealTitle, Meal meal) { + int rating = meal.individualRating ?? 0; + + MensaDialog.show(context: context, + title: "$mealTitle ${FlutterI18n.translate(context, "ratings.dialogTitle")}", + content: Consumer(builder: (context, mealAccess, child) => + Column(children: [ + MensaRatingInput(onChanged: (value) { + rating = value; + }, + value: rating as double), + Row(children: [ + const Spacer(), + MensaButton(onPressed: () async { + final temporalMessage = await mealAccess.updateMealRating(rating, meal); + Navigator.pop(context); + + if (temporalMessage.isNotEmpty) { + final snackBar = SnackBar( + content: Text(FlutterI18n.translate( + context, temporalMessage)), + backgroundColor: Theme.of(context).colorScheme.onError, + ); + + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + } + + }, + text: FlutterI18n.translate(context, "image.saveRating")) + ],) + ],)) + ); + } +} \ No newline at end of file From 94789539d885dc72ad771626f9d1cac8b88b07b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 17:06:13 +0200 Subject: [PATCH 154/184] upload image dialog --- .../view/detail_view/UploadImageDialog.dart | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/app/lib/view/detail_view/UploadImageDialog.dart b/app/lib/view/detail_view/UploadImageDialog.dart index e69de29b..f1de7f2c 100644 --- a/app/lib/view/detail_view/UploadImageDialog.dart +++ b/app/lib/view/detail_view/UploadImageDialog.dart @@ -0,0 +1,118 @@ +import 'package:app/view/core/buttons/MensaButton.dart'; +import 'package:app/view/core/dialogs/MensaDialog.dart'; +import 'package:app/view_model/logic/image/IImageAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:provider/provider.dart'; + +class UploadImageDialog { + static void show(BuildContext context, Meal meal) { + const TextStyle linkStyle = + TextStyle(color: Colors.blue, decoration: TextDecoration.underline); + + final textFieldController = TextEditingController(); + + MensaDialog.show( + context: context, + title: FlutterI18n.translate(context, "image.linkImageTitle"), + content: Consumer( + builder: (context, imageAccess, child) => Column( + children: [ + Text(FlutterI18n.translate( + context, "image.linkDescription")), + Container( + padding: const EdgeInsets.only(left: 4), + child: Column( + children: [ + RichText( + text: TextSpan(children: [ + TextSpan( + text: FlutterI18n.translate( + context, "image.linkFirstPoint")), + TextSpan( + text: 'clickable word', + style: linkStyle, + recognizer: TapGestureRecognizer() + ..onTap = () { + _launchURL(); + }, + ), + TextSpan( + text: FlutterI18n.translate(context, + "image.linkFirstPointSecondText")) + ]), + ), + Text(FlutterI18n.translate( + context, "image.linkSecondPoint")), + Text(FlutterI18n.translate( + context, "image.linkThirdPoint")), + Text(FlutterI18n.translate( + context, "image.linkFourthPoint")) + ], + )), + // todo padding + Text(FlutterI18n.translate( + context, "image.linkTextFieldDescription")), + TextField( + controller: textFieldController, + decoration: InputDecoration( + border: const OutlineInputBorder(), + hintText: FlutterI18n.translate( + context, "image.linkTextFieldDescription"), + ), + ), + // todo padding + Text( + FlutterI18n.translate( + context, "image.linkSmallTextOwnImages"), + style: DefaultTextStyle.of(context) + .style + .apply(fontSizeFactor: 0.5)), + Text( + FlutterI18n.translate( + context, "image.linkSmallTextDisplayed"), + style: DefaultTextStyle.of(context) + .style + .apply(fontSizeFactor: 0.5), + ), + Row( + children: [ + const Spacer(), + MensaButton( + onPressed: () async { + final temporalMessage = await imageAccess + .linkImage(textFieldController.text, meal); + Navigator.pop(context); + + if (temporalMessage.isNotEmpty) { + final snackBar = SnackBar( + content: Text(FlutterI18n.translate( + context, temporalMessage)), + backgroundColor: + Theme.of(context).colorScheme.onError, + ); + + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + } + }, + text: FlutterI18n.translate( + context, "image.linkButton")) + ], + ) + ], + ))); + } + + static void _launchURL() async { + Uri url = Uri.parse('https://flickr.com/'); + if (await launchUrl(url)) { + await launchUrl(url); + } else { + throw 'Could not launch $url'; + } + } +} From c9366e0134b1ac8d089b3d8f2ead923aa6176a71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Wed, 26 Jul 2023 23:03:19 +0200 Subject: [PATCH 155/184] center meal plan exception views --- app/lib/view/mealplan/MealPlanClosed.dart | 1 + app/lib/view/mealplan/MealPlanFilter.dart | 3 ++- app/lib/view/mealplan/MealPlanNoData.dart | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/lib/view/mealplan/MealPlanClosed.dart b/app/lib/view/mealplan/MealPlanClosed.dart index 85f9529f..bc941620 100644 --- a/app/lib/view/mealplan/MealPlanClosed.dart +++ b/app/lib/view/mealplan/MealPlanClosed.dart @@ -11,6 +11,7 @@ class MealPlanClosed extends StatelessWidget { @override Widget build(BuildContext context) { return Column(crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, children: [ const CanteenClosedExceptionIcon(size: 48), Text(FlutterI18n.translate( diff --git a/app/lib/view/mealplan/MealPlanFilter.dart b/app/lib/view/mealplan/MealPlanFilter.dart index 1cff41de..f3fc9068 100644 --- a/app/lib/view/mealplan/MealPlanFilter.dart +++ b/app/lib/view/mealplan/MealPlanFilter.dart @@ -15,7 +15,8 @@ class MealPlanFilter extends StatelessWidget { Widget build(BuildContext context) { return Consumer( builder: (context, mealAccess, child) => Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, children: [ const ErrorExceptionIcon(size: 48), Text(FlutterI18n.translate( diff --git a/app/lib/view/mealplan/MealPlanNoData.dart b/app/lib/view/mealplan/MealPlanNoData.dart index b23568b1..7d0b8a2f 100644 --- a/app/lib/view/mealplan/MealPlanNoData.dart +++ b/app/lib/view/mealplan/MealPlanNoData.dart @@ -11,6 +11,7 @@ class MealPlanNoData extends StatelessWidget { @override Widget build(BuildContext context) { return Column(crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, children: [ const NoDataExceptionIcon(size: 48), Text( From f6dc9fb8eeb9c3f1299f525b7aa9795bc86dc33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Thu, 27 Jul 2023 00:39:23 +0200 Subject: [PATCH 156/184] change access to filter preferences --- .../filter/FilterPreferences.dart | 14 ++---- app/test/view-model/MealPlanAccessTest.dart | 45 ++++++++++++++----- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart index e2ad6524..df0a76ce 100644 --- a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart +++ b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart @@ -93,16 +93,10 @@ class FilterPreferences { /// @return The allergens that should be inside meals shown on the meal plan List get allergens => _allergens; - /// add a allergen to the list of allergens that should be inside meals shown on the meal plan - /// @param allergen The allergen that should be inside meals shown on the meal plan - addAllergen(Allergen allergen) { - _allergens.add(allergen); - } - - /// remove an allergen to the list of allergens that should be inside meals shown on the meal plan - /// @param allergen The allergen that should not be inside meals shown on the meal plan - removeAllergen(Allergen allergen) { - _allergens.remove(allergen); + /// change the list of allergens that should be inside meals shown on the meal plan + /// @param allergen The list of allergen that should be inside meals shown on the meal plan + set allergens(List value) { + _allergens = value; } /// returns if the sorting of the meals is ascending or descending diff --git a/app/test/view-model/MealPlanAccessTest.dart b/app/test/view-model/MealPlanAccessTest.dart index f3758460..4cd487ce 100644 --- a/app/test/view-model/MealPlanAccessTest.dart +++ b/app/test/view-model/MealPlanAccessTest.dart @@ -166,8 +166,7 @@ void main() { }); setUp(() { - when(() => localStorage.getFilterPreferences()) - .thenAnswer((_) => null); + when(() => localStorage.getFilterPreferences()).thenAnswer((_) => null); when(() => localStorage.getCanteen()).thenAnswer((_) => canteenID); when(() => localStorage.getPriceCategory()) .thenAnswer((_) => PriceCategory.student); @@ -215,7 +214,10 @@ void main() { group("allergens", () { test("change allergens er", () async { - filter.removeAllergen(Allergen.er); + List allergens = _getAllAllergen(); + allergens.remove(Allergen.er); + + filter.allergens = allergens; await mealPlanAccess.changeFilterPreferences(filter); final List returnedMealPlan = switch ( @@ -249,8 +251,11 @@ void main() { }); test("change allergens er and sn", () async { - filter.removeAllergen(Allergen.er); - filter.removeAllergen(Allergen.sn); + List allergens = _getAllAllergen(); + allergens.remove(Allergen.er); + allergens.remove(Allergen.sn); + + filter.allergens = allergens; await mealPlanAccess.changeFilterPreferences(filter); final returnedMealPlan = switch (await mealPlanAccess.getMealPlan()) { @@ -270,7 +275,12 @@ void main() { }); test("change allergens er, sn and kr", () async { - filter.removeAllergen(Allergen.kr); + List allergens = _getAllAllergen(); + allergens.remove(Allergen.er); + allergens.remove(Allergen.sn); + allergens.remove(Allergen.kr); + + filter.allergens = allergens; await mealPlanAccess.changeFilterPreferences(filter); final returnedMealPlan = switch (await mealPlanAccess.getMealPlan()) { @@ -290,9 +300,9 @@ void main() { }); test("remove filter allergens", () async { - filter.addAllergen(Allergen.er); - filter.addAllergen(Allergen.sn); - filter.addAllergen(Allergen.kr); + List allergens = _getAllAllergen(); + + filter.allergens = allergens; await mealPlanAccess.changeFilterPreferences(filter); @@ -388,7 +398,7 @@ void main() { await mealPlanAccess.deactivateFilter(); final List returnedMealPlan = switch ( - await mealPlanAccess.getMealPlan()) { + await mealPlanAccess.getMealPlan()) { Success(value: final value) => value, Failure(exception: _) => [] }; @@ -403,7 +413,7 @@ void main() { await mealPlanAccess.activateFilter(); final List returnedMealPlan = switch ( - await mealPlanAccess.getMealPlan()) { + await mealPlanAccess.getMealPlan()) { Success(value: final value) => value, Failure(exception: _) => [] }; @@ -684,7 +694,10 @@ void main() { await mealPlanAccess.changeDate(DateTime.now()); filter.setCategoriesVegan(); - filter.removeAllergen(Allergen.lu); + List allergens = _getAllAllergen(); + allergens.remove(Allergen.lu); + + filter.allergens = allergens; when(() => localStorage.setFilterPreferences(filter)) .thenAnswer((_) async {}); @@ -700,3 +713,11 @@ void main() { }); }); } + +List _getAllAllergen() { + List list = []; + for (Allergen allergen in Allergen.values) { + list.add(allergen); + } + return list; +} From feaccde90ac3812f563e2dc6d3cdc6714e17fd93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Thu, 27 Jul 2023 01:01:29 +0200 Subject: [PATCH 157/184] initialization with a lot todos but is late so ... --- app/lib/view/filter/FilterDialog.dart | 215 ++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) diff --git a/app/lib/view/filter/FilterDialog.dart b/app/lib/view/filter/FilterDialog.dart index e69de29b..15268f92 100644 --- a/app/lib/view/filter/FilterDialog.dart +++ b/app/lib/view/filter/FilterDialog.dart @@ -0,0 +1,215 @@ +import 'package:app/view/core/MensaAppBar.dart'; +import 'package:app/view/core/buttons/MensaButton.dart'; +import 'package:app/view/core/dialogs/MensaFullscreenDialog.dart'; +import 'package:app/view/core/icons/allergens/AllergenIcon.dart'; +import 'package:app/view/core/selection_components/MensaSlider.dart'; +import 'package:app/view/core/selection_components/MensaToggle.dart'; +import 'package:app/view/filter/MensaButtonGroup.dart'; +import 'package:app/view/filter/MensaButtonGroupEntry.dart'; +import 'package:app/view/filter/MensaFilterIconCheckbox.dart'; +import 'package:app/view/filter/MensaFilterIconCheckboxGroup.dart'; +import 'package:app/view_model/logic/meal/IMealAccess.dart'; +import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; +import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; +import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; +import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:provider/provider.dart'; + +// todo padding +class FilterDialog { + static void show(BuildContext context) { + FilterPreferences preferences = FilterPreferences(); + + MensaFullscreenDialog.show( + context: context, + appBar: MensaAppBar( + appBarHeight: kToolbarHeight * 1.25, + child: Row( + children: [ + // todo icon back + Text( + FlutterI18n.translate(context, "filter.filterTitle"), + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold), + ), + const Spacer(), + // todo temporary disable or enable filter + ], + )), + content: Consumer( + builder: (context, mealAccess, child) => FutureBuilder( + future: mealAccess.getFilterPreferences(), + builder: (context, snapshot) { + if (snapshot.hasData) { + preferences = snapshot.requireData; + } + + // todo Scrollable + return Column( + children: [ + Text(FlutterI18n.translate(context, "filter.foodType")), + MensaButtonGroup( + value: _getValueCategory(preferences.categories), + onChanged: (value) => + _setValueCategory(value, preferences), + entries: _getAllFoodTypeEntries(context)), + // todo checkboxes + Text(FlutterI18n.translate( + context, "filter.allergensTitle")), + MensaFilterIconCheckboxGroup( + items: _getAllAllergen(context), + selectedValues: preferences.allergens, + onChanged: (value) { + preferences.allergens = value; + }), + Text(FlutterI18n.translate(context, "filter.priceTitle")), + MensaSlider( + onChanged: (value) => preferences.price = value, + value: preferences.price, + min: "0€", + max: "${FilterPreferences().price}€"), + Text( + FlutterI18n.translate(context, "filter.ratingTitle")), + MensaSlider( + onChanged: (value) => preferences.rating = value, + value: preferences.rating, + min: "5 Sterne", + max: "1 Stern"), + MensaToggle( + onChanged: (value) => + preferences.onlyFavorite = value, + value: preferences.onlyFavorite, + label: FlutterI18n.translate( + context, "filter.favoritesOnlyTitle")), + Text(FlutterI18n.translate( + context, "filter.frequencyTitle")), + MensaButtonGroup( + value: _getValueFrequency(preferences.frequency), + onChanged: (value) => + _setValueFrequency(value, preferences), + entries: _getAllFrequencyEntries(context)), + Text( + FlutterI18n.translate(context, "filter.sortByTitle")), + Row( + children: [ + // todo SortDropdown in Expanded + // todo Icon for ascending / descending + ], + ), + MensaButton( + onPressed: () { + Navigator.pop(context); + mealAccess.changeFilterPreferences(preferences); + }, + text: FlutterI18n.translate( + context, "filter.storeButton")) + ], + ); + }))); + } + + static _setValueFrequency(Frequency frequency, FilterPreferences filter) { + if (frequency == Frequency.newMeal) { + filter.setNewFrequency(); + return; + } + + if (frequency == Frequency.rare) { + filter.setRareFrequency(); + return; + } + + filter.setAllFrequencies(); + } + + static Frequency _getValueFrequency(List frequencies) { + if (frequencies.contains(Frequency.normal)) { + return Frequency.normal; + } + + if (frequencies.contains(Frequency.rare)) { + return Frequency.rare; + } + + return Frequency.newMeal; + } + + static List> _getAllFrequencyEntries( + BuildContext context) { + final List> entries = []; + + entries.add(MensaButtonGroupEntry( + title: FlutterI18n.translate(context, "filter.frequencySectionAll"), + value: Frequency.normal)); + entries.add(MensaButtonGroupEntry( + title: FlutterI18n.translate(context, "filter.frequencySectionRare"), + value: Frequency.rare)); + entries.add(MensaButtonGroupEntry( + title: FlutterI18n.translate(context, "filter.frequencySectionNew"), + value: Frequency.newMeal)); + + return entries; + } + + static List> _getAllAllergen( + BuildContext context) { + List> entries = []; + + for (final allergen in Allergen.values) { + entries.add(MensaFilterIconCheckbox( + icon: AllergenIcon( + allergen: allergen, + ), + text: FlutterI18n.translate(context, "allergen.${allergen.name}"), + value: allergen, + )); + } + + return entries; + } + + static List> _getAllFoodTypeEntries( + BuildContext context) { + final List> entries = []; + + entries.add(MensaButtonGroupEntry( + title: FlutterI18n.translate(context, "filter.foodTypeSectionAll"), + value: FoodType.beef)); + entries.add(MensaButtonGroupEntry( + title: + FlutterI18n.translate(context, "filter.foodTypeSectionVegetarian"), + value: FoodType.vegetarian)); + entries.add(MensaButtonGroupEntry( + title: FlutterI18n.translate(context, "filter.foodTypeSectionVegan"), + value: FoodType.vegan)); + + return entries; + } + + static _setValueCategory(FoodType type, FilterPreferences access) { + if (type == FoodType.vegan) { + access.setCategoriesVegan(); + return; + } + + if (type == FoodType.vegetarian) { + access.setCategoriesVegetarian(); + return; + } + + access.setAllCategories(); + } + + static FoodType _getValueCategory(List types) { + switch (types.length) { + case 1: + return FoodType.vegan; + case 2: + return FoodType.vegetarian; + default: + return FoodType.beef; + } + } +} From aef98ec3e915e8cc3f36bec887d48213acb6b049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Thu, 27 Jul 2023 13:29:34 +0200 Subject: [PATCH 158/184] delete one unnecessary parameter --- app/lib/view/detail_view/MealRaingDialog.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/view/detail_view/MealRaingDialog.dart b/app/lib/view/detail_view/MealRaingDialog.dart index a92cdec3..df69f0e6 100644 --- a/app/lib/view/detail_view/MealRaingDialog.dart +++ b/app/lib/view/detail_view/MealRaingDialog.dart @@ -8,11 +8,11 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; class MealRatingDialog { - static void show(BuildContext context, String mealTitle, Meal meal) { + static void show(BuildContext context, Meal meal) { int rating = meal.individualRating ?? 0; MensaDialog.show(context: context, - title: "$mealTitle ${FlutterI18n.translate(context, "ratings.dialogTitle")}", + title: "${meal.name} ${FlutterI18n.translate(context, "ratings.dialogTitle")}", content: Consumer(builder: (context, mealAccess, child) => Column(children: [ MensaRatingInput(onChanged: (value) { From 99582b0f4ce299270c2dca71d53e37f5a8ac9119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Thu, 27 Jul 2023 13:35:14 +0200 Subject: [PATCH 159/184] spelling in file name --- .../detail_view/{MealRaingDialog.dart => MealRatingDialog.dart} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/lib/view/detail_view/{MealRaingDialog.dart => MealRatingDialog.dart} (100%) diff --git a/app/lib/view/detail_view/MealRaingDialog.dart b/app/lib/view/detail_view/MealRatingDialog.dart similarity index 100% rename from app/lib/view/detail_view/MealRaingDialog.dart rename to app/lib/view/detail_view/MealRatingDialog.dart From 4d8f219df9240a054bb15d83722ece6abd1fa907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Thu, 27 Jul 2023 17:00:20 +0200 Subject: [PATCH 160/184] formatting --- app/lib/view/core/buttons/MensaTapable.dart | 17 +++++---- .../core/icons/allergens/IAllergenIcon.dart | 11 +++++- .../core/information_display/MealRating.dart | 1 + .../selection_components/MensaToggle.dart | 37 +++++++++++-------- app/lib/view/detail_view/RatingsOverview.dart | 4 +- app/lib/view/mealplan/MealPlanView.dart | 2 + app/lib/view/mealplan/MensaCanteenSelect.dart | 2 +- 7 files changed, 46 insertions(+), 28 deletions(-) diff --git a/app/lib/view/core/buttons/MensaTapable.dart b/app/lib/view/core/buttons/MensaTapable.dart index 2b0e657b..bb717c5a 100644 --- a/app/lib/view/core/buttons/MensaTapable.dart +++ b/app/lib/view/core/buttons/MensaTapable.dart @@ -1,12 +1,18 @@ import 'package:flutter/material.dart'; class MensaTapable extends StatelessWidget { - final Widget _child; final Color? _color; final Function() _onTap; - MensaTapable({super.key, required Widget child, Color? color, required Function() onTap}) : _child = child, _color = color, _onTap = onTap; + MensaTapable( + {super.key, + required Widget child, + Color? color, + required Function() onTap}) + : _child = child, + _color = color, + _onTap = onTap; @override Widget build(BuildContext context) { @@ -20,9 +26,4 @@ class MensaTapable extends StatelessWidget { ), ); } - - - - - -} \ No newline at end of file +} diff --git a/app/lib/view/core/icons/allergens/IAllergenIcon.dart b/app/lib/view/core/icons/allergens/IAllergenIcon.dart index 6ff6ebe3..08c421d9 100644 --- a/app/lib/view/core/icons/allergens/IAllergenIcon.dart +++ b/app/lib/view/core/icons/allergens/IAllergenIcon.dart @@ -11,7 +11,14 @@ abstract class IAllergenIcon extends StatelessWidget { /// @param width The width of the icon. /// @param height The height of the icon. /// @param color The color of the icon. - const IAllergenIcon({super.key, double width = 24, double height = 24, Color color = Colors.black}): _width = width, _height = height, _color = color; + const IAllergenIcon( + {super.key, + double width = 24, + double height = 24, + Color color = Colors.black}) + : _width = width, + _height = height, + _color = color; /// Returns the color of the icon. Color get color => _color; @@ -21,4 +28,4 @@ abstract class IAllergenIcon extends StatelessWidget { /// Returns the width of the icon. double get width => _width; -} \ No newline at end of file +} diff --git a/app/lib/view/core/information_display/MealRating.dart b/app/lib/view/core/information_display/MealRating.dart index e69de29b..c0fdaeff 100644 --- a/app/lib/view/core/information_display/MealRating.dart +++ b/app/lib/view/core/information_display/MealRating.dart @@ -0,0 +1 @@ +// todo MealRating \ No newline at end of file diff --git a/app/lib/view/core/selection_components/MensaToggle.dart b/app/lib/view/core/selection_components/MensaToggle.dart index 10b2efa8..d75b3de9 100644 --- a/app/lib/view/core/selection_components/MensaToggle.dart +++ b/app/lib/view/core/selection_components/MensaToggle.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; /// A toggle that is used in the Mensa app. class MensaToggle extends StatelessWidget { - final void Function(bool)? _onChanged; final bool _value; final String _label; @@ -13,25 +12,31 @@ class MensaToggle extends StatelessWidget { /// @param value The current value. /// @param label The label of the toggle. /// @returns A new MensaToggle. - const MensaToggle({super.key, required onChanged, required value, required label}): _onChanged = onChanged, _value = value, _label = label; + const MensaToggle( + {super.key, required onChanged, required value, required label}) + : _onChanged = onChanged, + _value = value, + _label = label; /// Builds the widget. /// @param context The context in which the widget is built. /// @returns The widget. @override Widget build(BuildContext context) { - return Padding(padding: const EdgeInsets.all(8), child: Row( - children: [ - Switch(value: _value, onChanged: _onChanged), - const SizedBox(width: 8), - GestureDetector( - onTap: () { - _onChanged!(!_value); - }, - child: Text(_label, style: Theme.of(context).textTheme.labelLarge), - ), - ], - )); + return Padding( + padding: const EdgeInsets.all(8), + child: Row( + children: [ + Switch(value: _value, onChanged: _onChanged), + const SizedBox(width: 8), + GestureDetector( + onTap: () { + _onChanged!(!_value); + }, + child: + Text(_label, style: Theme.of(context).textTheme.labelLarge), + ), + ], + )); } - -} \ No newline at end of file +} diff --git a/app/lib/view/detail_view/RatingsOverview.dart b/app/lib/view/detail_view/RatingsOverview.dart index ef69dce2..93a9279c 100644 --- a/app/lib/view/detail_view/RatingsOverview.dart +++ b/app/lib/view/detail_view/RatingsOverview.dart @@ -14,7 +14,9 @@ class RatingsOverview extends StatelessWidget { /// @param key The key to identify this widget. /// @param meal The meal to display the ratings for. /// @return A new RatingsOverview. - RatingsOverview({super.key, required Meal meal, Color? backgroundColor}) : _meal = meal, _backgroundColor = backgroundColor; + RatingsOverview({super.key, required Meal meal, Color? backgroundColor}) + : _meal = meal, + _backgroundColor = backgroundColor; @override Widget build(BuildContext context) { diff --git a/app/lib/view/mealplan/MealPlanView.dart b/app/lib/view/mealplan/MealPlanView.dart index 1707926d..c1d9d76d 100644 --- a/app/lib/view/mealplan/MealPlanView.dart +++ b/app/lib/view/mealplan/MealPlanView.dart @@ -24,6 +24,8 @@ import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:provider/provider.dart'; class MealPlanView extends StatelessWidget { + const MealPlanView({super.key}); + @override Widget build(BuildContext context) { return Consumer( diff --git a/app/lib/view/mealplan/MensaCanteenSelect.dart b/app/lib/view/mealplan/MensaCanteenSelect.dart index 634a1f6e..7f584ad0 100644 --- a/app/lib/view/mealplan/MensaCanteenSelect.dart +++ b/app/lib/view/mealplan/MensaCanteenSelect.dart @@ -1,4 +1,4 @@ -import 'package:app/view/core/icons/navigation/NavigationArrowDownIcon.dart'; + import 'package:app/view/core/icons/navigation/NavigationArrowDownIcon.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; import 'package:flutter/material.dart'; From d54867473e2910957ada563d1f0ae17c2f7e025a Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Thu, 27 Jul 2023 19:19:22 +0200 Subject: [PATCH 161/184] many changes again --- app/lib/model/api_server/config.dart | 4 +- .../local_storage/SharedPreferenceAccess.dart | 9 +- app/lib/view/core/buttons/MensaButton.dart | 4 +- app/lib/view/core/dialogs/MensaDialog.dart | 55 +++-- .../core/dialogs/MensaFullscreenDialog.dart | 36 ++-- .../core/input_components/MensaTextField.dart | 25 +++ .../selection_components/MensaSlider.dart | 10 +- app/lib/view/detail_view/DetailsPage.dart | 40 +++- .../view/detail_view/MealRatingDialog.dart | 89 +++++--- .../view/detail_view/UploadImageDialog.dart | 181 ++++++++-------- app/lib/view/filter/FilterDialog.dart | 199 ++++++++++-------- app/lib/view/mealplan/MealPlanView.dart | 12 +- .../view/settings/SettingsDropdownEntry.dart | 30 +-- .../logic/meal/CombinedMealPlanAccess.dart | 8 +- .../filter/FilterPreferences.dart | 2 +- 15 files changed, 403 insertions(+), 301 deletions(-) create mode 100644 app/lib/view/core/input_components/MensaTextField.dart diff --git a/app/lib/model/api_server/config.dart b/app/lib/model/api_server/config.dart index e5858167..94fce912 100644 --- a/app/lib/model/api_server/config.dart +++ b/app/lib/model/api_server/config.dart @@ -1,3 +1,3 @@ -const String testServer = "https://demo.data.mensa-ka.de/"; -const String testApiKey = "YWpzZGg4MnozNzhkMnppZGFzYXNkMiBzYWZzYSBzPGE5MDk4"; \ No newline at end of file +const String testServer = "https://data.mensa-ka.de/"; +const String testApiKey = "vN66fB7ACeDJDfN6csic9AFQn5r47mtRMmHSrc9XXeMRU5FCPG36vbsAAZTsnSqUE3erX2nMkwLyvgorpe7N6gfDtTvVWN3nzgnmNXFQQFXXi9BmhV62j6M5qXPWE6tL"; \ No newline at end of file diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index 19c513e8..fd95eb0f 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -8,6 +8,8 @@ import 'package:app/view_model/repository/data_classes/settings/PriceCategory.da import 'package:app/view_model/repository/interface/ILocalStorage.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:uuid/uuid.dart'; +import 'package:uuid/uuid_util.dart'; import '../../view_model/repository/data_classes/filter/Sorting.dart'; import '../../view_model/repository/data_classes/meal/FoodType.dart'; @@ -23,7 +25,12 @@ class SharedPreferenceAccess implements ILocalStorage { @override String? getClientIdentifier() { - final clientIdentifier = _pref.getString('clientIdentifier') ?? "1"; + String? clientIdentifier = _pref.getString('clientIdentifier'); + if (clientIdentifier == null) { + var uuid = const Uuid(); + clientIdentifier = uuid.v4(); + _pref.setString('clientIdentifier', clientIdentifier); + } return clientIdentifier; } diff --git a/app/lib/view/core/buttons/MensaButton.dart b/app/lib/view/core/buttons/MensaButton.dart index 414c2c8a..2f86d324 100644 --- a/app/lib/view/core/buttons/MensaButton.dart +++ b/app/lib/view/core/buttons/MensaButton.dart @@ -19,7 +19,7 @@ class MensaButton extends StatelessWidget { /// @returns The widget. @override Widget build(BuildContext context) { - return (MaterialButton( + return MaterialButton( textColor: Theme.of(context).colorScheme.onSurface, color: Theme.of(context).colorScheme.surface, shape: RoundedRectangleBorder( @@ -30,6 +30,6 @@ class MensaButton extends StatelessWidget { onPressed: _onPressed, onLongPress: _onLongPressed, child: Text(_text), - )); + ); } } diff --git a/app/lib/view/core/dialogs/MensaDialog.dart b/app/lib/view/core/dialogs/MensaDialog.dart index 6edfcd50..11d927ee 100644 --- a/app/lib/view/core/dialogs/MensaDialog.dart +++ b/app/lib/view/core/dialogs/MensaDialog.dart @@ -1,32 +1,31 @@ import 'package:flutter/material.dart'; -class MensaDialog { - static void show( - {required BuildContext context, - required String title, - Widget? content, - Widget? actions, - bool? barrierDismissible}) { - showDialog( - context: context, - useSafeArea: true, - barrierDismissible: barrierDismissible ?? true, - builder: (BuildContext context) { - return Dialog( - backgroundColor: Theme.of(context).colorScheme.background, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: EdgeInsets.all(16), - child: Text(title, - style: const TextStyle( - fontSize: 20, fontWeight: FontWeight.bold))), - if (content != null) content, - if (actions != null) actions - ])); - }, - ); +class MensaDialog extends StatelessWidget { + final String _title; + final Widget? _content; + final Widget? _actions; + + const MensaDialog( + {super.key, required String title, Widget? content, Widget? actions}) + : _title = title, + _content = content, + _actions = actions; + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: Theme.of(context).colorScheme.background, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: Text(_title, + style: const TextStyle( + fontSize: 20, fontWeight: FontWeight.bold))), + _content ?? Container(), + _actions ?? Container(), + ])); } } diff --git a/app/lib/view/core/dialogs/MensaFullscreenDialog.dart b/app/lib/view/core/dialogs/MensaFullscreenDialog.dart index 9e6db726..245dedd3 100644 --- a/app/lib/view/core/dialogs/MensaFullscreenDialog.dart +++ b/app/lib/view/core/dialogs/MensaFullscreenDialog.dart @@ -1,24 +1,24 @@ import 'package:flutter/material.dart'; -class MensaFullscreenDialog { - static void show( - {required BuildContext context, +class MensaFullscreenDialog extends StatelessWidget { + final PreferredSizeWidget? _appBar; + final Widget? _content; + final Widget? _actions; + + const MensaFullscreenDialog( + {super.key, PreferredSizeWidget? appBar, Widget? content, - Widget? actions}) { - showDialog( - context: context, - builder: (BuildContext context) { - return Dialog.fullscreen( - backgroundColor: Theme.of(context).colorScheme.background, - child: SafeArea( - child: Scaffold( - backgroundColor: Theme.of(context).colorScheme.background, - appBar: appBar, - body: content, - bottomNavigationBar: actions), - )); - }, - ); + Widget? actions}) + : _appBar = appBar, + _content = content, + _actions = actions; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: _appBar, + body: _content ?? Container(), + bottomNavigationBar: _actions ?? Container()); } } diff --git a/app/lib/view/core/input_components/MensaTextField.dart b/app/lib/view/core/input_components/MensaTextField.dart new file mode 100644 index 00000000..4299c2b3 --- /dev/null +++ b/app/lib/view/core/input_components/MensaTextField.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class MensaTextField extends StatelessWidget { + + final TextEditingController _controller; + + const MensaTextField({super.key, required TextEditingController controller}) + : _controller = controller; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(8), + ), + padding: const EdgeInsets.symmetric(horizontal: 8), + child: TextField( + controller: _controller, + decoration: const InputDecoration( + border: InputBorder.none, + ), + )); + } +} diff --git a/app/lib/view/core/selection_components/MensaSlider.dart b/app/lib/view/core/selection_components/MensaSlider.dart index 92f9281d..0e9035cf 100644 --- a/app/lib/view/core/selection_components/MensaSlider.dart +++ b/app/lib/view/core/selection_components/MensaSlider.dart @@ -18,11 +18,11 @@ class MensaSlider extends StatelessWidget { /// @returns A new MensaSlider. const MensaSlider( {super.key, - required onChanged, - required value, - min = 0.0, - max = 1.0, - divisions}) + required Function(double)? onChanged, + required double value, + double min = 0.0, + double max = 1.0, + int? divisions}) : _onChanged = onChanged, _value = value, _min = min, diff --git a/app/lib/view/detail_view/DetailsPage.dart b/app/lib/view/detail_view/DetailsPage.dart index 73b9f1e4..d3f90676 100644 --- a/app/lib/view/detail_view/DetailsPage.dart +++ b/app/lib/view/detail_view/DetailsPage.dart @@ -13,7 +13,9 @@ import 'package:app/view/core/information_display/MealSideEntry.dart'; import 'package:app/view/core/input_components/MensaRatingInput.dart'; import 'package:app/view/detail_view/MealAccordion.dart'; import 'package:app/view/detail_view/MealAccordionInfo.dart'; +import 'package:app/view/detail_view/MealRatingDialog.dart'; import 'package:app/view/detail_view/RatingsOverview.dart'; +import 'package:app/view/detail_view/UploadImageDialog.dart'; import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; import 'package:app/view_model/logic/meal/IMealAccess.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; @@ -58,13 +60,24 @@ class DetailsPageState extends State { onPressed: () => Navigator.of(context).pop(), icon: NavigationBackIcon()), Spacer(), - Consumer(builder: (context, favoriteMealAccess, child) => MensaIconButton( - onPressed: () => { - favoriteMealAccess.addFavoriteMeal(widget._meal) - }, - icon: widget._meal.isFavorite ? FavoriteFilledIcon() : FavoriteOutlinedIcon())), + Consumer( + builder: (context, favoriteMealAccess, child) => + MensaIconButton( + onPressed: () => { + favoriteMealAccess + .addFavoriteMeal(widget._meal) + }, + icon: widget._meal.isFavorite + ? FavoriteFilledIcon() + : FavoriteOutlinedIcon())), MensaIconButton( - onPressed: () => Navigator.of(context).pop(), + onPressed: () => { + showDialog( + context: context, + builder: (context) => + UploadImageDialog(meal: widget._meal), + ) + }, icon: NavigationAddImageIcon()), ], )), @@ -183,7 +196,8 @@ class DetailsPageState extends State { Row(children: [ MensaRatingInput( value: widget._meal.individualRating - ?.toDouble() ?? 0, + ?.toDouble() ?? + 0, disabled: true, color: Theme.of(context).colorScheme.onSurface, @@ -192,7 +206,17 @@ class DetailsPageState extends State { onChanged: (int) {}, ), Spacer(), - MensaButton(text: FlutterI18n.translate(context, "ratings.editRating"), onPressed: null,) + MensaButton( + text: FlutterI18n.translate( + context, "ratings.editRating"), + onPressed: () { + showDialog( + context: context, + builder: (context) => MealRatingDialog( + meal: widget._meal), + barrierDismissible: true); + }, + ) ]) ], ), diff --git a/app/lib/view/detail_view/MealRatingDialog.dart b/app/lib/view/detail_view/MealRatingDialog.dart index df69f0e6..facc18c1 100644 --- a/app/lib/view/detail_view/MealRatingDialog.dart +++ b/app/lib/view/detail_view/MealRatingDialog.dart @@ -7,39 +7,64 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; -class MealRatingDialog { - static void show(BuildContext context, Meal meal) { - int rating = meal.individualRating ?? 0; - - MensaDialog.show(context: context, - title: "${meal.name} ${FlutterI18n.translate(context, "ratings.dialogTitle")}", - content: Consumer(builder: (context, mealAccess, child) => - Column(children: [ - MensaRatingInput(onChanged: (value) { - rating = value; - }, - value: rating as double), - Row(children: [ - const Spacer(), - MensaButton(onPressed: () async { - final temporalMessage = await mealAccess.updateMealRating(rating, meal); - Navigator.pop(context); +class MealRatingDialog extends StatefulWidget { + final Meal _meal; - if (temporalMessage.isNotEmpty) { - final snackBar = SnackBar( - content: Text(FlutterI18n.translate( - context, temporalMessage)), - backgroundColor: Theme.of(context).colorScheme.onError, - ); + const MealRatingDialog({Key? key, required Meal meal}) + : _meal = meal, + super(key: key); - ScaffoldMessenger.of(context) - .showSnackBar(snackBar); - } + @override + State createState() => _MealRatingDialogState(); +} - }, - text: FlutterI18n.translate(context, "image.saveRating")) - ],) - ],)) - ); +class _MealRatingDialogState extends State { + int? rating; + + @override + Widget build(BuildContext context) { + Meal meal = widget._meal; + rating = rating ?? meal.individualRating ?? 0; + return MensaDialog( + title: + "${meal.name} ${FlutterI18n.translate(context, "ratings.dialogTitle")}", + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MensaRatingInput( + onChanged: (value) { + setState(() { + rating = value; + }); + }, + value: rating!.toDouble()), + ], + ), + actions: Row( + children: [ + const Spacer(), + MensaButton( + onPressed: () async { + final temporalMessage = + await context.read().updateMealRating( + rating!, + meal, + ); + if (!context.mounted) return; + Navigator.pop(context); + + if (temporalMessage.isNotEmpty) { + final snackBar = SnackBar( + content: + Text(FlutterI18n.translate(context, temporalMessage)), + backgroundColor: Theme.of(context).colorScheme.onError, + ); + + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } + }, + text: FlutterI18n.translate(context, "image.saveRating")) + ], + )); } -} \ No newline at end of file +} diff --git a/app/lib/view/detail_view/UploadImageDialog.dart b/app/lib/view/detail_view/UploadImageDialog.dart index f1de7f2c..36308102 100644 --- a/app/lib/view/detail_view/UploadImageDialog.dart +++ b/app/lib/view/detail_view/UploadImageDialog.dart @@ -1,5 +1,6 @@ import 'package:app/view/core/buttons/MensaButton.dart'; import 'package:app/view/core/dialogs/MensaDialog.dart'; +import 'package:app/view/core/input_components/MensaTextField.dart'; import 'package:app/view_model/logic/image/IImageAccess.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:flutter/gestures.dart'; @@ -8,109 +9,103 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:provider/provider.dart'; -class UploadImageDialog { - static void show(BuildContext context, Meal meal) { - const TextStyle linkStyle = - TextStyle(color: Colors.blue, decoration: TextDecoration.underline); +class UploadImageDialog extends StatelessWidget { + final Meal _meal; - final textFieldController = TextEditingController(); + final TextEditingController _textFieldController = TextEditingController(); - MensaDialog.show( - context: context, - title: FlutterI18n.translate(context, "image.linkImageTitle"), - content: Consumer( - builder: (context, imageAccess, child) => Column( - children: [ - Text(FlutterI18n.translate( - context, "image.linkDescription")), - Container( - padding: const EdgeInsets.only(left: 4), - child: Column( - children: [ - RichText( - text: TextSpan(children: [ - TextSpan( - text: FlutterI18n.translate( - context, "image.linkFirstPoint")), - TextSpan( - text: 'clickable word', - style: linkStyle, - recognizer: TapGestureRecognizer() - ..onTap = () { - _launchURL(); - }, - ), - TextSpan( - text: FlutterI18n.translate(context, - "image.linkFirstPointSecondText")) - ]), - ), - Text(FlutterI18n.translate( - context, "image.linkSecondPoint")), - Text(FlutterI18n.translate( - context, "image.linkThirdPoint")), - Text(FlutterI18n.translate( - context, "image.linkFourthPoint")) - ], - )), - // todo padding - Text(FlutterI18n.translate( - context, "image.linkTextFieldDescription")), - TextField( - controller: textFieldController, - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: FlutterI18n.translate( - context, "image.linkTextFieldDescription"), + UploadImageDialog({Key? key, required Meal meal}) + : _meal = meal, + super(key: key); + + @override + Widget build(BuildContext context) { + return MensaDialog( + title: FlutterI18n.translate(context, "image.linkImageTitle"), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(FlutterI18n.translate(context, "image.linkDescription")), + Container( + padding: const EdgeInsets.only(left: 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText( + text: TextSpan(children: [ + TextSpan( + text: FlutterI18n.translate( + context, "image.linkFirstPoint")), + TextSpan( + text: 'clickable word', + style: const TextStyle( + color: Colors.blue, + decoration: TextDecoration.underline), + recognizer: TapGestureRecognizer() + ..onTap = () { + _launchURL(); + }, ), - ), - // todo padding - Text( - FlutterI18n.translate( - context, "image.linkSmallTextOwnImages"), - style: DefaultTextStyle.of(context) - .style - .apply(fontSizeFactor: 0.5)), - Text( - FlutterI18n.translate( - context, "image.linkSmallTextDisplayed"), - style: DefaultTextStyle.of(context) - .style - .apply(fontSizeFactor: 0.5), - ), - Row( - children: [ - const Spacer(), - MensaButton( - onPressed: () async { - final temporalMessage = await imageAccess - .linkImage(textFieldController.text, meal); - Navigator.pop(context); + TextSpan( + text: FlutterI18n.translate( + context, "image.linkFirstPointSecondText")) + ]), + ), + Text(FlutterI18n.translate(context, "image.linkSecondPoint")), + Text(FlutterI18n.translate(context, "image.linkThirdPoint")), + Text(FlutterI18n.translate(context, "image.linkFourthPoint")) + ], + )), + // todo padding + Text( + FlutterI18n.translate(context, "image.linkTextFieldDescription")), + MensaTextField( + controller: _textFieldController, + ), + // todo padding + Text(FlutterI18n.translate(context, "image.linkSmallTextOwnImages"), + style: DefaultTextStyle.of(context) + .style + .apply(fontSizeFactor: 0.5)), + Text( + FlutterI18n.translate(context, "image.linkSmallTextDisplayed"), + style: + DefaultTextStyle.of(context).style.apply(fontSizeFactor: 0.5), + ), + ], + ), + actions: Row( + children: [ + const Spacer(), + MensaButton( + onPressed: () async { + final temporalMessage = await context + .read() + .linkImage(_textFieldController.text, _meal); + if (!context.mounted) return; + + Navigator.pop(context); - if (temporalMessage.isNotEmpty) { - final snackBar = SnackBar( - content: Text(FlutterI18n.translate( - context, temporalMessage)), - backgroundColor: - Theme.of(context).colorScheme.onError, - ); + if (temporalMessage.isNotEmpty) { + final snackBar = SnackBar( + content: + Text(FlutterI18n.translate(context, temporalMessage)), + backgroundColor: Theme.of(context).colorScheme.onError, + ); - ScaffoldMessenger.of(context) - .showSnackBar(snackBar); - } - }, - text: FlutterI18n.translate( - context, "image.linkButton")) - ], - ) - ], - ))); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } + }, + text: FlutterI18n.translate(context, "image.linkButton")) + ], + ), + ); } static void _launchURL() async { Uri url = Uri.parse('https://flickr.com/'); if (await launchUrl(url)) { - await launchUrl(url); + await launchUrl(url, mode: LaunchMode.externalApplication); } else { throw 'Could not launch $url'; } diff --git a/app/lib/view/filter/FilterDialog.dart b/app/lib/view/filter/FilterDialog.dart index 15268f92..0e9f3a73 100644 --- a/app/lib/view/filter/FilterDialog.dart +++ b/app/lib/view/filter/FilterDialog.dart @@ -1,7 +1,10 @@ import 'package:app/view/core/MensaAppBar.dart'; import 'package:app/view/core/buttons/MensaButton.dart'; +import 'package:app/view/core/buttons/MensaCtaButton.dart'; +import 'package:app/view/core/buttons/MensaIconButton.dart'; import 'package:app/view/core/dialogs/MensaFullscreenDialog.dart'; import 'package:app/view/core/icons/allergens/AllergenIcon.dart'; +import 'package:app/view/core/icons/navigation/NavigationBackIcon.dart'; import 'package:app/view/core/selection_components/MensaSlider.dart'; import 'package:app/view/core/selection_components/MensaToggle.dart'; import 'package:app/view/filter/MensaButtonGroup.dart'; @@ -18,96 +21,116 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; // todo padding -class FilterDialog { - static void show(BuildContext context) { - FilterPreferences preferences = FilterPreferences(); - - MensaFullscreenDialog.show( - context: context, - appBar: MensaAppBar( - appBarHeight: kToolbarHeight * 1.25, - child: Row( - children: [ - // todo icon back - Text( - FlutterI18n.translate(context, "filter.filterTitle"), - style: const TextStyle( - fontSize: 24, fontWeight: FontWeight.bold), - ), - const Spacer(), - // todo temporary disable or enable filter - ], - )), - content: Consumer( - builder: (context, mealAccess, child) => FutureBuilder( - future: mealAccess.getFilterPreferences(), - builder: (context, snapshot) { - if (snapshot.hasData) { - preferences = snapshot.requireData; - } - - // todo Scrollable - return Column( +class FilterDialog extends StatefulWidget { + @override + State createState() => _FilterDialogState(); +} + +class _FilterDialogState extends State { + FilterPreferences _preferences = FilterPreferences(); + + @override + Widget build(BuildContext context) { + return MensaFullscreenDialog( + appBar: MensaAppBar( + appBarHeight: kToolbarHeight * 1.25, + child: Row( + children: [ + MensaIconButton( + onPressed: () => Navigator.of(context).pop(), + icon: NavigationBackIcon()), + Text( + FlutterI18n.translate(context, "filter.filterTitle"), + style: + const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), + const Spacer(), + // todo temporary disable or enable filter + ], + )), + content: Consumer( + builder: (context, mealAccess, child) => FutureBuilder( + future: mealAccess.getFilterPreferences(), + builder: (BuildContext context, filterPreferences) { + if (!filterPreferences.hasData) { + return const Center(child: CircularProgressIndicator()); + } + if (filterPreferences.hasError) { + return const Center(child: Text("Error")); + } + _preferences = filterPreferences.requireData; + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(FlutterI18n.translate(context, "filter.foodType")), + MensaButtonGroup( + value: _getValueCategory(_preferences.categories), + onChanged: (value) { + _setValueCategory(value, _preferences); + setState(() { + _preferences = _preferences; + }); + }, + entries: _getAllFoodTypeEntries(context)), + // todo checkboxes + Text(FlutterI18n.translate(context, "filter.allergensTitle")), + MensaFilterIconCheckboxGroup( + items: _getAllAllergen(context), + selectedValues: _preferences.allergens, + onChanged: (value) { + _preferences.allergens = value; + }), + Text(FlutterI18n.translate(context, "filter.priceTitle")), + MensaSlider( + onChanged: (value) => _preferences.price = value.round(), + value: _preferences.price.toDouble(), + min: 0, + max: 10), + MensaSlider( + onChanged: (value) => _preferences.rating = value.round(), + value: _preferences.rating.toDouble(), + min: 1, + max: 1, + ), + MensaToggle( + onChanged: (value) => _preferences.onlyFavorite = value, + value: _preferences.onlyFavorite, + label: FlutterI18n.translate( + context, "filter.favoritesOnlyTitle")), + Text(FlutterI18n.translate(context, "filter.frequencyTitle")), + MensaButtonGroup( + value: _getValueFrequency(_preferences.frequency), + onChanged: (value) => + _setValueFrequency(value, _preferences), + entries: _getAllFrequencyEntries(context)), + Text(FlutterI18n.translate(context, "filter.sortByTitle")), + Row( children: [ - Text(FlutterI18n.translate(context, "filter.foodType")), - MensaButtonGroup( - value: _getValueCategory(preferences.categories), - onChanged: (value) => - _setValueCategory(value, preferences), - entries: _getAllFoodTypeEntries(context)), - // todo checkboxes - Text(FlutterI18n.translate( - context, "filter.allergensTitle")), - MensaFilterIconCheckboxGroup( - items: _getAllAllergen(context), - selectedValues: preferences.allergens, - onChanged: (value) { - preferences.allergens = value; - }), - Text(FlutterI18n.translate(context, "filter.priceTitle")), - MensaSlider( - onChanged: (value) => preferences.price = value, - value: preferences.price, - min: "0€", - max: "${FilterPreferences().price}€"), - Text( - FlutterI18n.translate(context, "filter.ratingTitle")), - MensaSlider( - onChanged: (value) => preferences.rating = value, - value: preferences.rating, - min: "5 Sterne", - max: "1 Stern"), - MensaToggle( - onChanged: (value) => - preferences.onlyFavorite = value, - value: preferences.onlyFavorite, - label: FlutterI18n.translate( - context, "filter.favoritesOnlyTitle")), - Text(FlutterI18n.translate( - context, "filter.frequencyTitle")), - MensaButtonGroup( - value: _getValueFrequency(preferences.frequency), - onChanged: (value) => - _setValueFrequency(value, preferences), - entries: _getAllFrequencyEntries(context)), - Text( - FlutterI18n.translate(context, "filter.sortByTitle")), - Row( - children: [ - // todo SortDropdown in Expanded - // todo Icon for ascending / descending - ], - ), - MensaButton( - onPressed: () { - Navigator.pop(context); - mealAccess.changeFilterPreferences(preferences); - }, - text: FlutterI18n.translate( - context, "filter.storeButton")) + // todo SortDropdown in Expanded + // todo Icon for ascending / descending ], - ); - }))); + ), + ], + ), + ); + }, + ), + ), + actions: Row( + children: [ + Spacer(), + MensaCtaButton( + onPressed: () { + context + .read() + .changeFilterPreferences(_preferences); + Navigator.of(context).pop(); + }, + text: FlutterI18n.translate(context, "filter.apply")), + ], + ), + ); } static _setValueFrequency(Frequency frequency, FilterPreferences filter) { diff --git a/app/lib/view/mealplan/MealPlanView.dart b/app/lib/view/mealplan/MealPlanView.dart index c1d9d76d..7249c511 100644 --- a/app/lib/view/mealplan/MealPlanView.dart +++ b/app/lib/view/mealplan/MealPlanView.dart @@ -5,6 +5,7 @@ import 'package:app/view/core/icons/navigation/NavigationGridOutlinedIcon.dart'; import 'package:app/view/core/icons/navigation/NavigationListOutlinedIcon.dart'; import 'package:app/view/core/meal_view_format/MealGrid.dart'; import 'package:app/view/core/meal_view_format/MealList.dart'; +import 'package:app/view/filter/FilterDialog.dart'; import 'package:app/view/mealplan/MealPlanClosed.dart'; import 'package:app/view/mealplan/MealPlanDateSelect.dart'; import 'package:app/view/mealplan/MealPlanError.dart'; @@ -46,7 +47,8 @@ class MealPlanView extends StatelessWidget { ); } if (snapshot.hasError) return const MealPlanError(); - MealPlanFormat mealPlanFormat = preferenceAccess.getMealPlanFormat(); + MealPlanFormat mealPlanFormat = + preferenceAccess.getMealPlanFormat(); Canteen selectedCanteen = snapshot.requireData[0] as Canteen; List availableCanteens = @@ -90,7 +92,13 @@ class MealPlanView extends StatelessWidget { padding: EdgeInsets.all(8), child: NavigationFilterOutlinedIcon()), - onTap: () => {}) + onTap: () => { + showDialog( + context: context, + builder: (context) => + FilterDialog(), + ) + }) ], ))), child: Padding( diff --git a/app/lib/view/settings/SettingsDropdownEntry.dart b/app/lib/view/settings/SettingsDropdownEntry.dart index 9b5786cd..1ed31013 100644 --- a/app/lib/view/settings/SettingsDropdownEntry.dart +++ b/app/lib/view/settings/SettingsDropdownEntry.dart @@ -39,27 +39,17 @@ class SettingsDropdownEntry extends StatelessWidget { Text( FlutterI18n.translate(context, _heading), style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - height: 1.5), + fontSize: 14, fontWeight: FontWeight.bold, height: 1.5), ), - Container( - // Container is used to give the dropdown a background color. - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4.0), - color: Theme.of(context).colorScheme.surface, - ), - child: Row( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: MensaDropdown( - onChanged: _onChanged, - value: _value, - items: _items), - ) - ] - )) + Row(children: [ + Expanded( + child: MensaDropdown( + backgroundColor: Theme.of(context).colorScheme.surface, + onChanged: _onChanged, + value: _value, + items: _items), + ) + ]) ], ); } diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index b5d200b2..97dde704 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -209,11 +209,17 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { final mealPlan = await _getMealPlanFromServer(); - if (_mealPlans.isEmpty) { + + if (mealPlan.isEmpty) { return "snackbar.refreshMealPlanError"; } + _database.updateAll(mealPlan); + _mealPlans = mealPlan; + + await _setNewMealPlan(); + await _filterMealPlans(); notifyListeners(); diff --git a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart index df0a76ce..ddc7ede4 100644 --- a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart +++ b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart @@ -37,7 +37,7 @@ class FilterPreferences { }) : _categories = categories ?? _getAllFoodTypes(), _allergens = allergens ?? _getAllAllergen(), - _price = price ?? 1000, + _price = price ?? 10, _rating = rating ?? 0, _frequency = frequency ?? _getAllFrequencies(), _onlyFavorite = onlyFavorite ?? false, From 519ed925192280b3aba550f94f6c0af1cc9c8901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Thu, 27 Jul 2023 21:45:20 +0200 Subject: [PATCH 162/184] make dialogs widgets --- app/lib/view/images/ImageReportDialog.dart | 102 ++++++++++++--------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/app/lib/view/images/ImageReportDialog.dart b/app/lib/view/images/ImageReportDialog.dart index 381a9beb..dd8918be 100644 --- a/app/lib/view/images/ImageReportDialog.dart +++ b/app/lib/view/images/ImageReportDialog.dart @@ -5,57 +5,77 @@ import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; import 'package:app/view_model/logic/image/IImageAccess.dart'; import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; -class ImageReportDialog { - static ReportCategory _reason = ReportCategory.other; - - static void show(BuildContext context, ImageData image) async { - MensaDialog.show(context: context, - title: FlutterI18n.translate(context, "image.reportImageTitle"), - content: Consumer(builder: (context, imageAccess, child) => Column(children: [ - Text(FlutterI18n.translate(context, "image.reportDescription")), - Row(children: [ - Expanded(child: MensaDropdown(onChanged: (value) { - if (value != null) { - _reason = value; - } - }, - value: _reason, - items: _getReportCategoryEntries(context),)) - ],), - Row(children: [ - const Spacer(), - MensaButton(onPressed: () async { - final temporalMessage = await imageAccess.reportImage(image, _reason); - Navigator.pop(context); - - if (temporalMessage.isNotEmpty) { - final snackBar = SnackBar( - content: Text(FlutterI18n.translate( - context, temporalMessage)), - backgroundColor: Theme.of(context).colorScheme.onError, - ); - - ScaffoldMessenger.of(context) - .showSnackBar(snackBar); - } - }, - text: FlutterI18n.translate(context, "image.reportButton")), - ],) - ],)) +class ImageReportDialog extends StatelessWidget { + ReportCategory _reason = ReportCategory.other; + late ImageData _image; + + ImageReportDialog({super.key, required ImageData image}) : _image = image; + + @override + Widget build(BuildContext context) { + return MensaDialog( + title: FlutterI18n.translate(context, "image.reportImageTitle"), + content: Consumer( + builder: (context, imageAccess, child) => Column( + children: [ + Text(FlutterI18n.translate( + context, "image.reportDescription")), + Row( + children: [ + Expanded( + child: MensaDropdown( + onChanged: (value) { + if (value != null) { + _reason = value; + } + }, + value: _reason, + items: _getReportCategoryEntries(context), + )) + ], + ), + Row( + children: [ + const Spacer(), + MensaButton( + onPressed: () async { + final temporalMessage = + await imageAccess.reportImage(_image, _reason); + Navigator.pop(context); + + if (temporalMessage.isNotEmpty) { + final snackBar = SnackBar( + content: Text(FlutterI18n.translate( + context, temporalMessage)), + backgroundColor: + Theme.of(context).colorScheme.onError, + ); + + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + } + }, + text: FlutterI18n.translate( + context, "image.reportButton")), + ], + ) + ], + )), ); } - static List> _getReportCategoryEntries(BuildContext context) { + List> _getReportCategoryEntries( + BuildContext context) { List> entries = []; for (final value in ReportCategory.values) { - entries - .add(MensaDropdownEntry(value: value, label: FlutterI18n.translate(context, "reportReason.${value.name}"))); + entries.add(MensaDropdownEntry( + value: value, + label: FlutterI18n.translate(context, "reportReason.${value.name}"))); } return entries; From 1ec075f5b0fb3746595433a342e7953de1caa3ae Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Thu, 27 Jul 2023 22:35:53 +0200 Subject: [PATCH 163/184] not so many changes this time --- app/lib/view/core/buttons/MensaTapable.dart | 8 +- app/lib/view/filter/FilterDialog.dart | 79 +++++++++++++------ app/lib/view/filter/MensaSortSelect.dart | 54 +++++++++++++ app/lib/view/filter/MensaSortSelectEntry.dart | 9 +++ app/lib/view/mealplan/MealPlanView.dart | 3 + .../logic/meal/CombinedMealPlanAccess.dart | 12 +++ .../view_model/logic/meal/IMealAccess.dart | 2 + .../filter/FilterPreferences.dart | 2 +- 8 files changed, 144 insertions(+), 25 deletions(-) create mode 100644 app/lib/view/filter/MensaSortSelect.dart create mode 100644 app/lib/view/filter/MensaSortSelectEntry.dart diff --git a/app/lib/view/core/buttons/MensaTapable.dart b/app/lib/view/core/buttons/MensaTapable.dart index bb717c5a..6fb72340 100644 --- a/app/lib/view/core/buttons/MensaTapable.dart +++ b/app/lib/view/core/buttons/MensaTapable.dart @@ -4,15 +4,18 @@ class MensaTapable extends StatelessWidget { final Widget _child; final Color? _color; final Function() _onTap; + final Function()? _onLongPress; MensaTapable( {super.key, required Widget child, Color? color, - required Function() onTap}) + required Function() onTap, + Function()? onLongPress}) : _child = child, _color = color, - _onTap = onTap; + _onTap = onTap, + _onLongPress = onLongPress; @override Widget build(BuildContext context) { @@ -22,6 +25,7 @@ class MensaTapable extends StatelessWidget { child: InkWell( borderRadius: BorderRadius.circular(4), onTap: _onTap, + onLongPress: _onLongPress, child: _child, ), ); diff --git a/app/lib/view/filter/FilterDialog.dart b/app/lib/view/filter/FilterDialog.dart index 0e9f3a73..9fb80490 100644 --- a/app/lib/view/filter/FilterDialog.dart +++ b/app/lib/view/filter/FilterDialog.dart @@ -64,7 +64,7 @@ class _FilterDialogState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(FlutterI18n.translate(context, "filter.foodType")), - MensaButtonGroup( + MensaButtonGroup( value: _getValueCategory(_preferences.categories), onChanged: (value) { _setValueCategory(value, _preferences); @@ -80,29 +80,51 @@ class _FilterDialogState extends State { selectedValues: _preferences.allergens, onChanged: (value) { _preferences.allergens = value; + setState(() { + _preferences = _preferences; + }); }), Text(FlutterI18n.translate(context, "filter.priceTitle")), MensaSlider( - onChanged: (value) => _preferences.price = value.round(), + onChanged: (value) { + _preferences.price = value.round(); + setState(() { + _preferences = _preferences; + }); + }, value: _preferences.price.toDouble(), min: 0, - max: 10), + max: 1000), MensaSlider( - onChanged: (value) => _preferences.rating = value.round(), + onChanged: (value) { + _preferences.rating = value.round(); + setState(() { + _preferences = _preferences; + }); + }, value: _preferences.rating.toDouble(), min: 1, - max: 1, + max: 5, ), MensaToggle( - onChanged: (value) => _preferences.onlyFavorite = value, + onChanged: (value) { + _preferences.onlyFavorite = value; + setState(() { + _preferences = _preferences; + }); + }, value: _preferences.onlyFavorite, label: FlutterI18n.translate( context, "filter.favoritesOnlyTitle")), Text(FlutterI18n.translate(context, "filter.frequencyTitle")), MensaButtonGroup( value: _getValueFrequency(_preferences.frequency), - onChanged: (value) => - _setValueFrequency(value, _preferences), + onChanged: (value) { + _setValueFrequency(value, _preferences); + setState(() { + _preferences = _preferences; + }); + }, entries: _getAllFrequencyEntries(context)), Text(FlutterI18n.translate(context, "filter.sortByTitle")), Row( @@ -193,25 +215,36 @@ class _FilterDialogState extends State { return entries; } - static List> _getAllFoodTypeEntries( + static List> _getAllFoodTypeEntries( BuildContext context) { - final List> entries = []; + final List> entries = []; entries.add(MensaButtonGroupEntry( title: FlutterI18n.translate(context, "filter.foodTypeSectionAll"), - value: FoodType.beef)); + value: 0)); entries.add(MensaButtonGroupEntry( title: FlutterI18n.translate(context, "filter.foodTypeSectionVegetarian"), - value: FoodType.vegetarian)); + value: 1)); entries.add(MensaButtonGroupEntry( title: FlutterI18n.translate(context, "filter.foodTypeSectionVegan"), - value: FoodType.vegan)); + value: 2)); return entries; } - static _setValueCategory(FoodType type, FilterPreferences access) { + static _setValueCategory(int type, FilterPreferences access) { + switch (type) { + case 0: + access.setAllCategories(); + return; + case 1: + access.setCategoriesVegetarian(); + return; + case 2: + access.setCategoriesVegan(); + return; + } if (type == FoodType.vegan) { access.setCategoriesVegan(); return; @@ -225,14 +258,16 @@ class _FilterDialogState extends State { access.setAllCategories(); } - static FoodType _getValueCategory(List types) { - switch (types.length) { - case 1: - return FoodType.vegan; - case 2: - return FoodType.vegetarian; - default: - return FoodType.beef; + static int _getValueCategory(List types) { + if (types.contains(FoodType.beef) || types.contains(FoodType.beefAw) || types.contains(FoodType.pork) || types.contains(FoodType.porkAw)) { + return 0; + } + if (types.contains(FoodType.vegetarian)) { + return 1; + } + if (types.contains(FoodType.vegan)) { + return 2; } + return 0; } } diff --git a/app/lib/view/filter/MensaSortSelect.dart b/app/lib/view/filter/MensaSortSelect.dart new file mode 100644 index 00000000..af85806d --- /dev/null +++ b/app/lib/view/filter/MensaSortSelect.dart @@ -0,0 +1,54 @@ +import 'package:app/view/core/buttons/MensaTapable.dart'; +import 'package:app/view/core/selection_components/MensaDropdown.dart'; +import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; +import 'package:app/view/filter/MensaSortSelectEntry.dart'; +import 'package:flutter/material.dart'; + +class MensaSortSelect extends StatelessWidget { + final List> _entries; + final T _selectedEntry; + final SortDirection _sortDirection; + final Function(T) _onEntrySelected; + final Function(SortDirection) _onSortDirectionSelected; + + const MensaSortSelect( + {super.key, + required List> entries, + required T selectedEntry, + required SortDirection sortDirection, + required Function(T) onEntrySelected, + required Function(SortDirection) onSortDirectionSelected}) + : _entries = entries, + _selectedEntry = selectedEntry, + _onEntrySelected = onEntrySelected, + _sortDirection = sortDirection, + _onSortDirectionSelected = onSortDirectionSelected; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + MensaDropdown( + onChanged: (v) => _onEntrySelected(v), + value: _selectedEntry, + items: _entries + .map((e) => MensaDropdownEntry( + value: e.value, + label: e.label, + )) + .toList()), + MensaTapable( + child: const Padding( + padding: EdgeInsets.all(8), + ), + onTap: () => _onSortDirectionSelected( + _sortDirection == SortDirection.ascending + ? SortDirection.descending + : SortDirection.ascending), + ) + ], + ); + } +} + +enum SortDirection { ascending, descending } diff --git a/app/lib/view/filter/MensaSortSelectEntry.dart b/app/lib/view/filter/MensaSortSelectEntry.dart new file mode 100644 index 00000000..e5ed42c5 --- /dev/null +++ b/app/lib/view/filter/MensaSortSelectEntry.dart @@ -0,0 +1,9 @@ +import 'package:app/view/core/selection_components/MensaDropdown.dart'; +import 'package:flutter/cupertino.dart'; + +class MensaSortSelectEntry { + final T value; + final String label; + + const MensaSortSelectEntry({required this.value, required this.label}); +} diff --git a/app/lib/view/mealplan/MealPlanView.dart b/app/lib/view/mealplan/MealPlanView.dart index 7249c511..51ca4166 100644 --- a/app/lib/view/mealplan/MealPlanView.dart +++ b/app/lib/view/mealplan/MealPlanView.dart @@ -92,6 +92,9 @@ class MealPlanView extends StatelessWidget { padding: EdgeInsets.all(8), child: NavigationFilterOutlinedIcon()), + onLongPress: () => { + mealAccess.toggleFilter(), + }, onTap: () => { showDialog( context: context, diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 97dde704..01c0813a 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -329,6 +329,18 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { notifyListeners(); } + @override + Future toggleFilter() async { + await _doneInitialization; + + if (_activeFilter) { + await deactivateFilter(); + } else { + await activateFilter(); + } + + } + void _changeRatingOfMeal(Meal changedMeal, int rating) { for (final mealPlan in _mealPlans) { // check if right meal plan diff --git a/app/lib/view_model/logic/meal/IMealAccess.dart b/app/lib/view_model/logic/meal/IMealAccess.dart index b64c5a2d..8d47f987 100644 --- a/app/lib/view_model/logic/meal/IMealAccess.dart +++ b/app/lib/view_model/logic/meal/IMealAccess.dart @@ -84,4 +84,6 @@ abstract class IMealAccess with ChangeNotifier { /// This method deactivates the filter. /// @return The result of the update Future deactivateFilter(); + + Future toggleFilter(); } diff --git a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart index ddc7ede4..37890312 100644 --- a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart +++ b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart @@ -38,7 +38,7 @@ class FilterPreferences { : _categories = categories ?? _getAllFoodTypes(), _allergens = allergens ?? _getAllAllergen(), _price = price ?? 10, - _rating = rating ?? 0, + _rating = rating ?? 1, _frequency = frequency ?? _getAllFrequencies(), _onlyFavorite = onlyFavorite ?? false, _sortedBy = sortedBy ?? Sorting.line, From 848c19e970ccbd692647965eef95f8e52514fff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Thu, 27 Jul 2023 22:55:45 +0200 Subject: [PATCH 164/184] documentation --- app/lib/view_model/logic/meal/IMealAccess.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/app/lib/view_model/logic/meal/IMealAccess.dart b/app/lib/view_model/logic/meal/IMealAccess.dart index 8d47f987..57fced43 100644 --- a/app/lib/view_model/logic/meal/IMealAccess.dart +++ b/app/lib/view_model/logic/meal/IMealAccess.dart @@ -85,5 +85,6 @@ abstract class IMealAccess with ChangeNotifier { /// @return The result of the update Future deactivateFilter(); + /// This method toggles the activity of a filter. Future toggleFilter(); } From 01eee81e58ffcd98d08b037dcb663e8d0b789f1c Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Thu, 27 Jul 2023 23:22:17 +0200 Subject: [PATCH 165/184] Almost finalized FilterDialog --- app/assets/icons/filter/filter_restore.svg | 1 + app/assets/icons/filter/sort_ascending.svg | 1 + app/assets/icons/filter/sort_descending.svg | 1 + app/assets/locales/de/filter.json | 4 +- .../core/dialogs/MensaFullscreenDialog.dart | 10 +- .../core/icons/filter/FilterRestoreIcon.dart | 23 ++ .../core/icons/filter/SortAscendingIcon.dart | 23 ++ .../core/icons/filter/SortDecendingIcon.dart | 23 ++ .../selection_components/MensaDropdown.dart | 1 + .../selection_components/MensaToggle.dart | 27 +- app/lib/view/filter/FilterDialog.dart | 387 +++++++++++++----- app/lib/view/filter/MensaSortSelect.dart | 16 +- app/lib/view/mealplan/MealPlanView.dart | 2 +- app/lib/view/mealplan/MensaCanteenSelect.dart | 4 +- .../filter/FilterPreferences.dart | 2 +- app/pubspec.yaml | 1 + 16 files changed, 394 insertions(+), 132 deletions(-) create mode 100644 app/assets/icons/filter/filter_restore.svg create mode 100644 app/assets/icons/filter/sort_ascending.svg create mode 100644 app/assets/icons/filter/sort_descending.svg create mode 100644 app/lib/view/core/icons/filter/FilterRestoreIcon.dart create mode 100644 app/lib/view/core/icons/filter/SortAscendingIcon.dart create mode 100644 app/lib/view/core/icons/filter/SortDecendingIcon.dart diff --git a/app/assets/icons/filter/filter_restore.svg b/app/assets/icons/filter/filter_restore.svg new file mode 100644 index 00000000..af4882f5 --- /dev/null +++ b/app/assets/icons/filter/filter_restore.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/filter/sort_ascending.svg b/app/assets/icons/filter/sort_ascending.svg new file mode 100644 index 00000000..3d04132d --- /dev/null +++ b/app/assets/icons/filter/sort_ascending.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/filter/sort_descending.svg b/app/assets/icons/filter/sort_descending.svg new file mode 100644 index 00000000..8add6c9d --- /dev/null +++ b/app/assets/icons/filter/sort_descending.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/locales/de/filter.json b/app/assets/locales/de/filter.json index 17dc4f92..22dac74a 100644 --- a/app/assets/locales/de/filter.json +++ b/app/assets/locales/de/filter.json @@ -6,8 +6,8 @@ "foodTypeSectionVegan": "Vegan", "foodTypeSelectionUnknown": "Keine Angabe", "foodTypeSelectionPork": "Schweinefleisch", - "foodTypeSelectionCow": "Rindfleisch", - "foodTypeSelectionFisch": "Fisch", + "foodTypeSelectionBeef": "Rindfleisch", + "foodTypeSelectionFish": "Fisch", "allergensTitle": "Allergene", "priceTitle": "Preis", diff --git a/app/lib/view/core/dialogs/MensaFullscreenDialog.dart b/app/lib/view/core/dialogs/MensaFullscreenDialog.dart index 245dedd3..e8a99768 100644 --- a/app/lib/view/core/dialogs/MensaFullscreenDialog.dart +++ b/app/lib/view/core/dialogs/MensaFullscreenDialog.dart @@ -16,9 +16,11 @@ class MensaFullscreenDialog extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - appBar: _appBar, - body: _content ?? Container(), - bottomNavigationBar: _actions ?? Container()); + return Dialog.fullscreen( + backgroundColor: Theme.of(context).colorScheme.background, + child: Scaffold( + appBar: _appBar, + body: _content ?? Container(), + bottomNavigationBar: _actions ?? Container())); } } diff --git a/app/lib/view/core/icons/filter/FilterRestoreIcon.dart b/app/lib/view/core/icons/filter/FilterRestoreIcon.dart new file mode 100644 index 00000000..4827ccfd --- /dev/null +++ b/app/lib/view/core/icons/filter/FilterRestoreIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class FilterRestoreIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const FilterRestoreIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/filter/filter_restore.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/filter/SortAscendingIcon.dart b/app/lib/view/core/icons/filter/SortAscendingIcon.dart new file mode 100644 index 00000000..20477a29 --- /dev/null +++ b/app/lib/view/core/icons/filter/SortAscendingIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class SortAscendingIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const SortAscendingIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/filter/sort_ascending.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/filter/SortDecendingIcon.dart b/app/lib/view/core/icons/filter/SortDecendingIcon.dart new file mode 100644 index 00000000..a8dccda4 --- /dev/null +++ b/app/lib/view/core/icons/filter/SortDecendingIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class SortDescendingIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const SortDescendingIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/filter/sort_descending.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/selection_components/MensaDropdown.dart b/app/lib/view/core/selection_components/MensaDropdown.dart index 10e43ccd..f2b41f8f 100644 --- a/app/lib/view/core/selection_components/MensaDropdown.dart +++ b/app/lib/view/core/selection_components/MensaDropdown.dart @@ -40,6 +40,7 @@ class MensaDropdown extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 16.0), child: DropdownButtonHideUnderline( // DropdownButtonHideUnderline is used to hide the underline of the dropdown. child: DropdownButton( + dropdownColor: Theme.of(context).colorScheme.surface, elevation: 0, borderRadius: BorderRadius.circular(4.0), value: _value, diff --git a/app/lib/view/core/selection_components/MensaToggle.dart b/app/lib/view/core/selection_components/MensaToggle.dart index d75b3de9..b6d63191 100644 --- a/app/lib/view/core/selection_components/MensaToggle.dart +++ b/app/lib/view/core/selection_components/MensaToggle.dart @@ -23,20 +23,17 @@ class MensaToggle extends StatelessWidget { /// @returns The widget. @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8), - child: Row( - children: [ - Switch(value: _value, onChanged: _onChanged), - const SizedBox(width: 8), - GestureDetector( - onTap: () { - _onChanged!(!_value); - }, - child: - Text(_label, style: Theme.of(context).textTheme.labelLarge), - ), - ], - )); + return Row( + children: [ + GestureDetector( + onTap: () { + _onChanged!(!_value); + }, + child: Text(_label, + style: TextStyle(fontSize: 16))), + Spacer(), + Switch(value: _value, onChanged: _onChanged), + ], + ); } } diff --git a/app/lib/view/filter/FilterDialog.dart b/app/lib/view/filter/FilterDialog.dart index 9fb80490..2d99e975 100644 --- a/app/lib/view/filter/FilterDialog.dart +++ b/app/lib/view/filter/FilterDialog.dart @@ -1,16 +1,19 @@ import 'package:app/view/core/MensaAppBar.dart'; -import 'package:app/view/core/buttons/MensaButton.dart'; import 'package:app/view/core/buttons/MensaCtaButton.dart'; import 'package:app/view/core/buttons/MensaIconButton.dart'; import 'package:app/view/core/dialogs/MensaFullscreenDialog.dart'; import 'package:app/view/core/icons/allergens/AllergenIcon.dart'; +import 'package:app/view/core/icons/filter/FilterRestoreIcon.dart'; import 'package:app/view/core/icons/navigation/NavigationBackIcon.dart'; +import 'package:app/view/core/selection_components/MensaCheckbox.dart'; import 'package:app/view/core/selection_components/MensaSlider.dart'; import 'package:app/view/core/selection_components/MensaToggle.dart'; import 'package:app/view/filter/MensaButtonGroup.dart'; import 'package:app/view/filter/MensaButtonGroupEntry.dart'; import 'package:app/view/filter/MensaFilterIconCheckbox.dart'; import 'package:app/view/filter/MensaFilterIconCheckboxGroup.dart'; +import 'package:app/view/filter/MensaSortSelect.dart'; +import 'package:app/view/filter/MensaSortSelectEntry.dart'; import 'package:app/view_model/logic/meal/IMealAccess.dart'; import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; @@ -28,26 +31,40 @@ class FilterDialog extends StatefulWidget { class _FilterDialogState extends State { FilterPreferences _preferences = FilterPreferences(); + String selectedSorting = "line"; + SortDirection sortDirection = SortDirection.ascending; + + final TextStyle _headingTextStyle = + const TextStyle(fontSize: 16, fontWeight: FontWeight.bold); @override Widget build(BuildContext context) { return MensaFullscreenDialog( appBar: MensaAppBar( - appBarHeight: kToolbarHeight * 1.25, - child: Row( - children: [ - MensaIconButton( - onPressed: () => Navigator.of(context).pop(), - icon: NavigationBackIcon()), - Text( - FlutterI18n.translate(context, "filter.filterTitle"), - style: - const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), - const Spacer(), - // todo temporary disable or enable filter - ], - )), + appBarHeight: kToolbarHeight, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + children: [ + MensaIconButton( + onPressed: () => Navigator.of(context).pop(), + icon: const NavigationBackIcon()), + Text( + FlutterI18n.translate(context, "filter.filterTitle"), + style: const TextStyle( + fontSize: 20, fontWeight: FontWeight.bold), + ), + const Spacer(), + MensaIconButton( + onPressed: () { + context.read().resetFilterPreferences(); + setState(() { + _preferences = FilterPreferences(); + }); + }, + icon: const FilterRestoreIcon()), + ], + ))), content: Consumer( builder: (context, mealAccess, child) => FutureBuilder( future: mealAccess.getFilterPreferences(), @@ -60,98 +77,259 @@ class _FilterDialogState extends State { } _preferences = filterPreferences.requireData; return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(FlutterI18n.translate(context, "filter.foodType")), - MensaButtonGroup( - value: _getValueCategory(_preferences.categories), - onChanged: (value) { - _setValueCategory(value, _preferences); - setState(() { - _preferences = _preferences; - }); - }, - entries: _getAllFoodTypeEntries(context)), - // todo checkboxes - Text(FlutterI18n.translate(context, "filter.allergensTitle")), - MensaFilterIconCheckboxGroup( - items: _getAllAllergen(context), - selectedValues: _preferences.allergens, - onChanged: (value) { - _preferences.allergens = value; - setState(() { - _preferences = _preferences; - }); - }), - Text(FlutterI18n.translate(context, "filter.priceTitle")), - MensaSlider( - onChanged: (value) { - _preferences.price = value.round(); - setState(() { - _preferences = _preferences; - }); - }, - value: _preferences.price.toDouble(), - min: 0, - max: 1000), - MensaSlider( - onChanged: (value) { - _preferences.rating = value.round(); - setState(() { - _preferences = _preferences; - }); - }, - value: _preferences.rating.toDouble(), - min: 1, - max: 5, - ), - MensaToggle( - onChanged: (value) { - _preferences.onlyFavorite = value; - setState(() { - _preferences = _preferences; - }); - }, - value: _preferences.onlyFavorite, - label: FlutterI18n.translate( - context, "filter.favoritesOnlyTitle")), - Text(FlutterI18n.translate(context, "filter.frequencyTitle")), - MensaButtonGroup( - value: _getValueFrequency(_preferences.frequency), - onChanged: (value) { - _setValueFrequency(value, _preferences); - setState(() { - _preferences = _preferences; - }); - }, - entries: _getAllFrequencyEntries(context)), - Text(FlutterI18n.translate(context, "filter.sortByTitle")), - Row( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - // todo SortDropdown in Expanded - // todo Icon for ascending / descending + Text(FlutterI18n.translate(context, "filter.foodType"), + style: _headingTextStyle), + const SizedBox( + height: 8, + ), + MensaButtonGroup( + value: _getValueCategory(_preferences.categories), + onChanged: (value) { + _setValueCategory(value, _preferences); + setState(() { + _preferences = _preferences; + }); + }, + entries: _getAllFoodTypeEntries(context)), + _getValueCategory(_preferences.categories) == 0 + ? Column( + children: [ + const SizedBox( + height: 8, + ), + MensaCheckbox( + label: FlutterI18n.translate( + context, "filter.foodTypeSelectionBeef"), + value: _preferences.categories + .contains(FoodType.beef) || + _preferences.categories + .contains(FoodType.beefAw), + onChanged: (value) { + if (value) { + _preferences.categories + .add(FoodType.beef); + _preferences.categories + .add(FoodType.beefAw); + } else { + _preferences.categories + .remove(FoodType.beef); + _preferences.categories + .remove(FoodType.beefAw); + } + setState(() { + _preferences = _preferences; + }); + }, + ), + MensaCheckbox( + label: FlutterI18n.translate( + context, "filter.foodTypeSelectionPork"), + value: _preferences.categories + .contains(FoodType.pork) || + _preferences.categories + .contains(FoodType.porkAw), + onChanged: (value) { + if (value) { + _preferences.categories + .add(FoodType.pork); + _preferences.categories + .add(FoodType.porkAw); + } else { + _preferences.categories + .remove(FoodType.pork); + _preferences.categories + .remove(FoodType.porkAw); + } + setState(() { + _preferences = _preferences; + }); + }, + ), + MensaCheckbox( + label: FlutterI18n.translate( + context, "filter.foodTypeSelectionFish"), + value: _preferences.categories + .contains(FoodType.fish), + onChanged: (value) { + if (value) { + _preferences.categories + .add(FoodType.fish); + } else { + _preferences.categories + .remove(FoodType.fish); + } + setState(() { + _preferences = _preferences; + }); + }, + ) + ], + ) + : Container(), + const SizedBox( + height: 16, + ), + Text( + FlutterI18n.translate(context, "filter.allergensTitle"), + style: _headingTextStyle, + ), + const SizedBox( + height: 8, + ), + MensaFilterIconCheckboxGroup( + items: _getAllAllergen(context), + selectedValues: _preferences.allergens, + onChanged: (value) { + _preferences.allergens = value; + setState(() { + _preferences = _preferences; + }); + }), + const SizedBox( + height: 16, + ), + Text( + FlutterI18n.translate(context, "filter.priceTitle"), + style: _headingTextStyle, + ), + const SizedBox( + height: 8, + ), + Row( + children: [ + Text(FlutterI18n.translate( + context, "filter.priceMin")), + const Spacer(), + Text(FlutterI18n.translate( + context, "filter.priceMax")), + ], + ), + MensaSlider( + onChanged: (value) { + _preferences.price = value.round(); + setState(() { + _preferences = _preferences; + }); + }, + value: _preferences.price.toDouble(), + min: 0, + max: 1000), + Text( + FlutterI18n.translate(context, "filter.titleRating"), + style: _headingTextStyle, + ), + const SizedBox( + height: 8, + ), + Row( + children: [ + Text(FlutterI18n.translate( + context, "filter.ratingMin")), + const Spacer(), + Text(FlutterI18n.translate( + context, "filter.ratingMax")), + ], + ), + MensaSlider( + onChanged: (value) { + _preferences.rating = value.round(); + setState(() { + _preferences = _preferences; + }); + }, + value: _preferences.rating.toDouble(), + min: 1, + max: 5, + ), + const SizedBox( + height: 16, + ), + MensaToggle( + onChanged: (value) { + _preferences.onlyFavorite = value; + setState(() { + _preferences = _preferences; + }); + }, + value: _preferences.onlyFavorite, + label: FlutterI18n.translate( + context, "filter.favoritesOnlyTitle")), + const SizedBox( + height: 16, + ), + Text( + FlutterI18n.translate(context, "filter.frequencyTitle"), + style: _headingTextStyle, + ), + const SizedBox( + height: 8, + ), + MensaButtonGroup( + value: _getValueFrequency(_preferences.frequency), + onChanged: (value) { + _setValueFrequency(value, _preferences); + setState(() { + _preferences = _preferences; + }); + }, + entries: _getAllFrequencyEntries(context)), + const SizedBox( + height: 16, + ), + Text( + FlutterI18n.translate(context, "filter.sortByTitle"), + style: _headingTextStyle, + ), + const SizedBox( + height: 8, + ), + MensaSortSelect( + entries: const [ + MensaSortSelectEntry(value: "line", label: "Linie"), + MensaSortSelectEntry(value: "price", label: "Preis"), + MensaSortSelectEntry( + value: "rating", label: "Bewertung"), + ], + selectedEntry: selectedSorting, + sortDirection: sortDirection, + onEntrySelected: (v) => { + setState(() { + selectedSorting = v; + }) + }, + onSortDirectionSelected: (v) => { + setState(() { + sortDirection = v; + }) + }, + ), ], - ), - ], - ), + )), ); }, ), ), - actions: Row( - children: [ - Spacer(), - MensaCtaButton( - onPressed: () { - context - .read() - .changeFilterPreferences(_preferences); - Navigator.of(context).pop(); - }, - text: FlutterI18n.translate(context, "filter.apply")), - ], - ), + actions: Padding( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + Expanded( + child: MensaCtaButton( + onPressed: () { + context + .read() + .changeFilterPreferences(_preferences); + Navigator.of(context).pop(); + }, + text: FlutterI18n.translate( + context, "filter.storeButton"))), + ], + )), ); } @@ -259,7 +437,10 @@ class _FilterDialogState extends State { } static int _getValueCategory(List types) { - if (types.contains(FoodType.beef) || types.contains(FoodType.beefAw) || types.contains(FoodType.pork) || types.contains(FoodType.porkAw)) { + if (types.contains(FoodType.beef) || + types.contains(FoodType.beefAw) || + types.contains(FoodType.pork) || + types.contains(FoodType.porkAw)) { return 0; } if (types.contains(FoodType.vegetarian)) { diff --git a/app/lib/view/filter/MensaSortSelect.dart b/app/lib/view/filter/MensaSortSelect.dart index af85806d..aa38dbe5 100644 --- a/app/lib/view/filter/MensaSortSelect.dart +++ b/app/lib/view/filter/MensaSortSelect.dart @@ -1,4 +1,6 @@ import 'package:app/view/core/buttons/MensaTapable.dart'; +import 'package:app/view/core/icons/filter/SortAscendingIcon.dart'; +import 'package:app/view/core/icons/filter/SortDecendingIcon.dart'; import 'package:app/view/core/selection_components/MensaDropdown.dart'; import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; import 'package:app/view/filter/MensaSortSelectEntry.dart'; @@ -28,7 +30,7 @@ class MensaSortSelect extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - MensaDropdown( + Expanded(child: MensaDropdown( onChanged: (v) => _onEntrySelected(v), value: _selectedEntry, items: _entries @@ -36,10 +38,16 @@ class MensaSortSelect extends StatelessWidget { value: e.value, label: e.label, )) - .toList()), + .toList())), + const SizedBox( + width: 8, + ), MensaTapable( - child: const Padding( - padding: EdgeInsets.all(8), + child: Padding( + padding: const EdgeInsets.all(12), + child: _sortDirection == SortDirection.ascending + ? const SortAscendingIcon() + : const SortDescendingIcon(), ), onTap: () => _onSortDirectionSelected( _sortDirection == SortDirection.ascending diff --git a/app/lib/view/mealplan/MealPlanView.dart b/app/lib/view/mealplan/MealPlanView.dart index 51ca4166..1ea65a0a 100644 --- a/app/lib/view/mealplan/MealPlanView.dart +++ b/app/lib/view/mealplan/MealPlanView.dart @@ -59,7 +59,7 @@ class MealPlanView extends StatelessWidget { as Result, MealPlanException>; return Scaffold( appBar: MensaAppBar( - appBarHeight: kToolbarHeight * 1.25, + appBarHeight: kToolbarHeight, bottom: MealPlanToolbar( child: Padding( padding: diff --git a/app/lib/view/mealplan/MensaCanteenSelect.dart b/app/lib/view/mealplan/MensaCanteenSelect.dart index 7f584ad0..183252de 100644 --- a/app/lib/view/mealplan/MensaCanteenSelect.dart +++ b/app/lib/view/mealplan/MensaCanteenSelect.dart @@ -34,12 +34,12 @@ class MensaCanteenSelect extends StatelessWidget { child: Text( e.name, style: const TextStyle( - fontSize: 24, fontWeight: FontWeight.bold), + fontSize: 20, fontWeight: FontWeight.bold), ), ) ])) .toList(), - icon: const NavigationArrowDownIcon(size: 40), + icon: const NavigationArrowDownIcon(size: 32), value: _selectedCanteen.id, items: _availableCanteens .map((e) => DropdownMenuItem(value: e.id, child: Center(child: Text(e.name)))) diff --git a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart index 37890312..02effe4c 100644 --- a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart +++ b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart @@ -37,7 +37,7 @@ class FilterPreferences { }) : _categories = categories ?? _getAllFoodTypes(), _allergens = allergens ?? _getAllAllergen(), - _price = price ?? 10, + _price = price ?? 1000, _rating = rating ?? 1, _frequency = frequency ?? _getAllFrequencies(), _onlyFavorite = onlyFavorite ?? false, diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 4a13b1e7..4e6bf83f 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -88,6 +88,7 @@ flutter: - assets/icons/exceptions/ - assets/icons/navigation/ - assets/icons/favorites/ + - assets/icons/filter/ - assets/icons/meal/ - assets/locales/en/ - assets/locales/de/ From 435d87b74759f4d91c26afce44cd139a845aabcf Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Fri, 28 Jul 2023 10:53:18 +0200 Subject: [PATCH 166/184] many last latenight changes, almost working full app except some lite dialog --- app/assets/locales/de/filter.json | 11 +- .../model/database/SQLiteDatabaseAccess.dart | 108 ++++++++++++++++-- app/lib/model/database/model/db_favorite.dart | 4 +- app/lib/model/database/model/db_meal.dart | 6 +- .../database/model/db_meal_additive.dart | 2 +- .../database/model/db_meal_allergen.dart | 4 +- .../model/database/model/db_meal_plan.dart | 3 +- .../database/model/db_side_additive.dart | 2 +- .../local_storage/SharedPreferenceAccess.dart | 55 +++++---- app/lib/view/core/buttons/MensaCtaButton.dart | 2 +- .../core/meal_view_format/MealGridLine.dart | 4 +- .../core/meal_view_format/MealListLine.dart | 4 +- app/lib/view/detail_view/DetailsPage.dart | 6 +- app/lib/view/detail_view/MealAccordion.dart | 16 ++- app/lib/view/favorites/Favorites.dart | 27 ++--- app/lib/view/filter/FilterDialog.dart | 23 ++-- .../view/filter/MensaFilterIconCheckbox.dart | 8 +- .../filter/MensaFilterIconCheckboxGroup.dart | 4 +- app/lib/view/filter/MensaSortSelectEntry.dart | 3 - app/lib/view/images/ImageReportDialog.dart | 4 +- app/lib/view/mealplan/MealPlanClosed.dart | 24 ++-- app/lib/view/mealplan/MealPlanDateSelect.dart | 8 +- app/lib/view/mealplan/MealPlanError.dart | 13 ++- app/lib/view/mealplan/MealPlanFilter.dart | 36 +++--- app/lib/view/mealplan/MealPlanNoData.dart | 13 ++- app/lib/view/mealplan/MealPlanView.dart | 49 ++------ app/lib/view/mealplan/MensaCanteenSelect.dart | 6 +- app/lib/view/settings/Settings.dart | 73 +++++++----- .../view/settings/SettingsDropdownEntry.dart | 3 +- app/lib/view/settings/SettingsSection.dart | 4 +- .../logic/meal/CombinedMealPlanAccess.dart | 11 +- 31 files changed, 319 insertions(+), 217 deletions(-) diff --git a/app/assets/locales/de/filter.json b/app/assets/locales/de/filter.json index 22dac74a..ba4bdfde 100644 --- a/app/assets/locales/de/filter.json +++ b/app/assets/locales/de/filter.json @@ -8,17 +8,18 @@ "foodTypeSelectionPork": "Schweinefleisch", "foodTypeSelectionBeef": "Rindfleisch", "foodTypeSelectionFish": "Fisch", - "allergensTitle": "Allergene", - "priceTitle": "Preis", - "ratingTitle": "Bewertung", + "priceTitle": "Preis (maximal)", + "ratingTitle": "Bewertung (mindestens)", "favoritesOnlyTitle": "Nur Favoriten anzeigen", "sortByTitle": "Sortiert nach", - "frequencyTitle": "Häufigkeit", "frequencySectionAll": "Alles", "frequencySectionRare": "Selten", "frequencySectionNew": "Neu", - + "priceMin": "0€", + "priceMax": "10€", + "ratingMin": "1 Stern", + "ratingMax": "5 Sterne", "storeButton": "Übernehmen" } \ No newline at end of file diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index d3c11c28..5fe9cd77 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -1,6 +1,8 @@ import 'dart:async'; +import 'dart:ffi'; import 'package:app/model/database/model/database_model.dart'; +import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; import 'package:app/view_model/repository/data_classes/meal/Additive.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; @@ -76,7 +78,9 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { @override Future addFavorite(Meal meal) async { var db = await database; + print(meal.id); var dbMeal = await _getDBMeal(meal.id); + print(dbMeal?.toMap()); var dbMealPlan = await _getDBMealPlan(dbMeal!.mealPlanID); var dbLine = await _getDBLine(dbMealPlan!.lineID); // FavoriteID is now the related mealID. Seems right. TODO: DateTime to String, if toString() isn't enough. @@ -104,7 +108,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { @override Future> getFavorites() async { var db = await database; - var meals = List.empty(); + var meals = List.empty(growable: true); var dbFavoritesListResult = await db.query(DBFavorite.tableName); for (Map favoriteMap in dbFavoritesListResult) { var dbFavorite = DBFavorite.fromMap(favoriteMap); @@ -132,27 +136,42 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { Future, NoDataException>> getMealPlan( DateTime date, Canteen canteen) async { var db = await database; - var result = await db.query('${DBMealPlan.tableName}, ${DBCanteen.tableName}, ${DBLine.tableName}', + var result = await db.query( + '${DBMealPlan.tableName}, ${DBCanteen.tableName}, ${DBLine.tableName}', where: '${DBCanteen.tableName}.${DBCanteen.columnCanteenID} = ${DBLine.tableName}.${DBLine.columnCanteenID} AND ${DBLine.tableName}.${DBLine.columnLineID} = ${DBMealPlan.tableName}.${DBMealPlan.columnLineID} AND ${DBCanteen.tableName}.${DBCanteen.columnCanteenID} = ? AND ${DBMealPlan.tableName}.${DBMealPlan.columnDate} = ?', whereArgs: [canteen.id, date.toString()]); if (result.isNotEmpty) { - var mealPlans = List.empty(); + // Maybe better solution... + /*List mealPlans = await Future.wait(result.map((plan) async { + DBMealPlan dbMealPlan= DBMealPlan.fromMap(plan); + var dbLine = await _getDBLine(dbMealPlan.lineID); + var dbCanteen = await _getDBCanteen(dbLine!.canteenID); + List meals = await Future.wait((await _getDBMeals(dbMealPlan.mealPlanID)).map>((dbMeal) async { + bool isFavorite = await _isFavorite(dbMeal.mealID); + return _getMeal(dbMeal.mealID, isFavorite); + })); + return DatabaseTransformer.fromDBMealPlan(dbMealPlan, dbLine, dbCanteen!, meals); + })); + return Success(mealPlans);*/ + int i =0; + List mealPlans = List.filled(result.length, null); for (DBMealPlan dbMealPlan in result.map((plan) => DBMealPlan.fromMap(plan))) { var dbLine = await _getDBLine(dbMealPlan.lineID); var dbCanteen = await _getDBCanteen(dbLine!.canteenID); var dbMeals = await _getDBMeals(dbMealPlan.mealPlanID); - var meals = List.empty(); + var meals = List.empty(growable: true); for (DBMeal dbMeal in dbMeals) { bool isFavorite = await _isFavorite(dbMeal.mealID); Meal meal = await _getMeal(dbMeal.mealID, isFavorite); meals.add(meal); } + i++; mealPlans.add(DatabaseTransformer.fromDBMealPlan( dbMealPlan, dbLine, dbCanteen!, meals)); } - return Success(mealPlans); + return Success(mealPlans as List); } else { return Failure(NoDataException("No meal plan found for this date.")); } @@ -193,21 +212,90 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { Future _insertLine(Line line) async { var db = await database; var dbLine = DBLine(line.id, line.canteen.id, line.name, line.position); - return db.insert(DBLine.tableName, dbLine.toMap(), conflictAlgorithm: ConflictAlgorithm.replace); + return db.insert(DBLine.tableName, dbLine.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace); } Future _insertCanteen(Canteen canteen) async { var db = await database; var dbCanteen = DBCanteen(canteen.id, canteen.name); - return db.insert(DBCanteen.tableName, dbCanteen.toMap(), conflictAlgorithm: ConflictAlgorithm.replace); + return db.insert(DBCanteen.tableName, dbCanteen.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace); } Future _insertMealPlan(MealPlan mealPlan) async { var db = await database; - var uuid = const Uuid(); // TODO: generate UUID or get uuid from backend - var dbMealPlan = DBMealPlan(uuid.toString(), mealPlan.line.id, + var result = await db.query(DBMealPlan.tableName, + where: + '${DBMealPlan.columnLineID} = ? AND ${DBMealPlan.columnDate} = ?', + whereArgs: [mealPlan.line.id, mealPlan.date.toString()]); + if (result.isNotEmpty) return 0; + /*var result = await db.query(DBMealPlan.tableName, + where: + '${DBMealPlan.columnLineID} = ? AND ${DBMealPlan.columnDate} = ?', + whereArgs: [mealPlan.line.id, mealPlan.date.toString()]); + DBMealPlan? dbMealPlan; + if (result.isNotEmpty) { + DBMealPlan oldDbMealPlan = DBMealPlan.fromMap(result.first); + dbMealPlan = DBMealPlan(oldDbMealPlan.mealPlanID, mealPlan.line.id, + mealPlan.date.toString(), mealPlan.isClosed); + } else { + var uuid = const Uuid(); + dbMealPlan = DBMealPlan(uuid.v4(), mealPlan.line.id, + mealPlan.date.toString(), mealPlan.isClosed); + } + int id = await db.insert(DBMealPlan.tableName, dbMealPlan.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace);*/ + /*for (Meal meal in mealPlan.meals) { + print(await _insertMeal(meal, dbMealPlan)); + }*/ + var uuid = const Uuid(); + DBMealPlan dbMealPlan = DBMealPlan(uuid.v4(), mealPlan.line.id, mealPlan.date.toString(), mealPlan.isClosed); - return db.insert(DBMealPlan.tableName, dbMealPlan.toMap(), conflictAlgorithm: ConflictAlgorithm.replace); + int id = await db.insert(DBMealPlan.tableName, dbMealPlan.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace); + return id; + } + + Future _insertMeal(Meal meal, DBMealPlan mealPlan) async { + var db = await database; + var dbMeal = DBMeal( + meal.id, + mealPlan.mealPlanID, + meal.name, + meal.foodType, + meal.price.student, + meal.price.employee, + meal.price.pupil, + meal.price.guest, + meal.individualRating ?? 0, + meal.numberOfRatings ?? 0, + meal.averageRating ?? 0, + meal.lastServed.toString(), + meal.nextServed.toString(), + meal.relativeFrequency ?? Frequency.normal); + for (Allergen allergen in meal.allergens ?? []) { + await _insertMealAllergen(allergen, dbMeal); + } + for (Additive additive in meal.additives ?? []) { + await _insertMealAdditive(additive, dbMeal); + } + return db.insert(DBMeal.tableName, dbMeal.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace); + } + + Future _insertMealAllergen(Allergen allergen, DBMeal meal) async { + var db = await database; + var dbMealAllergen = DBMealAllergen(meal.mealID, allergen); + return db.insert(DBMealAllergen.tableName, dbMealAllergen.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace); + } + + Future _insertMealAdditive(Additive additive, DBMeal meal) async { + var db = await database; + var dbMealAdditive = DBMealAdditive(meal.mealID, additive); + return db.insert(DBMealAdditive.tableName, dbMealAdditive.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace); } Future _getDBCanteen(String canteenID) async { diff --git a/app/lib/model/database/model/db_favorite.dart b/app/lib/model/database/model/db_favorite.dart index 548e3b9d..0def841a 100644 --- a/app/lib/model/database/model/db_favorite.dart +++ b/app/lib/model/database/model/db_favorite.dart @@ -33,7 +33,7 @@ class DBFavorite implements DatabaseModel { columnFavoriteID: _favoriteID, columnLineID: _lineID, columnLastDate: _lastDate, - columnFoodType: _foodType, + columnFoodType: _foodType.name, columnPriceStudent: _priceStudent, columnPriceEmployee: _priceEmployee, columnPricePupil: _pricePupil, @@ -42,7 +42,7 @@ class DBFavorite implements DatabaseModel { } static DBFavorite fromMap(Map map) { - return DBFavorite(map[columnFavoriteID], map[columnLineID], map[columnLastDate], map[columnFoodType], map[columnPriceStudent], map[columnPriceEmployee], map[columnPricePupil], map[columnPriceGuest]); + return DBFavorite(map[columnFavoriteID], map[columnLineID], map[columnLastDate], FoodType.values.byName(map[columnFoodType]), map[columnPriceStudent], map[columnPriceEmployee], map[columnPricePupil], map[columnPriceGuest]); } /// The string to create a table for a favorite. diff --git a/app/lib/model/database/model/db_meal.dart b/app/lib/model/database/model/db_meal.dart index 9443c482..65b13564 100644 --- a/app/lib/model/database/model/db_meal.dart +++ b/app/lib/model/database/model/db_meal.dart @@ -46,7 +46,7 @@ class DBMeal implements DatabaseModel { columnMealID: _mealID, columnMealPlanID: _mealPlanID, columnName: _name, - columnFoodType: _foodType, + columnFoodType: _foodType.name, columnPriceStudent: _priceStudent, columnPriceEmployee: _priceEmployee, columnPricePupil: _pricePupil, @@ -56,12 +56,12 @@ class DBMeal implements DatabaseModel { columnAverageRating: _averageRating, columnLastServed: _lastServed, columnNextServed: _nextServed, - columnRelativeFrequency: _relativeFrequency + columnRelativeFrequency: _relativeFrequency.name }; } static DBMeal fromMap(Map map) { - return DBMeal(map[columnMealID], map[columnMealPlanID], map[columnName], map[columnFoodType], map[columnPriceStudent], map[columnPriceEmployee], map[columnPricePupil], map[columnPriceGuest], map[columnIndividualRating], map[columnNumberOfRatings], map[columnAverageRating], map[columnLastServed], map[columnNextServed], map[columnRelativeFrequency]); + return DBMeal(map[columnMealID], map[columnMealPlanID], map[columnName], FoodType.values.byName(map[columnFoodType]), map[columnPriceStudent], map[columnPriceEmployee], map[columnPricePupil], map[columnPriceGuest], map[columnIndividualRating], map[columnNumberOfRatings], map[columnAverageRating], map[columnLastServed], map[columnNextServed], Frequency.values.byName(map[columnRelativeFrequency])); } static String initTable() { diff --git a/app/lib/model/database/model/db_meal_additive.dart b/app/lib/model/database/model/db_meal_additive.dart index 7a5862f1..5455fa66 100644 --- a/app/lib/model/database/model/db_meal_additive.dart +++ b/app/lib/model/database/model/db_meal_additive.dart @@ -19,7 +19,7 @@ class DBMealAdditive implements DatabaseModel { Map toMap() { return { columnMealID: _mealID, - columnAdditive: _additive + columnAdditive: _additive.name }; } diff --git a/app/lib/model/database/model/db_meal_allergen.dart b/app/lib/model/database/model/db_meal_allergen.dart index 5797d22e..816a643c 100644 --- a/app/lib/model/database/model/db_meal_allergen.dart +++ b/app/lib/model/database/model/db_meal_allergen.dart @@ -19,12 +19,12 @@ class DBMealAllergen implements DatabaseModel { Map toMap() { return { columnMealID: _mealID, - columnAllergen: _allergen + columnAllergen: _allergen.name }; } static DBMealAllergen fromMap(Map map) { - return DBMealAllergen(map[columnMealID], map[columnAllergen]); + return DBMealAllergen(map[columnMealID], Allergen.values.byName(map[columnAllergen])); } /// The string to create a table for an allergen of a meal. diff --git a/app/lib/model/database/model/db_meal_plan.dart b/app/lib/model/database/model/db_meal_plan.dart index 82d7ee91..a0d11069 100644 --- a/app/lib/model/database/model/db_meal_plan.dart +++ b/app/lib/model/database/model/db_meal_plan.dart @@ -28,7 +28,8 @@ class DBMealPlan implements DatabaseModel { } static DBMealPlan fromMap(Map map) { - return DBMealPlan(map[columnMealPlanID], map[columnLineID], map[columnDate], map[columnIsClosed]); + print(map[columnIsClosed]); + return DBMealPlan(map[columnMealPlanID], map[columnLineID], map[columnDate], map[columnIsClosed] == 1); } /// The string to create a table for a meal plan. diff --git a/app/lib/model/database/model/db_side_additive.dart b/app/lib/model/database/model/db_side_additive.dart index 0a069e98..02f044cc 100644 --- a/app/lib/model/database/model/db_side_additive.dart +++ b/app/lib/model/database/model/db_side_additive.dart @@ -24,7 +24,7 @@ class DBSideAdditive implements DatabaseModel { } static DBSideAdditive fromMap(Map map) { - return DBSideAdditive(map[columnSideID], map[columnAdditive]); + return DBSideAdditive(map[columnSideID], Additive.values.byName(map[columnAdditive])); } /// The string to create a table for an additive of a side. diff --git a/app/lib/model/local_storage/SharedPreferenceAccess.dart b/app/lib/model/local_storage/SharedPreferenceAccess.dart index fd95eb0f..02e19a0c 100644 --- a/app/lib/model/local_storage/SharedPreferenceAccess.dart +++ b/app/lib/model/local_storage/SharedPreferenceAccess.dart @@ -1,15 +1,12 @@ - import 'package:app/view_model/repository/data_classes/filter/FilterPreferences.dart'; import 'package:app/view_model/repository/data_classes/filter/Frequency.dart'; import 'package:app/view_model/repository/data_classes/meal/Allergen.dart'; -import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/MealPlanFormat.dart'; +import 'package:app/view_model/repository/data_classes/settings/MensaColorScheme.dart'; import 'package:app/view_model/repository/data_classes/settings/PriceCategory.dart'; import 'package:app/view_model/repository/interface/ILocalStorage.dart'; - import 'package:shared_preferences/shared_preferences.dart'; import 'package:uuid/uuid.dart'; -import 'package:uuid/uuid_util.dart'; import '../../view_model/repository/data_classes/filter/Sorting.dart'; import '../../view_model/repository/data_classes/meal/FoodType.dart'; @@ -37,7 +34,9 @@ class SharedPreferenceAccess implements ILocalStorage { @override MensaColorScheme? getColorScheme() { final colorScheme = _pref.getString('colorScheme'); - return MensaColorScheme.values.firstWhere((e) => e.toString() == colorScheme, orElse: () => MensaColorScheme.system); + return MensaColorScheme.values.firstWhere( + (e) => e.toString() == colorScheme, + orElse: () => MensaColorScheme.system); } @override @@ -55,47 +54,52 @@ class SharedPreferenceAccess implements ILocalStorage { // convert values to right format List? foodTypeEnum; if (categories != null) { - foodTypeEnum = List.of(categories.map((e) => FoodType.values.firstWhere((element) => element.toString() == e))); + foodTypeEnum = List.of(categories.map((e) => + FoodType.values.firstWhere((element) => element.toString() == e))); } List? allergensEnum; if (allergens != null) { - allergensEnum = List.of(allergens.map((e) => Allergen.values.firstWhere((element) => element.toString() == e))); + allergensEnum = List.of(allergens.map((e) => + Allergen.values.firstWhere((element) => element.toString() == e))); } List? frequencyEnum; if (frequency != null) { - frequencyEnum = List.of(frequency.map((e) => Frequency.values.firstWhere((element) => element.toString() == e))); + frequencyEnum = List.of(frequency.map((e) => + Frequency.values.firstWhere((element) => element.toString() == e))); } Sorting? sortedByEnum; if (sortedBy != null) { - sortedByEnum = Sorting.values.firstWhere((e) => e.toString() == sortedBy); + sortedByEnum = Sorting.values.firstWhere((e) => e.toString() == sortedBy); } // return filter preferences return FilterPreferences( - categories: foodTypeEnum, - allergens: allergensEnum, - price: price, - rating: rating, - frequency: frequencyEnum, - onlyFavorite: onlyFavorites, - sortedBy: sortedByEnum, - ascending: ascending - ); + categories: foodTypeEnum, + allergens: allergensEnum, + price: price, + rating: rating, + frequency: frequencyEnum, + onlyFavorite: onlyFavorites, + sortedBy: sortedByEnum, + ascending: ascending); } @override MealPlanFormat? getMealPlanFormat() { final mealPlanFormat = _pref.getString('mealPlanFormat'); - return MealPlanFormat.values.firstWhere((e) => e.toString() == mealPlanFormat, orElse: () => MealPlanFormat.grid); + return MealPlanFormat.values.firstWhere( + (e) => e.toString() == mealPlanFormat, + orElse: () => MealPlanFormat.grid); } @override PriceCategory? getPriceCategory() { final String? priceCategory = _pref.getString('priceCategory'); - return PriceCategory.values.firstWhere((e) => e.toString() == priceCategory, orElse: () => PriceCategory.student); + return PriceCategory.values.firstWhere((e) => e.toString() == priceCategory, + orElse: () => PriceCategory.student); } @override @@ -110,11 +114,14 @@ class SharedPreferenceAccess implements ILocalStorage { @override Future setFilterPreferences(FilterPreferences filter) async { - await _pref.setStringList('filterCategories', List.of(filter.categories.map((e) => e.toString()))); - await _pref.setStringList('filterAllergens', List.of(filter.allergens.map((e) => e.toString()))); + await _pref.setStringList('filterCategories', + List.of(filter.categories.map((e) => e.toString()))); + await _pref.setStringList( + 'filterAllergens', List.of(filter.allergens.map((e) => e.toString()))); await _pref.setInt('filterPrice', filter.price); await _pref.setInt('filterRating', filter.rating); - await _pref.setStringList('filterFrequency', List.of(filter.frequency.map((e) => e.toString()))); + await _pref.setStringList( + 'filterFrequency', List.of(filter.frequency.map((e) => e.toString()))); await _pref.setBool('filterFavorite', filter.onlyFavorite); await _pref.setString('filterSort', filter.sortedBy.toString()); await _pref.setBool('filterSortAscending', filter.ascending); @@ -140,4 +147,4 @@ class SharedPreferenceAccess implements ILocalStorage { Future setCanteen(String canteen) async { await _pref.setString('canteen', canteen); } -} \ No newline at end of file +} diff --git a/app/lib/view/core/buttons/MensaCtaButton.dart b/app/lib/view/core/buttons/MensaCtaButton.dart index 1104f6a1..bafd2fcf 100644 --- a/app/lib/view/core/buttons/MensaCtaButton.dart +++ b/app/lib/view/core/buttons/MensaCtaButton.dart @@ -29,7 +29,7 @@ class MensaCtaButton extends StatelessWidget { highlightElevation: 0, onPressed: _onPressed, onLongPress: _onLongPressed, - child: Text(_text), + child: Text(_text, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), )); } } diff --git a/app/lib/view/core/meal_view_format/MealGridLine.dart b/app/lib/view/core/meal_view_format/MealGridLine.dart index 1f76e11e..d1463ed9 100644 --- a/app/lib/view/core/meal_view_format/MealGridLine.dart +++ b/app/lib/view/core/meal_view_format/MealGridLine.dart @@ -17,10 +17,10 @@ class MealGridLine extends StatelessWidget { Widget build(BuildContext context) { return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), child: Text(_mealPlan.line.name, style: const TextStyle( - fontWeight: FontWeight.bold, fontSize: 20, height: 1.5)), + fontWeight: FontWeight.bold, fontSize: 16)), ), LayoutBuilder( builder: (context, constraints) => SingleChildScrollView( diff --git a/app/lib/view/core/meal_view_format/MealListLine.dart b/app/lib/view/core/meal_view_format/MealListLine.dart index c2761b7e..6fc41503 100644 --- a/app/lib/view/core/meal_view_format/MealListLine.dart +++ b/app/lib/view/core/meal_view_format/MealListLine.dart @@ -21,8 +21,8 @@ class MealListLine extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), - child: Text(_mealPlan.line.name, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 20, height: 1.5)), + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), + child: Text(_mealPlan.line.name, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), ), ListView.builder( physics: const NeverScrollableScrollPhysics(), diff --git a/app/lib/view/detail_view/DetailsPage.dart b/app/lib/view/detail_view/DetailsPage.dart index d3f90676..0443c1f3 100644 --- a/app/lib/view/detail_view/DetailsPage.dart +++ b/app/lib/view/detail_view/DetailsPage.dart @@ -1,6 +1,5 @@ import 'package:app/view/core/MensaAppBar.dart'; import 'package:app/view/core/buttons/MensaButton.dart'; -import 'package:app/view/core/buttons/MensaCtaButton.dart'; import 'package:app/view/core/buttons/MensaIconButton.dart'; import 'package:app/view/core/icons/favorites/FavoriteFilledIcon.dart'; import 'package:app/view/core/icons/favorites/FavoriteOutlinedIcon.dart'; @@ -17,7 +16,6 @@ import 'package:app/view/detail_view/MealRatingDialog.dart'; import 'package:app/view/detail_view/RatingsOverview.dart'; import 'package:app/view/detail_view/UploadImageDialog.dart'; import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; -import 'package:app/view_model/logic/meal/IMealAccess.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; import 'package:flutter/material.dart'; @@ -158,8 +156,8 @@ class DetailsPageState extends State { : themeData.colorScheme.background, sideEntry: MealSideEntry(side: e), info: MealAccordionInfo( - additives: e.additives ?? [], - allergens: e.allergens ?? []), + additives: e.additives, + allergens: e.allergens), isExpanded: expandedAccordionIndex == widget._meal.sides!.indexOf(e) + 1, onTap: () => setState(() => diff --git a/app/lib/view/detail_view/MealAccordion.dart b/app/lib/view/detail_view/MealAccordion.dart index a66996c5..da24c4ae 100644 --- a/app/lib/view/detail_view/MealAccordion.dart +++ b/app/lib/view/detail_view/MealAccordion.dart @@ -61,11 +61,17 @@ class MealAccordion extends StatelessWidget { child: Column( children: [ _mainEntry ?? _sideEntry ?? Container(), - _isExpanded - ? Padding( - padding: const EdgeInsets.only(left: 40), - child: _info) - : Container(), + Row( + children: [ + Expanded( + child: _isExpanded + ? Padding( + padding: const EdgeInsets.only(left: 40), + child: _info) + : Container()), + ], + ), + _isExpanded ? const SizedBox(height: 4) : Container(), ], ))), )); diff --git a/app/lib/view/favorites/Favorites.dart b/app/lib/view/favorites/Favorites.dart index d7b77b1c..605831f9 100644 --- a/app/lib/view/favorites/Favorites.dart +++ b/app/lib/view/favorites/Favorites.dart @@ -11,18 +11,17 @@ class Favorites extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer( - builder: (context, favoriteAccess, child) => Padding( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), - child: FutureBuilder( + builder: (context, favoriteAccess, child) => FutureBuilder( future: Future.wait([favoriteAccess.getFavoriteMeals()]), builder: (context, snapshot) { if (!snapshot.hasData || snapshot.hasError) { + print(snapshot.error); return Scaffold( appBar: MensaAppBar( - appBarHeight: kToolbarHeight * 1.25, - child: Text(FlutterI18n.translate( - context, "common.favorites")), - ), + appBarHeight: kToolbarHeight, + child: Center(child: Text(FlutterI18n.translate( + context, "common.favorites"), style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), + )), body: const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [], @@ -31,12 +30,12 @@ class Favorites extends StatelessWidget { final mealPlan = snapshot.requireData[0]; MensaAppBar appBar = MensaAppBar( - appBarHeight: kToolbarHeight * 1.25, - child: Text( + appBarHeight: kToolbarHeight, + child: Center(child: Text( FlutterI18n.translate(context, "common.favorites"), style: const TextStyle( - fontSize: 24, fontWeight: FontWeight.bold), - ), + fontSize: 20, fontWeight: FontWeight.bold), + )), ); if (mealPlan.isEmpty) { @@ -45,13 +44,11 @@ class Favorites extends StatelessWidget { body: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, children: [ Text( FlutterI18n.translate( context, "common.noFavorites"), - style: DefaultTextStyle.of(context) - .style - .apply(fontSizeFactor: 1.5), textAlign: TextAlign.center, ) ], @@ -75,6 +72,6 @@ class Favorites extends StatelessWidget { )); }, ), - )); + ); } } diff --git a/app/lib/view/filter/FilterDialog.dart b/app/lib/view/filter/FilterDialog.dart index 2d99e975..d7818257 100644 --- a/app/lib/view/filter/FilterDialog.dart +++ b/app/lib/view/filter/FilterDialog.dart @@ -181,15 +181,18 @@ class _FilterDialogState extends State { const SizedBox( height: 8, ), - MensaFilterIconCheckboxGroup( - items: _getAllAllergen(context), - selectedValues: _preferences.allergens, - onChanged: (value) { - _preferences.allergens = value; - setState(() { - _preferences = _preferences; - }); - }), + Row(children: [ + Expanded( + child: MensaFilterIconCheckboxGroup( + items: _getAllAllergen(context), + selectedValues: _preferences.allergens, + onChanged: (value) { + _preferences.allergens = value; + setState(() { + _preferences = _preferences; + }); + })) + ]), const SizedBox( height: 16, ), @@ -220,7 +223,7 @@ class _FilterDialogState extends State { min: 0, max: 1000), Text( - FlutterI18n.translate(context, "filter.titleRating"), + FlutterI18n.translate(context, "filter.ratingTitle"), style: _headingTextStyle, ), const SizedBox( diff --git a/app/lib/view/filter/MensaFilterIconCheckbox.dart b/app/lib/view/filter/MensaFilterIconCheckbox.dart index 4bfc2be1..77613f77 100644 --- a/app/lib/view/filter/MensaFilterIconCheckbox.dart +++ b/app/lib/view/filter/MensaFilterIconCheckbox.dart @@ -40,19 +40,19 @@ class MensaFilterIconCheckbox { mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ - SizedBox( + const SizedBox( height: 8, ), _icon, - SizedBox(height: 8), + const SizedBox(height: 8), Row(children: [ Expanded( child: Padding( - padding: EdgeInsets.all(1), + padding: const EdgeInsets.all(1), child: Text( _text, textAlign: TextAlign.center, - style: TextStyle(fontSize: 9), + style: const TextStyle(fontSize: 8), ))) ]), ]), diff --git a/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart b/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart index 1aa86a16..7c097b88 100644 --- a/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart +++ b/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart @@ -23,10 +23,10 @@ class MensaFilterIconCheckboxGroup extends StatelessWidget { @override Widget build(BuildContext context) { + double width = MediaQuery.of(context).size.width; return Wrap( runAlignment: WrapAlignment.center, - //alignment: WrapAlignment.spaceEvenly, - spacing: 8, + spacing: ((width - ((width % 80) * 80)) / (width % 80 - 1) + 1).floorToDouble(), runSpacing: 8, children: _items .map((e) => e.build(context, _selectedValues.contains(e.value), () { diff --git a/app/lib/view/filter/MensaSortSelectEntry.dart b/app/lib/view/filter/MensaSortSelectEntry.dart index e5ed42c5..66847690 100644 --- a/app/lib/view/filter/MensaSortSelectEntry.dart +++ b/app/lib/view/filter/MensaSortSelectEntry.dart @@ -1,6 +1,3 @@ -import 'package:app/view/core/selection_components/MensaDropdown.dart'; -import 'package:flutter/cupertino.dart'; - class MensaSortSelectEntry { final T value; final String label; diff --git a/app/lib/view/images/ImageReportDialog.dart b/app/lib/view/images/ImageReportDialog.dart index dd8918be..04bb8419 100644 --- a/app/lib/view/images/ImageReportDialog.dart +++ b/app/lib/view/images/ImageReportDialog.dart @@ -9,9 +9,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; +// TODO statefull machen class ImageReportDialog extends StatelessWidget { ReportCategory _reason = ReportCategory.other; - late ImageData _image; + late final ImageData _image; ImageReportDialog({super.key, required ImageData image}) : _image = image; @@ -45,6 +46,7 @@ class ImageReportDialog extends StatelessWidget { onPressed: () async { final temporalMessage = await imageAccess.reportImage(_image, _reason); + if (!context.mounted) return; Navigator.pop(context); if (temporalMessage.isNotEmpty) { diff --git a/app/lib/view/mealplan/MealPlanClosed.dart b/app/lib/view/mealplan/MealPlanClosed.dart index bc941620..825f5b53 100644 --- a/app/lib/view/mealplan/MealPlanClosed.dart +++ b/app/lib/view/mealplan/MealPlanClosed.dart @@ -10,15 +10,19 @@ class MealPlanClosed extends StatelessWidget { @override Widget build(BuildContext context) { - return Column(crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const CanteenClosedExceptionIcon(size: 48), - Text(FlutterI18n.translate( - context, "mealplanException.closedCanteenException"), - style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), - textAlign: TextAlign.center, - ), - ]); + return Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const CanteenClosedExceptionIcon(size: 48), + const SizedBox(height: 16), + Text( + FlutterI18n.translate( + context, "mealplanException.closedCanteenException"), + style: const TextStyle(fontSize: 16), + textAlign: TextAlign.center, + ), + ])); } } diff --git a/app/lib/view/mealplan/MealPlanDateSelect.dart b/app/lib/view/mealplan/MealPlanDateSelect.dart index 4fbe9d4f..205f2134 100644 --- a/app/lib/view/mealplan/MealPlanDateSelect.dart +++ b/app/lib/view/mealplan/MealPlanDateSelect.dart @@ -2,14 +2,13 @@ import 'package:app/view/core/buttons/MensaTapable.dart'; import 'package:app/view/core/icons/navigation/NavigationArrowLeftIcon.dart'; import 'package:app/view/core/icons/navigation/NavigationArrowRightIcon.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:intl/intl.dart'; class MealPlanDateSelect extends StatelessWidget { final DateTime _date; final Function(DateTime) _onDateChanged; - final DateFormat _dateFormat = DateFormat('dd.MM.yyyy'); - MealPlanDateSelect( {super.key, required DateTime date, @@ -19,6 +18,7 @@ class MealPlanDateSelect extends StatelessWidget { @override Widget build(BuildContext context) { + DateFormat dateFormat = DateFormat('E dd.MM.yyyy', FlutterI18n.currentLocale(context)?.languageCode); return Row(children: [ MensaTapable( child: const Padding( @@ -32,7 +32,7 @@ class MealPlanDateSelect extends StatelessWidget { child: Padding( padding: const EdgeInsets.all(10), child: Text( - _dateFormat.format(_date), + dateFormat.format(_date), style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold), ), ), @@ -43,7 +43,7 @@ class MealPlanDateSelect extends StatelessWidget { firstDate: DateTime(1923), lastDate: DateTime.now().add(const Duration(days: 365 * 10)), initialEntryMode: DatePickerEntryMode.calendarOnly, - ).then((value) => _onDateChanged(value!)) + ).then((value) => _onDateChanged(value ?? DateTime.now())) }), MensaTapable( child: const Padding( diff --git a/app/lib/view/mealplan/MealPlanError.dart b/app/lib/view/mealplan/MealPlanError.dart index dcd95aa4..5adc42a3 100644 --- a/app/lib/view/mealplan/MealPlanError.dart +++ b/app/lib/view/mealplan/MealPlanError.dart @@ -17,31 +17,34 @@ class MealPlanError extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const ErrorExceptionIcon(size: 48), + const SizedBox(height: 16), Text( FlutterI18n.translate( context, "mealplanException.noConnectionException"), - style: DefaultTextStyle.of(context) - .style - .apply(fontSizeFactor: 1.5), + style: const TextStyle(fontSize: 16), textAlign: TextAlign.center, ), + const SizedBox(height: 16), MensaButton( onPressed: () async { // Mach das einfach als lokale Variable final temporalMessage = await mealAccess.refreshMealplan() ?? ""; + if (!context.mounted) return; if (temporalMessage.isNotEmpty) { final snackBar = SnackBar( content: Text(FlutterI18n.translate( context, temporalMessage)), - backgroundColor: Theme.of(context).colorScheme.onError, + backgroundColor: + Theme.of(context).colorScheme.onError, ); ScaffoldMessenger.of(context) .showSnackBar(snackBar); } }, - text: FlutterI18n.translate(context, "mealplanException.noConnectionButton")), + text: FlutterI18n.translate( + context, "mealplanException.noConnectionButton")), ]), )); } diff --git a/app/lib/view/mealplan/MealPlanFilter.dart b/app/lib/view/mealplan/MealPlanFilter.dart index f3fc9068..dc5c1c1e 100644 --- a/app/lib/view/mealplan/MealPlanFilter.dart +++ b/app/lib/view/mealplan/MealPlanFilter.dart @@ -14,20 +14,24 @@ class MealPlanFilter extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer( - builder: (context, mealAccess, child) => Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const ErrorExceptionIcon(size: 48), - Text(FlutterI18n.translate( - context, "mealplanException.filterException"), - style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), - textAlign: TextAlign.center, - ), - MensaButton( - onPressed: () => mealAccess.deactivateFilter(), - text: FlutterI18n.translate(context, "mealplanException.filterButton")), - ])); + builder: (context, mealAccess, child) => Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const ErrorExceptionIcon(size: 48), + const SizedBox(height: 16), + Text( + FlutterI18n.translate( + context, "mealplanException.filterException"), + style: const TextStyle(fontSize: 16), + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + MensaButton( + onPressed: () => mealAccess.deactivateFilter(), + text: FlutterI18n.translate( + context, "mealplanException.filterButton")), + ]))); } - -} \ No newline at end of file +} diff --git a/app/lib/view/mealplan/MealPlanNoData.dart b/app/lib/view/mealplan/MealPlanNoData.dart index 7d0b8a2f..50d7c740 100644 --- a/app/lib/view/mealplan/MealPlanNoData.dart +++ b/app/lib/view/mealplan/MealPlanNoData.dart @@ -10,15 +10,18 @@ class MealPlanNoData extends StatelessWidget { @override Widget build(BuildContext context) { - return Column(crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ + return Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ const NoDataExceptionIcon(size: 48), + const SizedBox(height: 16), Text( FlutterI18n.translate(context, "mealplanException.noDataException"), - style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), + style: const TextStyle(fontSize: 16), textAlign: TextAlign.center, ), - ]); + ])); } } diff --git a/app/lib/view/mealplan/MealPlanView.dart b/app/lib/view/mealplan/MealPlanView.dart index 1ea65a0a..83921a95 100644 --- a/app/lib/view/mealplan/MealPlanView.dart +++ b/app/lib/view/mealplan/MealPlanView.dart @@ -40,7 +40,6 @@ class MealPlanView extends StatelessWidget { mealAccess.getMealPlan(), ], eagerError: true), builder: (context, snapshot) { - print(snapshot.data.toString()); if (!snapshot.hasData) { return const Center( child: CircularProgressIndicator(), @@ -57,6 +56,9 @@ class MealPlanView extends StatelessWidget { Result, MealPlanException> mealPlans = snapshot.requireData[3] as Result, MealPlanException>; + if (availableCanteens.indexWhere((element) => element.id == selectedCanteen.id) == -1) { + mealAccess.changeCanteen(availableCanteens[0]); + } return Scaffold( appBar: MensaAppBar( appBarHeight: kToolbarHeight, @@ -134,55 +136,20 @@ class MealPlanView extends StatelessWidget { exception: if (exception.exception is NoConnectionException) { - return SingleChildScrollView( - physics: - const AlwaysScrollableScrollPhysics(), - child: SizedBox( - height: - MediaQuery.of(context).size.height, - child: const MealPlanError()), - ); + return const MealPlanError(); } if (exception.exception is NoDataException) { - return SingleChildScrollView( - physics: - const AlwaysScrollableScrollPhysics(), - child: SizedBox( - height: - MediaQuery.of(context).size.height, - child: const MealPlanNoData()), - ); + return const MealPlanNoData(); } if (exception.exception is ClosedCanteenException) { - return SingleChildScrollView( - physics: - const AlwaysScrollableScrollPhysics(), - child: SizedBox( - height: - MediaQuery.of(context).size.height, - child: const MealPlanClosed()), - ); + return const MealPlanClosed(); } if (exception.exception is FilteredMealException) { - return SingleChildScrollView( - physics: - const AlwaysScrollableScrollPhysics(), - child: SizedBox( - height: - MediaQuery.of(context).size.height, - child: const MealPlanFilter()), - ); + return const MealPlanFilter(); } - return SingleChildScrollView( - physics: - const AlwaysScrollableScrollPhysics(), - child: SizedBox( - height: - MediaQuery.of(context).size.height, - child: const MealPlanError()), - ); + return const MealPlanError(); } }()), )); diff --git a/app/lib/view/mealplan/MensaCanteenSelect.dart b/app/lib/view/mealplan/MensaCanteenSelect.dart index 183252de..372fd174 100644 --- a/app/lib/view/mealplan/MensaCanteenSelect.dart +++ b/app/lib/view/mealplan/MensaCanteenSelect.dart @@ -1,4 +1,4 @@ - import 'package:app/view/core/icons/navigation/NavigationArrowDownIcon.dart'; +import 'package:app/view/core/icons/navigation/NavigationArrowDownIcon.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; import 'package:flutter/material.dart'; @@ -20,6 +20,7 @@ class MensaCanteenSelect extends StatelessWidget { Widget build(BuildContext context) { return DropdownButtonHideUnderline( child: DropdownButton( + dropdownColor: Theme.of(context).colorScheme.surface, selectedItemBuilder: (context) => _availableCanteens .map((e) => Row(children: [ SizedBox( @@ -42,7 +43,8 @@ class MensaCanteenSelect extends StatelessWidget { icon: const NavigationArrowDownIcon(size: 32), value: _selectedCanteen.id, items: _availableCanteens - .map((e) => DropdownMenuItem(value: e.id, child: Center(child: Text(e.name)))) + .map((e) => DropdownMenuItem( + value: e.id, child: Center(child: Text(e.name)))) .toList(), onChanged: (value) => _onCanteenSelected(_availableCanteens .firstWhere((element) => element.id == value)))); diff --git a/app/lib/view/settings/Settings.dart b/app/lib/view/settings/Settings.dart index 46283c66..bb2d2231 100644 --- a/app/lib/view/settings/Settings.dart +++ b/app/lib/view/settings/Settings.dart @@ -21,19 +21,17 @@ class Settings extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer( - builder: (context, storage, child) => Padding( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), - child: Scaffold( + builder: (context, storage, child) => Scaffold( appBar: MensaAppBar( - appBarHeight: kToolbarHeight * 1.25, - child: Text( + appBarHeight: kToolbarHeight, + child: Center( + child: Text( FlutterI18n.translate(context, "common.settings"), - style: const TextStyle( - fontSize: 24, fontWeight: FontWeight.bold), - ), + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + )), ), body: SingleChildScrollView( - child: Column(children: [ + child: Padding(padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Column(children: [ SettingsDropdownEntry( onChanged: (value) { if (value != null && @@ -44,6 +42,7 @@ class Settings extends StatelessWidget { value: storage.getColorScheme(), items: _getColorSchemeEntries(context), heading: "settings.colorScheme"), + const SizedBox(height: 16), SettingsDropdownEntry( onChanged: (value) { if (value != null && @@ -54,6 +53,7 @@ class Settings extends StatelessWidget { value: storage.getPriceCategory(), items: _getPriceCategoryEntries(context), heading: "settings.priceCategory"), + const SizedBox(height: 16), SettingsSection(heading: "settings.about", children: [ Row( children: [ @@ -71,17 +71,22 @@ class Settings extends StatelessWidget { }) ], ), + const SizedBox(height: 8), Text(FlutterI18n.translate(context, "settings.licence")), + const SizedBox(height: 8), Row( children: [ - Expanded(child: MensaLink( - onPressed: () => _launchUrl(Uri.parse( - 'https://github.com/kronos-et-al/MensaApp')), - text: FlutterI18n.translate( - context, "settings.gitHubLink")),) + Expanded( + child: MensaLink( + onPressed: () => _launchUrl(Uri.parse( + 'https://github.com/kronos-et-al/MensaApp')), + text: FlutterI18n.translate( + context, "settings.gitHubLink")), + ) ], ) ]), + const SizedBox(height: 16), SettingsSection( heading: "settings.legalInformation", children: [ @@ -96,49 +101,55 @@ class Settings extends StatelessWidget { ) ], ), + const SizedBox(height: 8), Row( children: [ - Expanded(child: MensaLink( - onPressed: () => _launchUrl(Uri.parse( - 'https://docs.flutter.io/flutter/services/UrlLauncher-class.html')), - text: FlutterI18n.translate( - context, "settings.contactDetails")),) + Expanded( + child: MensaLink( + onPressed: () => _launchUrl(Uri.parse( + 'https://docs.flutter.io/flutter/services/UrlLauncher-class.html')), + text: FlutterI18n.translate( + context, "settings.contactDetails")), + ) ], ) ]) - ]), + ])), ), - ))); + )); } - // todo add padding - Future _launchUrl(Uri url) async { - if (!await launchUrl(url)) { + if (!await launchUrl(url, mode: LaunchMode.externalApplication)) { throw Exception('Could not launch $url'); } } - List> _getColorSchemeEntries(BuildContext context) { + List> _getColorSchemeEntries( + BuildContext context) { List> entries = []; for (final value in MensaColorScheme.values) { - entries.add( - MensaDropdownEntry(value: value, label: FlutterI18n.translate(context, "mensaColorScheme.${value.name}"))); + entries.add(MensaDropdownEntry( + value: value, + label: FlutterI18n.translate( + context, "mensaColorScheme.${value.name}"))); } return entries; } - List> _getPriceCategoryEntries(BuildContext context) { + List> _getPriceCategoryEntries( + BuildContext context) { List> entries = []; for (final value in PriceCategory.values) { - entries - .add(MensaDropdownEntry(value: value, label: FlutterI18n.translate(context, "priceCategory.${value.name}"))); + entries.add(MensaDropdownEntry( + value: value, + label: + FlutterI18n.translate(context, "priceCategory.${value.name}"))); } return entries; } } - diff --git a/app/lib/view/settings/SettingsDropdownEntry.dart b/app/lib/view/settings/SettingsDropdownEntry.dart index 1ed31013..d49ab6df 100644 --- a/app/lib/view/settings/SettingsDropdownEntry.dart +++ b/app/lib/view/settings/SettingsDropdownEntry.dart @@ -39,8 +39,9 @@ class SettingsDropdownEntry extends StatelessWidget { Text( FlutterI18n.translate(context, _heading), style: const TextStyle( - fontSize: 14, fontWeight: FontWeight.bold, height: 1.5), + fontSize: 16, fontWeight: FontWeight.bold), ), + const SizedBox(height: 8), Row(children: [ Expanded( child: MensaDropdown( diff --git a/app/lib/view/settings/SettingsSection.dart b/app/lib/view/settings/SettingsSection.dart index fd423d98..d57021ba 100644 --- a/app/lib/view/settings/SettingsSection.dart +++ b/app/lib/view/settings/SettingsSection.dart @@ -25,9 +25,9 @@ class SettingsSection extends StatelessWidget { FlutterI18n.translate(context, _heading), style: const TextStyle( fontSize: 16, - fontWeight: FontWeight.bold, - height: 1.5), + fontWeight: FontWeight.bold), ), + const SizedBox(height: 8), ..._children ], ); diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 01c0813a..00bdc46b 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -39,8 +39,8 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { } Future _init() async { - _displayedDate = DateTime.timestamp(); - _filter = await _preferences.getFilterPreferences() ?? FilterPreferences(); + _displayedDate = DateTime.now(); + _filter = _preferences.getFilterPreferences() ?? FilterPreferences(); _priceCategory = _preferences.getPriceCategory() ?? PriceCategory.student; // get canteen from string @@ -92,6 +92,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { // filter meal plans await _filterMealPlans(); + await _setNewMealPlan(); } @override @@ -190,6 +191,12 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { return Future.value(Failure(ClosedCanteenException("canteen closed"))); } + _mealPlans.forEach((element) { + element.meals.forEach((element) { + print(element.name); + }); + }); + if (!_activeFilter) { return Future.value(Success(_mealPlans)); } From 56d14f86cf26bab957d207adc4f88efc2055bb2b Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Fri, 28 Jul 2023 12:36:28 +0200 Subject: [PATCH 167/184] await database update --- app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 00bdc46b..83de2288 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -221,7 +221,7 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { return "snackbar.refreshMealPlanError"; } - _database.updateAll(mealPlan); + await _database.updateAll(mealPlan); _mealPlans = mealPlan; From 9014ba209be4266252ac24c5a9df2a3f223b067e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 12:37:14 +0200 Subject: [PATCH 168/184] update synchronization --- app/test/view-model/PreferenceAccessTest.dart | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/test/view-model/PreferenceAccessTest.dart b/app/test/view-model/PreferenceAccessTest.dart index 0eac7549..cd9937e7 100644 --- a/app/test/view-model/PreferenceAccessTest.dart +++ b/app/test/view-model/PreferenceAccessTest.dart @@ -23,20 +23,20 @@ void main () { }); group("initialization", () { - test("client identifier", () async { - expect(await preferences.getClientIdentifier(), ""); + test("client identifier", () { + expect(preferences.getClientIdentifier(), ""); }); - test("color scheme", () async { + test("color scheme", () { expect(preferences.getColorScheme(), MensaColorScheme.system); }); - test("meal plan format", () async { - expect(await preferences.getMealPlanFormat(), MealPlanFormat.grid); + test("meal plan format", () { + expect(preferences.getMealPlanFormat(), MealPlanFormat.grid); }); - test("price category", () async { - expect(await preferences.getPriceCategory(), PriceCategory.student); + test("price category", () { + expect(preferences.getPriceCategory(), PriceCategory.student); }); }); @@ -47,7 +47,7 @@ void main () { await preferences.setClientIdentifier(string); verify(() => localStorage.setClientIdentifier(string)).called(1); - expect(await preferences.getClientIdentifier(), string); + expect(preferences.getClientIdentifier(), string); }); test("set Color Scheme", () async { @@ -65,7 +65,7 @@ void main () { await preferences.setMealPlanFormat(format); verify(() => localStorage.setMealPlanFormat(format)).called(1); - expect(await preferences.getMealPlanFormat(), format); + expect(preferences.getMealPlanFormat(), format); }); test("set Price Category", () async { @@ -74,7 +74,7 @@ void main () { await preferences.setPriceCategory(price); verify(() => localStorage.setPriceCategory(price)).called(1); - expect(await preferences.getPriceCategory(), price); + expect(preferences.getPriceCategory(), price); }); }); @@ -86,20 +86,20 @@ void main () { preferencesPredefined = PreferenceAccess(localStorage); - test("client identifier", () async { - expect(await preferencesPredefined.getClientIdentifier(), "42"); + test("client identifier", () { + expect(preferencesPredefined.getClientIdentifier(), "42"); }); - test("color scheme", () async { + test("color scheme", () { expect(preferencesPredefined.getColorScheme(), MensaColorScheme.light); }); - test("meal plan format", () async { - expect(await preferencesPredefined.getMealPlanFormat(), MealPlanFormat.list); + test("meal plan format", () { + expect(preferencesPredefined.getMealPlanFormat(), MealPlanFormat.list); }); - test("price category", () async { - expect(await preferencesPredefined.getPriceCategory(), PriceCategory.employee); + test("price category", () { + expect(preferencesPredefined.getPriceCategory(), PriceCategory.employee); }); }); From 1bd72845d6ee5b756ea04afcaeb1628e691ca41b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 12:37:44 +0200 Subject: [PATCH 169/184] delete file --- app/lib/view/core/MensaApp.dart | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app/lib/view/core/MensaApp.dart diff --git a/app/lib/view/core/MensaApp.dart b/app/lib/view/core/MensaApp.dart deleted file mode 100644 index e69de29b..00000000 From 422441cf022405000c8f455ef64eae05c7d1d781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 12:38:06 +0200 Subject: [PATCH 170/184] formating --- .../api_server/requests/querys.graphql.dart | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/app/lib/model/api_server/requests/querys.graphql.dart b/app/lib/model/api_server/requests/querys.graphql.dart index 3afd4334..68d55f6d 100644 --- a/app/lib/model/api_server/requests/querys.graphql.dart +++ b/app/lib/model/api_server/requests/querys.graphql.dart @@ -199,6 +199,7 @@ extension ClientExtension$Fragment$canteen on graphql.GraphQLClient { data: data.toJson(), broadcast: broadcast, ); + Fragment$canteen? readFragment$canteen({ required Map idFields, bool optimistic = true, @@ -235,6 +236,7 @@ class Variables$Fragment$mealPlan { Map _$data; String get date => (_$data['date'] as String); + Map toJson() { final result$data = {}; final l$date = date; @@ -247,6 +249,7 @@ class Variables$Fragment$mealPlan { this, (i) => i, ); + @override bool operator ==(Object other) { if (identical(this, other)) { @@ -403,6 +406,7 @@ abstract class CopyWith$Fragment$mealPlan { List? lines, String? $__typename, }); + TRes lines( Iterable Function( Iterable< @@ -435,6 +439,7 @@ class _CopyWithImpl$Fragment$mealPlan ? _instance.$__typename : ($__typename as String), )); + TRes lines( Iterable Function( Iterable< @@ -460,6 +465,7 @@ class _CopyWithStubImpl$Fragment$mealPlan String? $__typename, }) => _res; + lines(_fn) => _res; } @@ -579,6 +585,7 @@ extension ClientExtension$Fragment$mealPlan on graphql.GraphQLClient { data: data.toJson(), broadcast: broadcast, ); + Fragment$mealPlan? readFragment$mealPlan({ required Map idFields, required Variables$Fragment$mealPlan variables, @@ -739,7 +746,9 @@ abstract class CopyWith$Fragment$mealPlan$lines { List? meals, String? $__typename, }); + CopyWith$Fragment$canteen get canteen; + TRes meals( Iterable? Function( Iterable>?) @@ -781,6 +790,7 @@ class _CopyWithImpl$Fragment$mealPlan$lines ? _instance.$__typename : ($__typename as String), )); + CopyWith$Fragment$canteen get canteen { final local$canteen = _instance.canteen; return CopyWith$Fragment$canteen(local$canteen, (e) => call(canteen: e)); @@ -811,8 +821,10 @@ class _CopyWithStubImpl$Fragment$mealPlan$lines String? $__typename, }) => _res; + CopyWith$Fragment$canteen get canteen => CopyWith$Fragment$canteen.stub(_res); + meals(_fn) => _res; } @@ -1074,14 +1086,19 @@ abstract class CopyWith$Fragment$mealInfo { List? sides, String? $__typename, }); + CopyWith$Fragment$price get price; + CopyWith$Fragment$mealInfo$statistics get statistics; + CopyWith$Fragment$mealInfo$ratings get ratings; + TRes images( Iterable Function( Iterable< CopyWith$Fragment$mealInfo$images>) _fn); + TRes sides( Iterable Function( Iterable< @@ -1148,6 +1165,7 @@ class _CopyWithImpl$Fragment$mealInfo ? _instance.$__typename : ($__typename as String), )); + CopyWith$Fragment$price get price { final local$price = _instance.price; return CopyWith$Fragment$price(local$price, (e) => call(price: e)); @@ -1177,6 +1195,7 @@ class _CopyWithImpl$Fragment$mealInfo e, (i) => i, ))).toList()); + TRes sides( Iterable Function( Iterable< @@ -1211,12 +1230,17 @@ class _CopyWithStubImpl$Fragment$mealInfo String? $__typename, }) => _res; + CopyWith$Fragment$price get price => CopyWith$Fragment$price.stub(_res); + CopyWith$Fragment$mealInfo$statistics get statistics => CopyWith$Fragment$mealInfo$statistics.stub(_res); + CopyWith$Fragment$mealInfo$ratings get ratings => CopyWith$Fragment$mealInfo$ratings.stub(_res); + images(_fn) => _res; + sides(_fn) => _res; } @@ -1526,6 +1550,7 @@ extension ClientExtension$Fragment$mealInfo on graphql.GraphQLClient { data: data.toJson(), broadcast: broadcast, ); + Fragment$mealInfo? readFragment$mealInfo({ required Map idFields, bool optimistic = true, @@ -2313,6 +2338,7 @@ abstract class CopyWith$Fragment$mealInfo$sides { Enum$MealType? mealType, String? $__typename, }); + CopyWith$Fragment$price get price; } @@ -2359,6 +2385,7 @@ class _CopyWithImpl$Fragment$mealInfo$sides ? _instance.$__typename : ($__typename as String), )); + CopyWith$Fragment$price get price { final local$price = _instance.price; return CopyWith$Fragment$price(local$price, (e) => call(price: e)); @@ -2381,6 +2408,7 @@ class _CopyWithStubImpl$Fragment$mealInfo$sides String? $__typename, }) => _res; + CopyWith$Fragment$price get price => CopyWith$Fragment$price.stub(_res); } @@ -2634,6 +2662,7 @@ extension ClientExtension$Fragment$price on graphql.GraphQLClient { data: data.toJson(), broadcast: broadcast, ); + Fragment$price? readFragment$price({ required Map idFields, bool optimistic = true, @@ -2671,6 +2700,7 @@ class Variables$Query$GetMealPlanForDay { Map _$data; String get date => (_$data['date'] as String); + Map toJson() { final result$data = {}; final l$date = date; @@ -2683,6 +2713,7 @@ class Variables$Query$GetMealPlanForDay { this, (i) => i, ); + @override bool operator ==(Object other) { if (identical(this, other)) { @@ -2839,6 +2870,7 @@ abstract class CopyWith$Query$GetMealPlanForDay { List? getCanteens, String? $__typename, }); + TRes getCanteens( Iterable Function( Iterable>) @@ -2870,6 +2902,7 @@ class _CopyWithImpl$Query$GetMealPlanForDay ? _instance.$__typename : ($__typename as String), )); + TRes getCanteens( Iterable Function( Iterable>) @@ -2893,6 +2926,7 @@ class _CopyWithStubImpl$Query$GetMealPlanForDay String? $__typename, }) => _res; + getCanteens(_fn) => _res; } @@ -2946,9 +2980,11 @@ const documentNodeQueryGetMealPlanForDay = DocumentNode(definitions: [ fragmentDefinitionmealInfo, fragmentDefinitionprice, ]); + Query$GetMealPlanForDay _parserFn$Query$GetMealPlanForDay( Map data) => Query$GetMealPlanForDay.fromJson(data); + typedef OnQueryComplete$Query$GetMealPlanForDay = FutureOr Function( Map?, Query$GetMealPlanForDay?, @@ -3050,9 +3086,11 @@ extension ClientExtension$Query$GetMealPlanForDay on graphql.GraphQLClient { Future> query$GetMealPlanForDay( Options$Query$GetMealPlanForDay options) async => await this.query(options); + graphql.ObservableQuery watchQuery$GetMealPlanForDay( WatchOptions$Query$GetMealPlanForDay options) => this.watchQuery(options); + void writeQuery$GetMealPlanForDay({ required Query$GetMealPlanForDay data, required Variables$Query$GetMealPlanForDay variables, @@ -3067,6 +3105,7 @@ extension ClientExtension$Query$GetMealPlanForDay on graphql.GraphQLClient { data: data.toJson(), broadcast: broadcast, ); + Query$GetMealPlanForDay? readQuery$GetMealPlanForDay({ required Variables$Query$GetMealPlanForDay variables, bool optimistic = true, @@ -3086,6 +3125,7 @@ extension ClientExtension$Query$GetMealPlanForDay on graphql.GraphQLClient { graphql_flutter.QueryHookResult useQuery$GetMealPlanForDay(Options$Query$GetMealPlanForDay options) => graphql_flutter.useQuery(options); + graphql.ObservableQuery useWatchQuery$GetMealPlanForDay( WatchOptions$Query$GetMealPlanForDay options) => @@ -3128,7 +3168,9 @@ class Variables$Query$GetCanteenDate { Map _$data; String get canteenId => (_$data['canteenId'] as String); + String get date => (_$data['date'] as String); + Map toJson() { final result$data = {}; final l$canteenId = canteenId; @@ -3143,6 +3185,7 @@ class Variables$Query$GetCanteenDate { this, (i) => i, ); + @override bool operator ==(Object other) { if (identical(this, other)) { @@ -3312,6 +3355,7 @@ abstract class CopyWith$Query$GetCanteenDate { Fragment$mealPlan? getCanteen, String? $__typename, }); + CopyWith$Fragment$mealPlan get getCanteen; } @@ -3340,6 +3384,7 @@ class _CopyWithImpl$Query$GetCanteenDate ? _instance.$__typename : ($__typename as String), )); + CopyWith$Fragment$mealPlan get getCanteen { final local$getCanteen = _instance.getCanteen; return local$getCanteen == null @@ -3360,6 +3405,7 @@ class _CopyWithStubImpl$Query$GetCanteenDate String? $__typename, }) => _res; + CopyWith$Fragment$mealPlan get getCanteen => CopyWith$Fragment$mealPlan.stub(_res); } @@ -3428,9 +3474,11 @@ const documentNodeQueryGetCanteenDate = DocumentNode(definitions: [ fragmentDefinitionmealInfo, fragmentDefinitionprice, ]); + Query$GetCanteenDate _parserFn$Query$GetCanteenDate( Map data) => Query$GetCanteenDate.fromJson(data); + typedef OnQueryComplete$Query$GetCanteenDate = FutureOr Function( Map?, Query$GetCanteenDate?, @@ -3529,9 +3577,11 @@ extension ClientExtension$Query$GetCanteenDate on graphql.GraphQLClient { Future> query$GetCanteenDate( Options$Query$GetCanteenDate options) async => await this.query(options); + graphql.ObservableQuery watchQuery$GetCanteenDate( WatchOptions$Query$GetCanteenDate options) => this.watchQuery(options); + void writeQuery$GetCanteenDate({ required Query$GetCanteenDate data, required Variables$Query$GetCanteenDate variables, @@ -3546,6 +3596,7 @@ extension ClientExtension$Query$GetCanteenDate on graphql.GraphQLClient { data: data.toJson(), broadcast: broadcast, ); + Query$GetCanteenDate? readQuery$GetCanteenDate({ required Variables$Query$GetCanteenDate variables, bool optimistic = true, @@ -3564,6 +3615,7 @@ extension ClientExtension$Query$GetCanteenDate on graphql.GraphQLClient { graphql_flutter.QueryHookResult useQuery$GetCanteenDate( Options$Query$GetCanteenDate options) => graphql_flutter.useQuery(options); + graphql.ObservableQuery useWatchQuery$GetCanteenDate( WatchOptions$Query$GetCanteenDate options) => graphql_flutter.useWatchQuery(options); @@ -3609,8 +3661,11 @@ class Variables$Query$GetMeal { Map _$data; String get date => (_$data['date'] as String); + String get mealId => (_$data['mealId'] as String); + String get lineId => (_$data['lineId'] as String); + Map toJson() { final result$data = {}; final l$date = date; @@ -3627,6 +3682,7 @@ class Variables$Query$GetMeal { this, (i) => i, ); + @override bool operator ==(Object other) { if (identical(this, other)) { @@ -3807,6 +3863,7 @@ abstract class CopyWith$Query$GetMeal { Fragment$mealInfo? getMeal, String? $__typename, }); + CopyWith$Fragment$mealInfo get getMeal; } @@ -3835,6 +3892,7 @@ class _CopyWithImpl$Query$GetMeal ? _instance.$__typename : ($__typename as String), )); + CopyWith$Fragment$mealInfo get getMeal { final local$getMeal = _instance.getMeal; return local$getMeal == null @@ -3854,6 +3912,7 @@ class _CopyWithStubImpl$Query$GetMeal String? $__typename, }) => _res; + CopyWith$Fragment$mealInfo get getMeal => CopyWith$Fragment$mealInfo.stub(_res); } @@ -3937,8 +3996,10 @@ const documentNodeQueryGetMeal = DocumentNode(definitions: [ fragmentDefinitionmealInfo, fragmentDefinitionprice, ]); + Query$GetMeal _parserFn$Query$GetMeal(Map data) => Query$GetMeal.fromJson(data); + typedef OnQueryComplete$Query$GetMeal = FutureOr Function( Map?, Query$GetMeal?, @@ -4036,9 +4097,11 @@ extension ClientExtension$Query$GetMeal on graphql.GraphQLClient { Future> query$GetMeal( Options$Query$GetMeal options) async => await this.query(options); + graphql.ObservableQuery watchQuery$GetMeal( WatchOptions$Query$GetMeal options) => this.watchQuery(options); + void writeQuery$GetMeal({ required Query$GetMeal data, required Variables$Query$GetMeal variables, @@ -4052,6 +4115,7 @@ extension ClientExtension$Query$GetMeal on graphql.GraphQLClient { data: data.toJson(), broadcast: broadcast, ); + Query$GetMeal? readQuery$GetMeal({ required Variables$Query$GetMeal variables, bool optimistic = true, @@ -4070,6 +4134,7 @@ extension ClientExtension$Query$GetMeal on graphql.GraphQLClient { graphql_flutter.QueryHookResult useQuery$GetMeal( Options$Query$GetMeal options) => graphql_flutter.useQuery(options); + graphql.ObservableQuery useWatchQuery$GetMeal( WatchOptions$Query$GetMeal options) => graphql_flutter.useWatchQuery(options); @@ -4177,6 +4242,7 @@ abstract class CopyWith$Query$GetDefaultCanteen { List? getCanteens, String? $__typename, }); + TRes getCanteens( Iterable Function( Iterable>) @@ -4208,6 +4274,7 @@ class _CopyWithImpl$Query$GetDefaultCanteen ? _instance.$__typename : ($__typename as String), )); + TRes getCanteens( Iterable Function( Iterable>) @@ -4231,6 +4298,7 @@ class _CopyWithStubImpl$Query$GetDefaultCanteen String? $__typename, }) => _res; + getCanteens(_fn) => _res; } @@ -4271,9 +4339,11 @@ const documentNodeQueryGetDefaultCanteen = DocumentNode(definitions: [ ), fragmentDefinitioncanteen, ]); + Query$GetDefaultCanteen _parserFn$Query$GetDefaultCanteen( Map data) => Query$GetDefaultCanteen.fromJson(data); + typedef OnQueryComplete$Query$GetDefaultCanteen = FutureOr Function( Map?, Query$GetDefaultCanteen?, @@ -4369,9 +4439,11 @@ extension ClientExtension$Query$GetDefaultCanteen on graphql.GraphQLClient { Future> query$GetDefaultCanteen( [Options$Query$GetDefaultCanteen? options]) async => await this.query(options ?? Options$Query$GetDefaultCanteen()); + graphql.ObservableQuery watchQuery$GetDefaultCanteen( [WatchOptions$Query$GetDefaultCanteen? options]) => this.watchQuery(options ?? WatchOptions$Query$GetDefaultCanteen()); + void writeQuery$GetDefaultCanteen({ required Query$GetDefaultCanteen data, bool broadcast = true, @@ -4383,6 +4455,7 @@ extension ClientExtension$Query$GetDefaultCanteen on graphql.GraphQLClient { data: data.toJson(), broadcast: broadcast, ); + Query$GetDefaultCanteen? readQuery$GetDefaultCanteen( {bool optimistic = true}) { final result = this.readQuery( @@ -4398,6 +4471,7 @@ extension ClientExtension$Query$GetDefaultCanteen on graphql.GraphQLClient { graphql_flutter.QueryHookResult useQuery$GetDefaultCanteen([Options$Query$GetDefaultCanteen? options]) => graphql_flutter.useQuery(options ?? Options$Query$GetDefaultCanteen()); + graphql.ObservableQuery useWatchQuery$GetDefaultCanteen( [WatchOptions$Query$GetDefaultCanteen? options]) => From d70743c51edfbac3408718664630b409f9c32518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 12:38:26 +0200 Subject: [PATCH 171/184] add todos --- app/lib/model/database/SQLiteDatabaseAccess.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index d3c11c28..3236828b 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -243,6 +243,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } } + // todo unused Future _getDBSide(String sideID) async { var db = await database; var result = await db.query(DBSide.tableName, @@ -268,6 +269,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { return result.map((sideRow) => DBSide.fromMap(sideRow)).toList(); } + // todo unused Future _getDBImage(String imageID) async { var db = await database; var result = await db.query(DBImage.tableName, @@ -297,6 +299,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { } } + // todo unused Future _getDBFavorite(String favoriteID) async { var db = await database; var result = await db.query(DBFavorite.tableName, @@ -351,6 +354,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { @override Future getFavoriteMealsDate(Meal meal) { + // todo what did he do? return Future.value(DateTime.now()); } From f52bfed2e58fccf67a881d8431190b6d9262d6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 13:04:38 +0200 Subject: [PATCH 172/184] documentation and formating repository --- .../filter/FilterPreferences.dart | 21 +++---- .../data_classes/filter/Frequency.dart | 4 +- .../data_classes/filter/Sorting.dart | 6 +- .../data_classes/meal/Additive.dart | 31 ++++++++- .../data_classes/meal/Allergen.dart | 58 ++++++++++++++++- .../data_classes/meal/FoodType.dart | 9 ++- .../data_classes/meal/ImageData.dart | 3 +- .../repository/data_classes/meal/Meal.dart | 63 +++++++++++++++---- .../repository/data_classes/meal/Price.dart | 25 +++++++- .../repository/data_classes/meal/Side.dart | 57 ++++++++++++++--- .../data_classes/mealplan/Canteen.dart | 15 ++++- .../data_classes/mealplan/Line.dart | 17 +++++ .../data_classes/mealplan/MealPlan.dart | 29 ++++++++- .../data_classes/settings/MealPlanFormat.dart | 4 +- .../settings/MensaColorScheme.dart | 5 +- .../data_classes/settings/PriceCategory.dart | 5 +- .../data_classes/settings/ReportCategory.dart | 7 ++- .../error_handling/NoMealException.dart | 2 +- .../repository/error_handling/Result.dart | 1 - .../repository/interface/IDatabaseAccess.dart | 5 +- 20 files changed, 309 insertions(+), 58 deletions(-) diff --git a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart index 02effe4c..49ac4f7e 100644 --- a/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart +++ b/app/lib/view_model/repository/data_classes/filter/FilterPreferences.dart @@ -25,16 +25,15 @@ class FilterPreferences { /// @param onlyFavorite Only favorite meals are to be displayed. /// @param sortedBy The value which sorts the meals. /// @param ascending The order in which the meals are displayed. - FilterPreferences({ - List? categories, - List? allergens, - int? price, - int? rating, - List? frequency, - bool? onlyFavorite, - Sorting? sortedBy, - bool? ascending - }) + FilterPreferences( + {List? categories, + List? allergens, + int? price, + int? rating, + List? frequency, + bool? onlyFavorite, + Sorting? sortedBy, + bool? ascending}) : _categories = categories ?? _getAllFoodTypes(), _allergens = allergens ?? _getAllAllergen(), _price = price ?? 1000, @@ -220,4 +219,4 @@ class FilterPreferences { _onlyFavorite.hashCode ^ _sortedBy.hashCode ^ _ascending.hashCode; -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/filter/Frequency.dart b/app/lib/view_model/repository/data_classes/filter/Frequency.dart index a3c0fc4a..0bb1450c 100644 --- a/app/lib/view_model/repository/data_classes/filter/Frequency.dart +++ b/app/lib/view_model/repository/data_classes/filter/Frequency.dart @@ -2,8 +2,10 @@ enum Frequency { /// A meal is offered for the first time newMeal, + /// A meal is not offered often. rare, + /// The meal is not new and is not rare. normal -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/filter/Sorting.dart b/app/lib/view_model/repository/data_classes/filter/Sorting.dart index a488c8d2..e516473a 100644 --- a/app/lib/view_model/repository/data_classes/filter/Sorting.dart +++ b/app/lib/view_model/repository/data_classes/filter/Sorting.dart @@ -1,12 +1,14 @@ - /// This enum contains all sorting options for the meal plan enum Sorting { /// sort after line position of website line, + /// sort after price price, + /// sort after rating rating, + /// sort after frequency frequency -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/meal/Additive.dart b/app/lib/view_model/repository/data_classes/meal/Additive.dart index 784097a6..bdc8df4d 100644 --- a/app/lib/view_model/repository/data_classes/meal/Additive.dart +++ b/app/lib/view_model/repository/data_classes/meal/Additive.dart @@ -1,18 +1,47 @@ /// Enum class for additives enum Additive { + /// the food includes colorant colorant, + + /// the food includes preservative agents preservingAgents, + + /// the food includes antioxidant agents antioxidantAgents, + + /// the food includes flavor enhancer flavourEnhancer, + + /// the food includes phosphate phosphate, + + /// the food includes waxed surfaces surfaceWaxed, + + /// the food includes sulphur sulphur, + + /// the food includes artificially blackened olives artificiallyBlackenedOlives, + + /// the food includes sweetener sweetener, + + /// could lead to laxative effects if overused laxativeIfOverused, + + /// the food includes phenylalanine phenylalanine, + + /// the food could include alcohol alcohol, + + /// the food includes pressed meat pressedMeat, + + /// the food is glazed with cacao glazingWithCacao, + + /// the food includes pressed fish pressedFish -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/meal/Allergen.dart b/app/lib/view_model/repository/data_classes/meal/Allergen.dart index bb0c1a08..e235453e 100644 --- a/app/lib/view_model/repository/data_classes/meal/Allergen.dart +++ b/app/lib/view_model/repository/data_classes/meal/Allergen.dart @@ -1,30 +1,86 @@ +/// Enum for allergens enum Allergen { + /// the food includes cashews ca, + + /// the food includes spelt and spelt gluten di, + + /// the food includes eggs ei, + + /// the food includes peanuts er, + + /// the food includes fish fi, + + /// the food includes barley and barley gluten ge, + + /// the food includes oat and oat gluten hf, + + /// the food includes hazelnuts ha, + + /// the food includes kamut and kamut gluten ka, + + /// the food includes crustaceans kr, + + /// the food includes lupine lu, + + /// the food includes almonds ma, + + /// the food includes milk ml, + + /// the food includes brazil nuts pa, + + /// the food includes pecans pe, + + /// the food includes pistachios pi, + + /// the food includes macadamia nuts qu, + + /// the food includes rye and rye gluten ro, + + /// the food includes sesame sa, + + /// the food includes celery se, + + /// the food includes sulphite sf, + + /// the food includes mustard sn, + + /// the food includes soya so, + + /// the food includes walnuts wa, + + /// the food includes wheat and wheat gluten we, + + /// the food includes molluscs wt, + + /// the food includes animal loaf la, + + /// the food includes gelatine gl -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/meal/FoodType.dart b/app/lib/view_model/repository/data_classes/meal/FoodType.dart index f8e4b109..b062c061 100644 --- a/app/lib/view_model/repository/data_classes/meal/FoodType.dart +++ b/app/lib/view_model/repository/data_classes/meal/FoodType.dart @@ -2,18 +2,25 @@ enum FoodType { /// the food includes beef beef, + /// the food includes regional beef beefAw, + /// the food includes pork pork, + /// the food includes regional pork porkAw, + /// the food includes fish fish, + /// the food is vegetarian vegetarian, + /// the food is vegan vegan, + /// the food type is unspecified unknown, -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/meal/ImageData.dart b/app/lib/view_model/repository/data_classes/meal/ImageData.dart index 12a1a944..790c4468 100644 --- a/app/lib/view_model/repository/data_classes/meal/ImageData.dart +++ b/app/lib/view_model/repository/data_classes/meal/ImageData.dart @@ -1,6 +1,5 @@ /// This class represents an image of a meal. class ImageData { - final String _id; final String _url; final double _imageRank; @@ -72,4 +71,4 @@ class ImageData { 'url': _url, }; } -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/meal/Meal.dart b/app/lib/view_model/repository/data_classes/meal/Meal.dart index 1007de87..3efaf007 100644 --- a/app/lib/view_model/repository/data_classes/meal/Meal.dart +++ b/app/lib/view_model/repository/data_classes/meal/Meal.dart @@ -61,8 +61,7 @@ class Meal { List? images, int? numberOfOccurance, bool? isFavorite, - }) - : _id = id, + }) : _id = id, _name = name, _foodType = foodType, _price = price, @@ -116,8 +115,7 @@ class Meal { List? images, int? numberOfOccurance, bool? isFavorite, - }) - : _id = id ?? meal.id, + }) : _id = id ?? meal.id, _name = name ?? meal.name, _foodType = foodType ?? meal.foodType, _price = price ?? meal.price, @@ -134,21 +132,24 @@ class Meal { _numberOfOccurance = numberOfOccurance ?? meal._numberOfOccurance, _isFavorite = isFavorite ?? meal.isFavorite; - /// This method returns the number of occurences in three months. + /// @return The number of occurences in three months int? get numberOfOccurance => _numberOfOccurance; /// This method adds the meal to the favorites. + /// If the meal is already a favorite, it does nothing. void setFavorite() { _isFavorite = true; } /// This method deletes the meal from the favorites. + /// If the meal is not a favorite, it does nothing. void deleteFavorite() { _isFavorite = false; } /// This method returns the information of the meal that are stored in the database. + /// @return The information of the meal that are stored in the database Map toMap() { return { 'mealID': _id, @@ -165,49 +166,85 @@ class Meal { } /// This method returns the additives as a map. + /// @return The additives as a map List> additiveToMap() { - return _additives!.map((additive) => { - 'mealID': _id, - 'additive': additive, - }).toList(); + return _additives! + .map((additive) => { + 'mealID': _id, + 'additive': additive, + }) + .toList(); } /// This method returns the allerens as a map. + /// @return The allergens as a map List> allergenToMap() { - return _allergens!.map((allergen) => { - 'mealID': _id, - 'allergen': allergen, - }).toList(); + return _allergens! + .map((allergen) => { + 'mealID': _id, + 'allergen': allergen, + }) + .toList(); } + /// This method returns the sides as a map. + /// @return The sides as a map String get id => _id; + /// This method returns the name of the meal. + /// @return The name of the meal String get name => _name; + /// This method returns the food type of the meal. + /// @return The food type of the meal FoodType get foodType => _foodType; + /// This method returns the price of the meal. + /// @return The price of the meal Price get price => _price; + /// This method returns the allergens of the meal. + /// @return The allergens of the meal List? get allergens => _allergens; + /// This method returns the additives of the meal. + /// @return The additives of the meal List? get additives => _additives; + /// This method returns the sides of the meal. + /// @return The sides of the meal List? get sides => _sides; + /// This method returns the individual rating of the meal. + /// @return The individual rating of the meal int? get individualRating => _individualRating; + /// This method returns the number of ratings of the meal. + /// @return The number of ratings of the meal int? get numberOfRatings => _numberOfRatings; + /// This method returns the average rating of the meal. + /// @return The average rating of the meal double? get averageRating => _averageRating; + /// This method returns the date when the meal was last served. + /// @return The date when the meal was last served DateTime? get lastServed => _lastServed; + /// This method returns the date when the meal will be served next. + /// @return The date when the meal will be served next DateTime? get nextServed => _nextServed; + /// This method returns the relative frequency of the meal. + /// @return The relative frequency of the meal Frequency? get relativeFrequency => _relativeFrequency; + /// This method returns the images of the meal. + /// @return The images of the meal List? get images => _images; + /// This method returns the favorite status of the meal. + /// @return The favorite status of the meal bool get isFavorite => _isFavorite ?? false; @override diff --git a/app/lib/view_model/repository/data_classes/meal/Price.dart b/app/lib/view_model/repository/data_classes/meal/Price.dart index 0dcefd46..2a01b1f1 100644 --- a/app/lib/view_model/repository/data_classes/meal/Price.dart +++ b/app/lib/view_model/repository/data_classes/meal/Price.dart @@ -1,12 +1,18 @@ import '../settings/PriceCategory.dart'; +/// This class represents a price. class Price { - final int _student; final int _employee; final int _pupil; final int _guest; + /// This constructor creates a new price. + /// @param student The price for students + /// @param employee The price for employees + /// @param pupil The price for pupils + /// @param guest The price for guests + /// @return A new price Price({ required int student, required int employee, @@ -17,7 +23,10 @@ class Price { _pupil = pupil, _guest = guest; - getPrice(PriceCategory category) { + /// This method returns the price for the committed price category. + /// @param category The price category + /// @return The price for the committed price category + int getPrice(PriceCategory category) { switch (category) { case PriceCategory.student: return _student; @@ -30,6 +39,8 @@ class Price { } } + /// This method returns all attributes needed for the database. + /// @return All attributes needed for the database Map toMap() { return { 'priceStudent': _student, @@ -39,11 +50,19 @@ class Price { }; } + /// This method returns the price for guests. + /// @return The price for guests int get guest => _guest; + /// This method returns the price for pupils. + /// @return The price for pupils int get pupil => _pupil; + /// This method returns the price for employees. + /// @return The price for employees int get employee => _employee; + /// This method returns the price for students. + /// @return The price for students int get student => _student; -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/meal/Side.dart b/app/lib/view_model/repository/data_classes/meal/Side.dart index 4a37bae0..c73e2508 100644 --- a/app/lib/view_model/repository/data_classes/meal/Side.dart +++ b/app/lib/view_model/repository/data_classes/meal/Side.dart @@ -3,6 +3,7 @@ import 'Allergen.dart'; import 'FoodType.dart'; import 'Price.dart'; +/// This class represents a side. class Side { final String _id; final String _name; @@ -11,6 +12,14 @@ class Side { final List _allergens; final List _additives; + /// This constructor creates a new side. + /// @param id The id of the side + /// @param name The name of the side + /// @param foodType The food type of the side + /// @param price The price of the side + /// @param allergens The allergens of the side + /// @param additives The additives of the side + /// @return A new side Side({ required String id, required String name, @@ -25,6 +34,16 @@ class Side { _allergens = allergens, _additives = additives; + /// This constructor creates a new side with the committed values. + /// If any values are not committed these values are replaced with the values of the committed side. + /// @param side The side that should be copied + /// @param id The id of the side + /// @param name The name of the side + /// @param foodType The food type of the side + /// @param price The price of the side + /// @param allergens The allergens of the side + /// @param additives The additives of the side + /// @return A new side with the committed values Side.copy({ required Side side, String? id, @@ -40,8 +59,12 @@ class Side { _allergens = allergens ?? side.allergens, _additives = additives ?? side.additives; + /// This method returns the id of the side. + /// @return The id of the side String get id => _id; + /// This method returns all attributes needed for the database. + /// @return All attributes needed for the database Map toMap() { return { 'sideID': _id, @@ -52,28 +75,46 @@ class Side { }; } + /// This method returns the additives as a map. + /// @return The additives as a map List> additiveToMap() { - return _additives.map((additive) => { - 'sideID': _id, - 'additive': additive, - }).toList(); + return _additives + .map((additive) => { + 'sideID': _id, + 'additive': additive, + }) + .toList(); } + /// This method returns the allerens as a map. + /// @return The allergens as a map List> allergenToMap() { - return _allergens.map((allergen) => { - 'sideID': _id, - 'allergen': allergen, - }).toList(); + return _allergens + .map((allergen) => { + 'sideID': _id, + 'allergen': allergen, + }) + .toList(); } + /// This method returns the name of the side. + /// @return The name of the side String get name => _name; + /// This method returns the food type of the side. + /// @return The food type of the side FoodType get foodType => _foodType; + /// This method returns the price of the side. + /// @return The price of the side Price get price => _price; + /// This method returns the allergens of the side. + /// @return The allergens of the side List get allergens => _allergens; + /// This method returns the additives of the side. + /// @return The additives of the side List get additives => _additives; @override diff --git a/app/lib/view_model/repository/data_classes/mealplan/Canteen.dart b/app/lib/view_model/repository/data_classes/mealplan/Canteen.dart index c76b8093..90e28117 100644 --- a/app/lib/view_model/repository/data_classes/mealplan/Canteen.dart +++ b/app/lib/view_model/repository/data_classes/mealplan/Canteen.dart @@ -1,23 +1,32 @@ +/// This class represents a canteen. class Canteen { - final String _id; final String _name; + /// This constructor creates a new canteen. + /// @param id The id of the canteen + /// @param name The name of the canteen + /// @return A new canteen Canteen({ required String id, required String name, }) : _id = id, _name = name; + /// This method returns the id of the canteen. + /// @return The id of the canteen String get id => _id; + /// This method returns the name of the canteen. + /// @return The name of the canteen String get name => _name; + /// This method returns all attributes needed for the database. + /// @return All attributes needed for the database Map toMap() { return { 'canteenID': _id, 'name': _name, }; } - -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/mealplan/Line.dart b/app/lib/view_model/repository/data_classes/mealplan/Line.dart index b16b6232..dbc5fd87 100644 --- a/app/lib/view_model/repository/data_classes/mealplan/Line.dart +++ b/app/lib/view_model/repository/data_classes/mealplan/Line.dart @@ -1,11 +1,18 @@ import 'Canteen.dart'; +/// This class represents a line. class Line { final String _id; final String _name; final Canteen _canteen; final int _position; + /// This constructor creates a new line. + /// @param id The id of the line + /// @param name The name of the line + /// @param canteen The canteen of the line + /// @param position The position of the line + /// @return A new line Line({ required String id, required String name, @@ -16,6 +23,8 @@ class Line { _canteen = canteen, _position = position; + /// This method returns all attributes needed for the database. + /// @return All attributes needed for the database Map toMap() { return { 'lineID': _id, @@ -25,11 +34,19 @@ class Line { }; } + /// This method returns the id of the line. + /// @return The id of the line String get id => _id; + /// This method returns the name of the line. + /// @return The name of the line String get name => _name; + /// This method returns the canteen of the line. + /// @return The canteen of the line Canteen get canteen => _canteen; + /// This method returns the position of the line. + /// @return The position of the line int get position => _position; } diff --git a/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart b/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart index 31b847aa..42c8e64c 100644 --- a/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart +++ b/app/lib/view_model/repository/data_classes/mealplan/MealPlan.dart @@ -3,13 +3,19 @@ import 'package:flutter/foundation.dart'; import '../meal/Meal.dart'; import 'Line.dart'; +/// This class represents a meal plan. class MealPlan { - final DateTime _date; final Line _line; final bool _isClosed; final List _meals; + /// This constructor creates a new meal plan. + /// @param date The date of the meal plan + /// @param line The line of the meal plan + /// @param isClosed The information if the line is closed on the date of the meal plan + /// @param meals The meals of the meal plan + /// @return A new meal plan MealPlan({ required DateTime date, required Line line, @@ -20,6 +26,15 @@ class MealPlan { _isClosed = isClosed, _meals = meals; + /// This constructor creates a new meal plan with the committed values. + /// If any values are not committed these values are replaced with the values of the committed meal plan. + /// + /// @param mealPlan The meal plan that should be copied + /// @param date The date of the meal plan + /// @param line The line of the meal plan + /// @param isClosed The information if the line is closed on the date of the meal plan + /// @param meals The meals of the meal plan + /// @return A new meal plan with the committed values MealPlan.copy({ required MealPlan mealPlan, DateTime? date, @@ -31,14 +46,24 @@ class MealPlan { _isClosed = isClosed ?? mealPlan.isClosed, _meals = meals ?? mealPlan.meals; + /// This method returns the date of the meal plan. + /// @return The date of the meal plan DateTime get date => _date; + /// This method returns the line of the meal plan. + /// @return The line of the meal plan Line get line => _line; + /// This method returns the information if the line is closed on the date of the meal plan. + /// @return The information if the line is closed on the date of the meal plan bool get isClosed => _isClosed; + /// This method returns the meals of the meal plan. + /// @return The meals of the meal plan List get meals => _meals; + /// This method returns the date, the line and if the line is closed on the date of the meal plan as a map. + /// @return The date, the line and if the line is closed on the date of the meal plan as a map Map toMap() { return { 'date': _date, @@ -62,4 +87,4 @@ class MealPlan { @override int get hashCode => _date.hashCode ^ _line.hashCode ^ _isClosed.hashCode ^ _meals.hashCode; -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/settings/MealPlanFormat.dart b/app/lib/view_model/repository/data_classes/settings/MealPlanFormat.dart index 721e9708..50e554b7 100644 --- a/app/lib/view_model/repository/data_classes/settings/MealPlanFormat.dart +++ b/app/lib/view_model/repository/data_classes/settings/MealPlanFormat.dart @@ -1,8 +1,8 @@ - /// This enum contains all different meal plan formats. enum MealPlanFormat { /// the meal plan is displayed as a list list, + /// the meal plan is displayed as a grid grid -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/settings/MensaColorScheme.dart b/app/lib/view_model/repository/data_classes/settings/MensaColorScheme.dart index e0c98921..952e919b 100644 --- a/app/lib/view_model/repository/data_classes/settings/MensaColorScheme.dart +++ b/app/lib/view_model/repository/data_classes/settings/MensaColorScheme.dart @@ -1,10 +1,11 @@ - /// This enums contains all different color schemes. enum MensaColorScheme { /// The light color scheme. light, + /// The dark color scheme. dark, + /// The color scheme chosen in the system. system -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/settings/PriceCategory.dart b/app/lib/view_model/repository/data_classes/settings/PriceCategory.dart index c80e0b9c..8e48b219 100644 --- a/app/lib/view_model/repository/data_classes/settings/PriceCategory.dart +++ b/app/lib/view_model/repository/data_classes/settings/PriceCategory.dart @@ -2,10 +2,13 @@ enum PriceCategory { /// The price category for students. student, + /// The price category for employees. employee, + /// The price category for pupils. pupil, + /// The price category for guests. guest, -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/data_classes/settings/ReportCategory.dart b/app/lib/view_model/repository/data_classes/settings/ReportCategory.dart index 158d211d..83946876 100644 --- a/app/lib/view_model/repository/data_classes/settings/ReportCategory.dart +++ b/app/lib/view_model/repository/data_classes/settings/ReportCategory.dart @@ -2,14 +2,19 @@ enum ReportCategory { /// The image is offensive. offensive, + /// The image includes an advertisement. advert, + /// The image does not show a meal. noMeal, + /// The image shows the wrong meal. wrongMeal, + /// The image violates the rights of another person. violatesRights, + /// The report reason is not one of the other reasons. other -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/error_handling/NoMealException.dart b/app/lib/view_model/repository/error_handling/NoMealException.dart index 921ba686..e01d40a8 100644 --- a/app/lib/view_model/repository/error_handling/NoMealException.dart +++ b/app/lib/view_model/repository/error_handling/NoMealException.dart @@ -6,4 +6,4 @@ class NoMealException implements Exception { /// This constructor creates a new NoMealException with the given message. /// @param message The message of the exception. NoMealException(this.message); -} \ No newline at end of file +} diff --git a/app/lib/view_model/repository/error_handling/Result.dart b/app/lib/view_model/repository/error_handling/Result.dart index 8e7577ac..1c9b206c 100644 --- a/app/lib/view_model/repository/error_handling/Result.dart +++ b/app/lib/view_model/repository/error_handling/Result.dart @@ -26,4 +26,3 @@ final class Failure extends Result { /// The value of the failure. final E exception; } - diff --git a/app/lib/view_model/repository/interface/IDatabaseAccess.dart b/app/lib/view_model/repository/interface/IDatabaseAccess.dart index 7ff60a19..6de18438 100644 --- a/app/lib/view_model/repository/interface/IDatabaseAccess.dart +++ b/app/lib/view_model/repository/interface/IDatabaseAccess.dart @@ -17,7 +17,8 @@ abstract class IDatabaseAccess { /// @param date The date of the mealplan /// @param canteen The canteen of the mealplan /// @return The mealplan of the committed date of the committed canteen or an error - Future, MealPlanException>> getMealPlan(DateTime date, Canteen canteen); + Future, MealPlanException>> getMealPlan( + DateTime date, Canteen canteen); /// This method returns a favorite meal. /// @param id The id of the meal @@ -50,4 +51,4 @@ abstract class IDatabaseAccess { /// This method returns the canteen with the committed id. Future getCanteenById(String id); -} \ No newline at end of file +} From 1c5545d9a3ea60d58f578eba286427c73ca6a55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 13:10:16 +0200 Subject: [PATCH 173/184] documentation and formating logic --- .../logic/favorite/FavoriteMealAccess.dart | 11 +++++++---- .../logic/favorite/IFavoriteMealAccess.dart | 4 +--- app/lib/view_model/logic/image/IImageAccess.dart | 3 +-- app/lib/view_model/logic/image/ImageAccess.dart | 8 ++++++-- .../logic/meal/CombinedMealPlanAccess.dart | 12 ++++++++---- .../logic/preference/IPreferenceAccess.dart | 2 +- .../logic/preference/PreferenceAccess.dart | 4 ++++ 7 files changed, 28 insertions(+), 16 deletions(-) diff --git a/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart index 65bd8ab2..f383d375 100644 --- a/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart +++ b/app/lib/view_model/logic/favorite/FavoriteMealAccess.dart @@ -1,9 +1,9 @@ - import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/interface/IDatabaseAccess.dart'; import 'package:flutter/material.dart'; +/// This class is the interface for the access to the favorite meals data that are stored in the database. class FavoriteMealAccess extends ChangeNotifier implements IFavoriteMealAccess { final IDatabaseAccess _database; @@ -12,6 +12,9 @@ class FavoriteMealAccess extends ChangeNotifier implements IFavoriteMealAccess { // waits until _init() is finished initializing late Future _doneInitialization; + /// Stores the access to the database and loads the values that are stored there. + /// @param database The access to the database. + /// @return A new instance of the class. FavoriteMealAccess(this._database) { _doneInitialization = _init(); } @@ -43,7 +46,8 @@ class FavoriteMealAccess extends ChangeNotifier implements IFavoriteMealAccess { @override Future isFavoriteMeal(Meal meal) async { await _doneInitialization; - return Future.value(_favorites.map((favorite) => favorite.id).contains(meal.id)); + return Future.value( + _favorites.map((favorite) => favorite.id).contains(meal.id)); } @override @@ -59,5 +63,4 @@ class FavoriteMealAccess extends ChangeNotifier implements IFavoriteMealAccess { meal.deleteFavorite(); notifyListeners(); } - -} \ No newline at end of file +} diff --git a/app/lib/view_model/logic/favorite/IFavoriteMealAccess.dart b/app/lib/view_model/logic/favorite/IFavoriteMealAccess.dart index a07c6347..2b5e134a 100644 --- a/app/lib/view_model/logic/favorite/IFavoriteMealAccess.dart +++ b/app/lib/view_model/logic/favorite/IFavoriteMealAccess.dart @@ -1,11 +1,9 @@ - import 'package:flutter/material.dart'; import '../../repository/data_classes/meal/Meal.dart'; /// This class is the interface for the access to the favorite meals data. abstract class IFavoriteMealAccess with ChangeNotifier { - /// This method adds the committed meal to the favorite meals in the database. /// @param meal The meal that should be added /// @return The result of the update @@ -24,4 +22,4 @@ abstract class IFavoriteMealAccess with ChangeNotifier { /// This method returns the favorite meals from the database. /// @return The favorite meals or an error Future> getFavoriteMeals(); -} \ No newline at end of file +} diff --git a/app/lib/view_model/logic/image/IImageAccess.dart b/app/lib/view_model/logic/image/IImageAccess.dart index b4d102e0..5213ae92 100644 --- a/app/lib/view_model/logic/image/IImageAccess.dart +++ b/app/lib/view_model/logic/image/IImageAccess.dart @@ -1,4 +1,3 @@ - import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; import 'package:app/view_model/repository/data_classes/settings/ReportCategory.dart'; import 'package:flutter/material.dart'; @@ -54,4 +53,4 @@ abstract class IImageAccess with ChangeNotifier { /// @param context The context of the app used for displaying temporal messages. /// @return The string that should be displayed in a temporal message Future reportImage(ImageData image, ReportCategory reportReason); -} \ No newline at end of file +} diff --git a/app/lib/view_model/logic/image/ImageAccess.dart b/app/lib/view_model/logic/image/ImageAccess.dart index 658ba348..5efdf6a8 100644 --- a/app/lib/view_model/logic/image/ImageAccess.dart +++ b/app/lib/view_model/logic/image/ImageAccess.dart @@ -5,10 +5,13 @@ import 'package:app/view_model/repository/data_classes/settings/ReportCategory.d import 'package:app/view_model/repository/interface/IServerAccess.dart'; import 'package:flutter/material.dart'; -// todo string for error and success +/// This class is the interface for the access to the image data. The access can be done via server. class ImageAccess extends ChangeNotifier implements IImageAccess { final IServerAccess _api; + /// Stores the access to the server. + /// @param api The access to the server. + /// @return A new instance of the class. ImageAccess(this._api); @override @@ -63,7 +66,8 @@ class ImageAccess extends ChangeNotifier implements IImageAccess { } @override - Future reportImage(ImageData image, ReportCategory reportReason) async { + Future reportImage( + ImageData image, ReportCategory reportReason) async { final result = await _api.reportImage(image, reportReason); if (!result) { diff --git a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart index 83de2288..61585a42 100644 --- a/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart +++ b/app/lib/view_model/logic/meal/CombinedMealPlanAccess.dart @@ -15,6 +15,7 @@ import 'package:flutter/material.dart'; import '../../repository/data_classes/meal/Side.dart'; +/// This class is the interface for the access to the meal data. The access can be done via the database or the server. class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { final ILocalStorage _preferences; final IServerAccess _api; @@ -28,12 +29,16 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { bool _noDataYet = false; bool _activeFilter = true; - late PriceCategory _priceCategory; // waits until _init() is finished initializing late Future _doneInitialization; + /// Stores the access to the api and database and loads the meal plan. + /// @param preferences The access to the local storage. + /// @param api The access to the api. + /// @param database The access to the database. + /// @return A new instance of the class. CombinedMealPlanAccess(this._preferences, this._api, this._database) { _doneInitialization = _init(); } @@ -82,7 +87,8 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { // get meal plans form server List mealPlans = switch (await _api.updateAll()) { Success(value: final mealplan) => mealplan, - Failure(exception: final exception) => _convertMealPlanExceptionToMealPlan(exception) + Failure(exception: final exception) => + _convertMealPlanExceptionToMealPlan(exception) }; // update all if connection to server is successful @@ -216,7 +222,6 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { final mealPlan = await _getMealPlanFromServer(); - if (mealPlan.isEmpty) { return "snackbar.refreshMealPlanError"; } @@ -345,7 +350,6 @@ class CombinedMealPlanAccess extends ChangeNotifier implements IMealAccess { } else { await activateFilter(); } - } void _changeRatingOfMeal(Meal changedMeal, int rating) { diff --git a/app/lib/view_model/logic/preference/IPreferenceAccess.dart b/app/lib/view_model/logic/preference/IPreferenceAccess.dart index e4df0266..94da5ec7 100644 --- a/app/lib/view_model/logic/preference/IPreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/IPreferenceAccess.dart @@ -41,4 +41,4 @@ abstract class IPreferenceAccess with ChangeNotifier { /// @param format The new MealPlanFormat. /// @return The result of the update. Future setMealPlanFormat(MealPlanFormat format); -} \ No newline at end of file +} diff --git a/app/lib/view_model/logic/preference/PreferenceAccess.dart b/app/lib/view_model/logic/preference/PreferenceAccess.dart index 0e9ecc4c..b5f3123d 100644 --- a/app/lib/view_model/logic/preference/PreferenceAccess.dart +++ b/app/lib/view_model/logic/preference/PreferenceAccess.dart @@ -7,6 +7,7 @@ import 'package:app/view_model/repository/interface/ILocalStorage.dart'; import 'package:flutter/foundation.dart'; // todo muss vor Combined Meal Plan Access initialisiert werden +/// This class accesses preferences that are stored locally. class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { final ILocalStorage _access; @@ -15,6 +16,9 @@ class PreferenceAccess extends ChangeNotifier implements IPreferenceAccess { late PriceCategory _priceCategory; late MealPlanFormat _mealPlanFormat; + /// Stores the access to the local storage and loads the values that are stored therepreferencs. + /// @param access The access to the local storage. + /// @return A new instance of the class. PreferenceAccess(this._access) { _init(); } From c953f2128e57113d2d09238a5df41fe6fe5f5a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 13:18:33 +0200 Subject: [PATCH 174/184] make stateful --- app/lib/view/images/ImageReportDialog.dart | 80 ++++++++++++---------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/app/lib/view/images/ImageReportDialog.dart b/app/lib/view/images/ImageReportDialog.dart index 04bb8419..ab7476f2 100644 --- a/app/lib/view/images/ImageReportDialog.dart +++ b/app/lib/view/images/ImageReportDialog.dart @@ -10,11 +10,17 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; // TODO statefull machen -class ImageReportDialog extends StatelessWidget { - ReportCategory _reason = ReportCategory.other; - late final ImageData _image; +class ImageReportDialog extends StatefulWidget { + final ImageData _image; + + const ImageReportDialog({super.key, required ImageData image}) : _image = image; + + @override + State createState() => _ImageReportState(); +} - ImageReportDialog({super.key, required ImageData image}) : _image = image; +class _ImageReportState extends State { + ReportCategory _reason = ReportCategory.other; @override Widget build(BuildContext context) { @@ -22,13 +28,13 @@ class ImageReportDialog extends StatelessWidget { title: FlutterI18n.translate(context, "image.reportImageTitle"), content: Consumer( builder: (context, imageAccess, child) => Column( + children: [ + Text(FlutterI18n.translate( + context, "image.reportDescription")), + Row( children: [ - Text(FlutterI18n.translate( - context, "image.reportDescription")), - Row( - children: [ - Expanded( - child: MensaDropdown( + Expanded( + child: MensaDropdown( onChanged: (value) { if (value != null) { _reason = value; @@ -37,36 +43,36 @@ class ImageReportDialog extends StatelessWidget { value: _reason, items: _getReportCategoryEntries(context), )) - ], - ), - Row( - children: [ - const Spacer(), - MensaButton( - onPressed: () async { - final temporalMessage = - await imageAccess.reportImage(_image, _reason); - if (!context.mounted) return; - Navigator.pop(context); + ], + ), + Row( + children: [ + const Spacer(), + MensaButton( + onPressed: () async { + final temporalMessage = + await imageAccess.reportImage(widget._image, _reason); + if (!context.mounted) return; + Navigator.pop(context); - if (temporalMessage.isNotEmpty) { - final snackBar = SnackBar( - content: Text(FlutterI18n.translate( - context, temporalMessage)), - backgroundColor: - Theme.of(context).colorScheme.onError, - ); + if (temporalMessage.isNotEmpty) { + final snackBar = SnackBar( + content: Text(FlutterI18n.translate( + context, temporalMessage)), + backgroundColor: + Theme.of(context).colorScheme.onError, + ); - ScaffoldMessenger.of(context) - .showSnackBar(snackBar); - } - }, - text: FlutterI18n.translate( - context, "image.reportButton")), - ], - ) + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + } + }, + text: FlutterI18n.translate( + context, "image.reportButton")), ], - )), + ) + ], + )), ); } From ea3e5bc5cecb7426b29a932b2e6ee9b58e7f9d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 13:43:45 +0200 Subject: [PATCH 175/184] format and document part of view --- app/lib/view/core/MainPage.dart | 6 + app/lib/view/core/MensaAppBar.dart | 10 +- .../navigation/NavigationAddImageIcon.dart | 7 +- .../navigation/NavigationArrowDownIcon.dart | 7 +- .../navigation/NavigationArrowLeftIcon.dart | 7 +- .../navigation/NavigationArrowRightIcon.dart | 7 +- .../icons/navigation/NavigationBackIcon.dart | 7 +- .../navigation/NavigationFavoritesIcon.dart | 7 +- .../NavigationFilterOutlinedIcon.dart | 10 +- .../NavigationGridOutlinedIcon.dart | 7 +- .../NavigationListOutlinedIcon.dart | 7 +- .../navigation/NavigationMealPlanIcon.dart | 7 +- .../navigation/NavigationSettingsIcon.dart | 7 +- .../information_display/MealMainEntry.dart | 6 + .../information_display/MealPreviewImage.dart | 4 +- .../core/information_display/MealRating.dart | 1 - .../information_display/MealSideEntry.dart | 1 + .../core/input_components/MensaTextField.dart | 6 +- .../core/meal_view_format/MealGridEntry.dart | 5 +- .../core/meal_view_format/MealGridLine.dart | 3 +- .../core/meal_view_format/MealListLine.dart | 46 +++--- .../selection_components/MensaDropdown.dart | 8 +- .../selection_components/MensaToggle.dart | 3 +- app/lib/view/detail_view/DetailsPage.dart | 8 +- .../view/detail_view/MealRatingDialog.dart | 4 + .../view/detail_view/UploadImageDialog.dart | 6 +- app/lib/view/favorites/Favorites.dart | 122 +++++++------- app/lib/view/filter/FilterDialog.dart | 103 ++++++------ app/lib/view/filter/MensaButtonGroup.dart | 3 +- .../view/filter/MensaButtonGroupEntry.dart | 2 + .../view/filter/MensaFilterIconCheckbox.dart | 2 +- .../filter/MensaFilterIconCheckboxGroup.dart | 3 +- app/lib/view/filter/MensaSortSelect.dart | 28 +++- app/lib/view/filter/MensaSortSelectEntry.dart | 8 + app/lib/view/images/ImageReportDialog.dart | 75 +++++---- app/lib/view/mealplan/MealPlanDateSelect.dart | 11 +- app/lib/view/mealplan/MealPlanError.dart | 4 + app/lib/view/mealplan/MealPlanToolbar.dart | 12 +- app/lib/view/mealplan/MealPlanView.dart | 8 +- app/lib/view/mealplan/MensaCanteenSelect.dart | 7 + app/lib/view/settings/Settings.dart | 156 ++++++++++-------- .../view/settings/SettingsDropdownEntry.dart | 5 +- app/lib/view/settings/SettingsSection.dart | 12 +- 43 files changed, 466 insertions(+), 292 deletions(-) delete mode 100644 app/lib/view/core/information_display/MealRating.dart diff --git a/app/lib/view/core/MainPage.dart b/app/lib/view/core/MainPage.dart index daf74dfd..1f7f3839 100644 --- a/app/lib/view/core/MainPage.dart +++ b/app/lib/view/core/MainPage.dart @@ -7,7 +7,13 @@ import 'package:app/view/settings/Settings.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; +/// This widget is used to display the main page of the app. class MainPage extends StatefulWidget { + /// Creates a new MainPage widget. + /// @param key The key to identify this widget. + /// @returns A new MainPage widget. + const MainPage({super.key}); + @override State createState() => _MainPageState(); } diff --git a/app/lib/view/core/MensaAppBar.dart b/app/lib/view/core/MensaAppBar.dart index 5cc28462..51656b44 100644 --- a/app/lib/view/core/MensaAppBar.dart +++ b/app/lib/view/core/MensaAppBar.dart @@ -1,10 +1,17 @@ import 'package:flutter/material.dart'; +/// A custom AppBar that is used in the Mensa app. class MensaAppBar extends StatelessWidget implements PreferredSizeWidget { final PreferredSizeWidget? _bottom; final Widget _child; final double _appBarHeight; + /// Creates a new MensaAppBar. + /// @param key The key to identify this widget. + /// @param bottom The bottom widget. + /// @param appBarHeight The height of the AppBar. + /// @param child The child widget. + /// @returns A new MensaAppBar. MensaAppBar( {super.key, PreferredSizeWidget? bottom, @@ -32,8 +39,7 @@ class MensaAppBar extends StatelessWidget implements PreferredSizeWidget { class _PreferredAppBarSize extends Size { _PreferredAppBarSize(this.appBarHeight, this.bottomHeight) - : super.fromHeight( - appBarHeight + (bottomHeight ?? 0)); + : super.fromHeight(appBarHeight + (bottomHeight ?? 0)); final double appBarHeight; final double? bottomHeight; diff --git a/app/lib/view/core/icons/navigation/NavigationAddImageIcon.dart b/app/lib/view/core/icons/navigation/NavigationAddImageIcon.dart index ac01af59..3e3be0be 100644 --- a/app/lib/view/core/icons/navigation/NavigationAddImageIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationAddImageIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for an image upload class NavigationAddImageIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for an image upload + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [NavigationAddImageIcon] widget const NavigationAddImageIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/navigation/NavigationArrowDownIcon.dart b/app/lib/view/core/icons/navigation/NavigationArrowDownIcon.dart index e5b4bf28..3cb2f09d 100644 --- a/app/lib/view/core/icons/navigation/NavigationArrowDownIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationArrowDownIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for an arrow down class NavigationArrowDownIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for an arrow to the left + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [NavigationArrowDownIcon] widget const NavigationArrowDownIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/navigation/NavigationArrowLeftIcon.dart b/app/lib/view/core/icons/navigation/NavigationArrowLeftIcon.dart index 5620680d..3ffb1ad6 100644 --- a/app/lib/view/core/icons/navigation/NavigationArrowLeftIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationArrowLeftIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for an arrow to the left class NavigationArrowLeftIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for an arrow to the left + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [NavigationArrowLeftIcon] widget const NavigationArrowLeftIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/navigation/NavigationArrowRightIcon.dart b/app/lib/view/core/icons/navigation/NavigationArrowRightIcon.dart index 33006580..0e4bd405 100644 --- a/app/lib/view/core/icons/navigation/NavigationArrowRightIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationArrowRightIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for an arrow to the right class NavigationArrowRightIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for an arrow to the right + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [NavigationArrowRightIcon] widget const NavigationArrowRightIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/navigation/NavigationBackIcon.dart b/app/lib/view/core/icons/navigation/NavigationBackIcon.dart index 52c19751..d60043f3 100644 --- a/app/lib/view/core/icons/navigation/NavigationBackIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationBackIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for navigating back class NavigationBackIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for navigating back + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [NavigationBackIcon] widget const NavigationBackIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/navigation/NavigationFavoritesIcon.dart b/app/lib/view/core/icons/navigation/NavigationFavoritesIcon.dart index bd0ced56..c1a94429 100644 --- a/app/lib/view/core/icons/navigation/NavigationFavoritesIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationFavoritesIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for favorites class NavigationFavoritesIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for favorites + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [NavigationFavoritesIcon] widget const NavigationFavoritesIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart b/app/lib/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart index ab287e56..5b887353 100644 --- a/app/lib/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationFilterOutlinedIcon.dart @@ -1,12 +1,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for the filter class NavigationFilterOutlinedIcon extends StatelessWidget { final double? _size; final Color? _color; - const NavigationFilterOutlinedIcon({super.key, double size = 24, Color? color}) + /// This widget is used to display the icon for the filter + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [NavigationFilterOutlinedIcon] widget + const NavigationFilterOutlinedIcon( + {super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/navigation/NavigationGridOutlinedIcon.dart b/app/lib/view/core/icons/navigation/NavigationGridOutlinedIcon.dart index 62030d9b..c65ca1a9 100644 --- a/app/lib/view/core/icons/navigation/NavigationGridOutlinedIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationGridOutlinedIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for the grid class NavigationGridOutlinedIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for the grid + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [NavigationGridOutlinedIcon] widget const NavigationGridOutlinedIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/navigation/NavigationListOutlinedIcon.dart b/app/lib/view/core/icons/navigation/NavigationListOutlinedIcon.dart index 1ff1ff91..e07abb36 100644 --- a/app/lib/view/core/icons/navigation/NavigationListOutlinedIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationListOutlinedIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for the list class NavigationListOutlinedIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for the list + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [NavigationListOutlinedIcon] widget const NavigationListOutlinedIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/navigation/NavigationMealPlanIcon.dart b/app/lib/view/core/icons/navigation/NavigationMealPlanIcon.dart index 49475afd..5e040469 100644 --- a/app/lib/view/core/icons/navigation/NavigationMealPlanIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationMealPlanIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for the meal plan class NavigationMealPlanIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for the meal plan + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [NavigationMealPlanIcon] widget const NavigationMealPlanIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/navigation/NavigationSettingsIcon.dart b/app/lib/view/core/icons/navigation/NavigationSettingsIcon.dart index 95fa8f01..943c987e 100644 --- a/app/lib/view/core/icons/navigation/NavigationSettingsIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationSettingsIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for the settings class NavigationSettingsIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for the settings + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [NavigationSettingsIcon] widget const NavigationSettingsIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/information_display/MealMainEntry.dart b/app/lib/view/core/information_display/MealMainEntry.dart index 020919af..2025d75b 100644 --- a/app/lib/view/core/information_display/MealMainEntry.dart +++ b/app/lib/view/core/information_display/MealMainEntry.dart @@ -5,12 +5,18 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +/// Displays a Main Dish. class MealMainEntry extends StatelessWidget { final Meal _meal; + // TODO use locale final NumberFormat _priceFormat = NumberFormat.currency(locale: 'de_DE', symbol: '€'); + /// Creates a MealMainEntry. + /// @param meal The Meal to display. + /// @param key The key to use for this widget. + /// @return A MealMainEntry. MealMainEntry({Key? key, required Meal meal}) : _meal = meal, super(key: key); diff --git a/app/lib/view/core/information_display/MealPreviewImage.dart b/app/lib/view/core/information_display/MealPreviewImage.dart index 2f90f3ed..372cf7fa 100644 --- a/app/lib/view/core/information_display/MealPreviewImage.dart +++ b/app/lib/view/core/information_display/MealPreviewImage.dart @@ -41,7 +41,9 @@ class MealPreviewImage extends StatelessWidget { @override Widget build(BuildContext context) { - if (_meal.images == null || _meal.images!.isEmpty || _meal.images!.first.url.isEmpty) { + if (_meal.images == null || + _meal.images!.isEmpty || + _meal.images!.first.url.isEmpty) { return Container( width: _width, height: _height, diff --git a/app/lib/view/core/information_display/MealRating.dart b/app/lib/view/core/information_display/MealRating.dart deleted file mode 100644 index c0fdaeff..00000000 --- a/app/lib/view/core/information_display/MealRating.dart +++ /dev/null @@ -1 +0,0 @@ -// todo MealRating \ No newline at end of file diff --git a/app/lib/view/core/information_display/MealSideEntry.dart b/app/lib/view/core/information_display/MealSideEntry.dart index 584af73e..b1a2927c 100644 --- a/app/lib/view/core/information_display/MealSideEntry.dart +++ b/app/lib/view/core/information_display/MealSideEntry.dart @@ -8,6 +8,7 @@ import 'package:provider/provider.dart'; /// Displays a Side Dish. class MealSideEntry extends StatelessWidget { final Side _side; + // TODO use locale final NumberFormat _priceFormat = NumberFormat.currency(locale: 'de_DE', symbol: '€'); diff --git a/app/lib/view/core/input_components/MensaTextField.dart b/app/lib/view/core/input_components/MensaTextField.dart index 4299c2b3..76eec836 100644 --- a/app/lib/view/core/input_components/MensaTextField.dart +++ b/app/lib/view/core/input_components/MensaTextField.dart @@ -1,9 +1,13 @@ import 'package:flutter/material.dart'; +/// A text field that is used in the Mensa app. class MensaTextField extends StatelessWidget { - final TextEditingController _controller; + /// Creates a new MensaTextField. + /// @param key The key to identify this widget. + /// @param controller The controller that is used to control the text field. + /// @returns A new MensaTextField. const MensaTextField({super.key, required TextEditingController controller}) : _controller = controller; diff --git a/app/lib/view/core/meal_view_format/MealGridEntry.dart b/app/lib/view/core/meal_view_format/MealGridEntry.dart index 07236775..0b97829a 100644 --- a/app/lib/view/core/meal_view_format/MealGridEntry.dart +++ b/app/lib/view/core/meal_view_format/MealGridEntry.dart @@ -33,7 +33,10 @@ class MealGridEntry extends StatelessWidget { child: GestureDetector( onTap: () => { Navigator.of(context).push(MaterialPageRoute( - builder: (context) => DetailsPage(meal: _meal, line: _line,))) + builder: (context) => DetailsPage( + meal: _meal, + line: _line, + ))) }, child: Container( decoration: BoxDecoration( diff --git a/app/lib/view/core/meal_view_format/MealGridLine.dart b/app/lib/view/core/meal_view_format/MealGridLine.dart index d1463ed9..e63e8bc9 100644 --- a/app/lib/view/core/meal_view_format/MealGridLine.dart +++ b/app/lib/view/core/meal_view_format/MealGridLine.dart @@ -19,8 +19,7 @@ class MealGridLine extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), child: Text(_mealPlan.line.name, - style: const TextStyle( - fontWeight: FontWeight.bold, fontSize: 16)), + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), ), LayoutBuilder( builder: (context, constraints) => SingleChildScrollView( diff --git a/app/lib/view/core/meal_view_format/MealListLine.dart b/app/lib/view/core/meal_view_format/MealListLine.dart index 6fc41503..bb4df4f8 100644 --- a/app/lib/view/core/meal_view_format/MealListLine.dart +++ b/app/lib/view/core/meal_view_format/MealListLine.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; /// Displays the section for the MealList with all meals of a line. class MealListLine extends StatelessWidget { - final MealPlan _mealPlan; /// Creates a MealListLine. @@ -16,26 +15,27 @@ class MealListLine extends StatelessWidget { @override Widget build(BuildContext context) { - return ( - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), - child: Text(_mealPlan.line.name, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), - ), - ListView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: _mealPlan.meals.length, - itemBuilder: (context, index) { - return MealListEntry(meal: _mealPlan.meals[index], line: _mealPlan.line,); - }, - ), - ], - ) - ); + return (Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), + child: Text(_mealPlan.line.name, + style: + const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), + ), + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: _mealPlan.meals.length, + itemBuilder: (context, index) { + return MealListEntry( + meal: _mealPlan.meals[index], + line: _mealPlan.line, + ); + }, + ), + ], + )); } - - -} \ No newline at end of file +} diff --git a/app/lib/view/core/selection_components/MensaDropdown.dart b/app/lib/view/core/selection_components/MensaDropdown.dart index f2b41f8f..2879099d 100644 --- a/app/lib/view/core/selection_components/MensaDropdown.dart +++ b/app/lib/view/core/selection_components/MensaDropdown.dart @@ -31,16 +31,18 @@ class MensaDropdown extends StatelessWidget { /// @returns The widget. @override Widget build(BuildContext context) { - return Container( // Container is used to give the dropdown a background color. + return Container( + // Container is used to give the dropdown a background color. decoration: BoxDecoration( borderRadius: BorderRadius.circular(4.0), color: _backgroundColor ?? Theme.of(context).colorScheme.surface, ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: DropdownButtonHideUnderline( // DropdownButtonHideUnderline is used to hide the underline of the dropdown. + child: DropdownButtonHideUnderline( + // DropdownButtonHideUnderline is used to hide the underline of the dropdown. child: DropdownButton( - dropdownColor: Theme.of(context).colorScheme.surface, + dropdownColor: Theme.of(context).colorScheme.surface, elevation: 0, borderRadius: BorderRadius.circular(4.0), value: _value, diff --git a/app/lib/view/core/selection_components/MensaToggle.dart b/app/lib/view/core/selection_components/MensaToggle.dart index b6d63191..5673fbc2 100644 --- a/app/lib/view/core/selection_components/MensaToggle.dart +++ b/app/lib/view/core/selection_components/MensaToggle.dart @@ -29,8 +29,7 @@ class MensaToggle extends StatelessWidget { onTap: () { _onChanged!(!_value); }, - child: Text(_label, - style: TextStyle(fontSize: 16))), + child: Text(_label, style: TextStyle(fontSize: 16))), Spacer(), Switch(value: _value, onChanged: _onChanged), ], diff --git a/app/lib/view/detail_view/DetailsPage.dart b/app/lib/view/detail_view/DetailsPage.dart index 0443c1f3..039e39cf 100644 --- a/app/lib/view/detail_view/DetailsPage.dart +++ b/app/lib/view/detail_view/DetailsPage.dart @@ -22,11 +22,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; +/// This widget is used to display the details of a meal. class DetailsPage extends StatefulWidget { final Meal _meal; final Line? _line; - DetailsPage({super.key, required Meal meal, Line? line}) + /// Creates a new DetailsPage. + /// @param key The key to identify this widget. + /// @param meal The meal to display. + /// @param line The line of the meal. + /// @returns A new DetailsPage. + const DetailsPage({super.key, required Meal meal, Line? line}) : _meal = meal, _line = line; diff --git a/app/lib/view/detail_view/MealRatingDialog.dart b/app/lib/view/detail_view/MealRatingDialog.dart index facc18c1..b8b36600 100644 --- a/app/lib/view/detail_view/MealRatingDialog.dart +++ b/app/lib/view/detail_view/MealRatingDialog.dart @@ -7,9 +7,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; +/// This widget is used to display a dialog to rate a meal. class MealRatingDialog extends StatefulWidget { final Meal _meal; + /// Creates a new MealRatingDialog. + /// @param key The key to identify this widget. + /// @param meal The meal to rate. const MealRatingDialog({Key? key, required Meal meal}) : _meal = meal, super(key: key); diff --git a/app/lib/view/detail_view/UploadImageDialog.dart b/app/lib/view/detail_view/UploadImageDialog.dart index 36308102..0b9f28e2 100644 --- a/app/lib/view/detail_view/UploadImageDialog.dart +++ b/app/lib/view/detail_view/UploadImageDialog.dart @@ -9,11 +9,15 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:provider/provider.dart'; +/// This widget is used to display a dialog to upload an image. class UploadImageDialog extends StatelessWidget { final Meal _meal; - final TextEditingController _textFieldController = TextEditingController(); + /// Creates a new UploadImageDialog. + /// @param key The key to identify this widget. + /// @param meal The meal to upload an image for. + /// @returns A new UploadImageDialog. UploadImageDialog({Key? key, required Meal meal}) : _meal = meal, super(key: key); diff --git a/app/lib/view/favorites/Favorites.dart b/app/lib/view/favorites/Favorites.dart index 605831f9..63403fb6 100644 --- a/app/lib/view/favorites/Favorites.dart +++ b/app/lib/view/favorites/Favorites.dart @@ -5,73 +5,79 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; +/// This widget is used to display the favorites. class Favorites extends StatelessWidget { + /// Creates a new Favorites widget. + /// @param key The key to identify this widget. + /// @returns A new Favorites widget. const Favorites({super.key}); @override Widget build(BuildContext context) { return Consumer( - builder: (context, favoriteAccess, child) => FutureBuilder( - future: Future.wait([favoriteAccess.getFavoriteMeals()]), - builder: (context, snapshot) { - if (!snapshot.hasData || snapshot.hasError) { - print(snapshot.error); - return Scaffold( - appBar: MensaAppBar( - appBarHeight: kToolbarHeight, - child: Center(child: Text(FlutterI18n.translate( - context, "common.favorites"), style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), - )), - body: const Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [], - )); - } - - final mealPlan = snapshot.requireData[0]; - MensaAppBar appBar = MensaAppBar( + builder: (context, favoriteAccess, child) => FutureBuilder( + future: Future.wait([favoriteAccess.getFavoriteMeals()]), + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.hasError) { + print(snapshot.error); + return Scaffold( + appBar: MensaAppBar( appBarHeight: kToolbarHeight, - child: Center(child: Text( - FlutterI18n.translate(context, "common.favorites"), - style: const TextStyle( - fontSize: 20, fontWeight: FontWeight.bold), + child: Center( + child: Text( + FlutterI18n.translate(context, "common.favorites"), + style: const TextStyle( + fontSize: 20, fontWeight: FontWeight.bold)), )), - ); + body: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [], + )); + } + + final mealPlan = snapshot.requireData[0]; + MensaAppBar appBar = MensaAppBar( + appBarHeight: kToolbarHeight, + child: Center( + child: Text( + FlutterI18n.translate(context, "common.favorites"), + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + )), + ); - if (mealPlan.isEmpty) { - return Scaffold( - appBar: appBar, - body: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - FlutterI18n.translate( - context, "common.noFavorites"), - textAlign: TextAlign.center, - ) - ], - ), - )); - } + if (mealPlan.isEmpty) { + return Scaffold( + appBar: appBar, + body: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + FlutterI18n.translate(context, "common.noFavorites"), + textAlign: TextAlign.center, + ) + ], + ), + )); + } - return Scaffold( - appBar: appBar, - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ListView.builder( - shrinkWrap: true, - itemCount: mealPlan.length, - itemBuilder: (context, index) { - return MealListEntry(meal: mealPlan[index]); - }, - ) - ], - )); - }, - ), - ); + return Scaffold( + appBar: appBar, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.builder( + shrinkWrap: true, + itemCount: mealPlan.length, + itemBuilder: (context, index) { + return MealListEntry(meal: mealPlan[index]); + }, + ) + ], + )); + }, + ), + ); } } diff --git a/app/lib/view/filter/FilterDialog.dart b/app/lib/view/filter/FilterDialog.dart index d7818257..af37441a 100644 --- a/app/lib/view/filter/FilterDialog.dart +++ b/app/lib/view/filter/FilterDialog.dart @@ -23,18 +23,23 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; -// todo padding +/// This widget is used to display the filter dialog. class FilterDialog extends StatefulWidget { + /// Creates a new filter dialog. + /// @param key The key to identify this widget. + /// @return a widget that displays the filter dialog + const FilterDialog({super.key}); + @override State createState() => _FilterDialogState(); } class _FilterDialogState extends State { - FilterPreferences _preferences = FilterPreferences(); + FilterPreferences preferences = FilterPreferences(); String selectedSorting = "line"; SortDirection sortDirection = SortDirection.ascending; - final TextStyle _headingTextStyle = + final TextStyle headingTextStyle = const TextStyle(fontSize: 16, fontWeight: FontWeight.bold); @override @@ -59,7 +64,7 @@ class _FilterDialogState extends State { onPressed: () { context.read().resetFilterPreferences(); setState(() { - _preferences = FilterPreferences(); + preferences = FilterPreferences(); }); }, icon: const FilterRestoreIcon()), @@ -75,7 +80,7 @@ class _FilterDialogState extends State { if (filterPreferences.hasError) { return const Center(child: Text("Error")); } - _preferences = filterPreferences.requireData; + preferences = filterPreferences.requireData; return SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16), @@ -83,20 +88,20 @@ class _FilterDialogState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(FlutterI18n.translate(context, "filter.foodType"), - style: _headingTextStyle), + style: headingTextStyle), const SizedBox( height: 8, ), MensaButtonGroup( - value: _getValueCategory(_preferences.categories), + value: _getValueCategory(preferences.categories), onChanged: (value) { - _setValueCategory(value, _preferences); + _setValueCategory(value, preferences); setState(() { - _preferences = _preferences; + preferences = preferences; }); }, entries: _getAllFoodTypeEntries(context)), - _getValueCategory(_preferences.categories) == 0 + _getValueCategory(preferences.categories) == 0 ? Column( children: [ const SizedBox( @@ -105,66 +110,66 @@ class _FilterDialogState extends State { MensaCheckbox( label: FlutterI18n.translate( context, "filter.foodTypeSelectionBeef"), - value: _preferences.categories + value: preferences.categories .contains(FoodType.beef) || - _preferences.categories + preferences.categories .contains(FoodType.beefAw), onChanged: (value) { if (value) { - _preferences.categories + preferences.categories .add(FoodType.beef); - _preferences.categories + preferences.categories .add(FoodType.beefAw); } else { - _preferences.categories + preferences.categories .remove(FoodType.beef); - _preferences.categories + preferences.categories .remove(FoodType.beefAw); } setState(() { - _preferences = _preferences; + preferences = preferences; }); }, ), MensaCheckbox( label: FlutterI18n.translate( context, "filter.foodTypeSelectionPork"), - value: _preferences.categories + value: preferences.categories .contains(FoodType.pork) || - _preferences.categories + preferences.categories .contains(FoodType.porkAw), onChanged: (value) { if (value) { - _preferences.categories + preferences.categories .add(FoodType.pork); - _preferences.categories + preferences.categories .add(FoodType.porkAw); } else { - _preferences.categories + preferences.categories .remove(FoodType.pork); - _preferences.categories + preferences.categories .remove(FoodType.porkAw); } setState(() { - _preferences = _preferences; + preferences = preferences; }); }, ), MensaCheckbox( label: FlutterI18n.translate( context, "filter.foodTypeSelectionFish"), - value: _preferences.categories + value: preferences.categories .contains(FoodType.fish), onChanged: (value) { if (value) { - _preferences.categories + preferences.categories .add(FoodType.fish); } else { - _preferences.categories + preferences.categories .remove(FoodType.fish); } setState(() { - _preferences = _preferences; + preferences = preferences; }); }, ) @@ -176,7 +181,7 @@ class _FilterDialogState extends State { ), Text( FlutterI18n.translate(context, "filter.allergensTitle"), - style: _headingTextStyle, + style: headingTextStyle, ), const SizedBox( height: 8, @@ -185,11 +190,11 @@ class _FilterDialogState extends State { Expanded( child: MensaFilterIconCheckboxGroup( items: _getAllAllergen(context), - selectedValues: _preferences.allergens, + selectedValues: preferences.allergens, onChanged: (value) { - _preferences.allergens = value; + preferences.allergens = value; setState(() { - _preferences = _preferences; + preferences = preferences; }); })) ]), @@ -198,7 +203,7 @@ class _FilterDialogState extends State { ), Text( FlutterI18n.translate(context, "filter.priceTitle"), - style: _headingTextStyle, + style: headingTextStyle, ), const SizedBox( height: 8, @@ -214,17 +219,17 @@ class _FilterDialogState extends State { ), MensaSlider( onChanged: (value) { - _preferences.price = value.round(); + preferences.price = value.round(); setState(() { - _preferences = _preferences; + preferences = preferences; }); }, - value: _preferences.price.toDouble(), + value: preferences.price.toDouble(), min: 0, max: 1000), Text( FlutterI18n.translate(context, "filter.ratingTitle"), - style: _headingTextStyle, + style: headingTextStyle, ), const SizedBox( height: 8, @@ -240,12 +245,12 @@ class _FilterDialogState extends State { ), MensaSlider( onChanged: (value) { - _preferences.rating = value.round(); + preferences.rating = value.round(); setState(() { - _preferences = _preferences; + preferences = preferences; }); }, - value: _preferences.rating.toDouble(), + value: preferences.rating.toDouble(), min: 1, max: 5, ), @@ -254,12 +259,12 @@ class _FilterDialogState extends State { ), MensaToggle( onChanged: (value) { - _preferences.onlyFavorite = value; + preferences.onlyFavorite = value; setState(() { - _preferences = _preferences; + preferences = preferences; }); }, - value: _preferences.onlyFavorite, + value: preferences.onlyFavorite, label: FlutterI18n.translate( context, "filter.favoritesOnlyTitle")), const SizedBox( @@ -267,17 +272,17 @@ class _FilterDialogState extends State { ), Text( FlutterI18n.translate(context, "filter.frequencyTitle"), - style: _headingTextStyle, + style: headingTextStyle, ), const SizedBox( height: 8, ), MensaButtonGroup( - value: _getValueFrequency(_preferences.frequency), + value: _getValueFrequency(preferences.frequency), onChanged: (value) { - _setValueFrequency(value, _preferences); + _setValueFrequency(value, preferences); setState(() { - _preferences = _preferences; + preferences = preferences; }); }, entries: _getAllFrequencyEntries(context)), @@ -286,7 +291,7 @@ class _FilterDialogState extends State { ), Text( FlutterI18n.translate(context, "filter.sortByTitle"), - style: _headingTextStyle, + style: headingTextStyle, ), const SizedBox( height: 8, @@ -326,7 +331,7 @@ class _FilterDialogState extends State { onPressed: () { context .read() - .changeFilterPreferences(_preferences); + .changeFilterPreferences(preferences); Navigator.of(context).pop(); }, text: FlutterI18n.translate( diff --git a/app/lib/view/filter/MensaButtonGroup.dart b/app/lib/view/filter/MensaButtonGroup.dart index bf9b97d1..21af4f3d 100644 --- a/app/lib/view/filter/MensaButtonGroup.dart +++ b/app/lib/view/filter/MensaButtonGroup.dart @@ -31,7 +31,8 @@ class MensaButtonGroup extends StatelessWidget { borderRadius: BorderRadius.circular(4), border: Border.all( color: Theme.of(context).colorScheme.surface, width: 1)), - child: IntrinsicHeight(child: Row( + child: IntrinsicHeight( + child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: _entries .map((e) => e.build(context, e.value == _value, _onChanged)) diff --git a/app/lib/view/filter/MensaButtonGroupEntry.dart b/app/lib/view/filter/MensaButtonGroupEntry.dart index 805ba761..4bde922e 100644 --- a/app/lib/view/filter/MensaButtonGroupEntry.dart +++ b/app/lib/view/filter/MensaButtonGroupEntry.dart @@ -3,6 +3,8 @@ import 'package:flutter/material.dart'; /// This class represents a single entry in a MensaButtonGroup. class MensaButtonGroupEntry { final String _title; + + /// The value of the entry. final T value; /// Creates a new MensaButtonGroupEntry. diff --git a/app/lib/view/filter/MensaFilterIconCheckbox.dart b/app/lib/view/filter/MensaFilterIconCheckbox.dart index 77613f77..351013a6 100644 --- a/app/lib/view/filter/MensaFilterIconCheckbox.dart +++ b/app/lib/view/filter/MensaFilterIconCheckbox.dart @@ -23,7 +23,7 @@ class MensaFilterIconCheckbox { decoration: BoxDecoration( borderRadius: BorderRadius.circular(4.0), border: Border.all( - width: 2, + width: 2, color: active ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.surface), diff --git a/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart b/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart index 7c097b88..27ee8e3b 100644 --- a/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart +++ b/app/lib/view/filter/MensaFilterIconCheckboxGroup.dart @@ -26,7 +26,8 @@ class MensaFilterIconCheckboxGroup extends StatelessWidget { double width = MediaQuery.of(context).size.width; return Wrap( runAlignment: WrapAlignment.center, - spacing: ((width - ((width % 80) * 80)) / (width % 80 - 1) + 1).floorToDouble(), + spacing: ((width - ((width % 80) * 80)) / (width % 80 - 1) + 1) + .floorToDouble(), runSpacing: 8, children: _items .map((e) => e.build(context, _selectedValues.contains(e.value), () { diff --git a/app/lib/view/filter/MensaSortSelect.dart b/app/lib/view/filter/MensaSortSelect.dart index aa38dbe5..a7f0b9e0 100644 --- a/app/lib/view/filter/MensaSortSelect.dart +++ b/app/lib/view/filter/MensaSortSelect.dart @@ -6,6 +6,7 @@ import 'package:app/view/core/selection_components/MensaDropdownEntry.dart'; import 'package:app/view/filter/MensaSortSelectEntry.dart'; import 'package:flutter/material.dart'; +/// This widget is used to display a sort select. class MensaSortSelect extends StatelessWidget { final List> _entries; final T _selectedEntry; @@ -13,6 +14,14 @@ class MensaSortSelect extends StatelessWidget { final Function(T) _onEntrySelected; final Function(SortDirection) _onSortDirectionSelected; + /// Creates a new sort select. + /// @param key The key to identify this widget. + /// @param entries The entries to display. + /// @param selectedEntry The selected entry. + /// @param sortDirection The sort direction. + /// @param onEntrySelected The function to call when an entry is selected. + /// @param onSortDirectionSelected The function to call when the sort direction is selected. + /// @returns A new sort select. const MensaSortSelect( {super.key, required List> entries, @@ -30,15 +39,16 @@ class MensaSortSelect extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - Expanded(child: MensaDropdown( - onChanged: (v) => _onEntrySelected(v), - value: _selectedEntry, - items: _entries - .map((e) => MensaDropdownEntry( - value: e.value, - label: e.label, - )) - .toList())), + Expanded( + child: MensaDropdown( + onChanged: (v) => _onEntrySelected(v), + value: _selectedEntry, + items: _entries + .map((e) => MensaDropdownEntry( + value: e.value, + label: e.label, + )) + .toList())), const SizedBox( width: 8, ), diff --git a/app/lib/view/filter/MensaSortSelectEntry.dart b/app/lib/view/filter/MensaSortSelectEntry.dart index 66847690..7c861ee9 100644 --- a/app/lib/view/filter/MensaSortSelectEntry.dart +++ b/app/lib/view/filter/MensaSortSelectEntry.dart @@ -1,6 +1,14 @@ +/// This class represents a single entry in the sort select. class MensaSortSelectEntry { + /// The value of the entry. final T value; + + /// The label of the entry. final String label; + /// Creates a new sort select entry. + /// @param value The value of the entry. + /// @param label The label of the entry. + /// @returns A new sort select entry. const MensaSortSelectEntry({required this.value, required this.label}); } diff --git a/app/lib/view/images/ImageReportDialog.dart b/app/lib/view/images/ImageReportDialog.dart index ab7476f2..c922cf3f 100644 --- a/app/lib/view/images/ImageReportDialog.dart +++ b/app/lib/view/images/ImageReportDialog.dart @@ -9,11 +9,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; -// TODO statefull machen +/// This widget is used to display the report dialog for an image. class ImageReportDialog extends StatefulWidget { final ImageData _image; - const ImageReportDialog({super.key, required ImageData image}) : _image = image; + /// Creates a new image report dialog. + /// @param key The key to identify this widget. + /// @param image The image to report. + /// @return a widget that displays the report dialog for an image + const ImageReportDialog({super.key, required ImageData image}) + : _image = image; @override State createState() => _ImageReportState(); @@ -28,13 +33,13 @@ class _ImageReportState extends State { title: FlutterI18n.translate(context, "image.reportImageTitle"), content: Consumer( builder: (context, imageAccess, child) => Column( - children: [ - Text(FlutterI18n.translate( - context, "image.reportDescription")), - Row( children: [ - Expanded( - child: MensaDropdown( + Text(FlutterI18n.translate( + context, "image.reportDescription")), + Row( + children: [ + Expanded( + child: MensaDropdown( onChanged: (value) { if (value != null) { _reason = value; @@ -43,36 +48,36 @@ class _ImageReportState extends State { value: _reason, items: _getReportCategoryEntries(context), )) - ], - ), - Row( - children: [ - const Spacer(), - MensaButton( - onPressed: () async { - final temporalMessage = - await imageAccess.reportImage(widget._image, _reason); - if (!context.mounted) return; - Navigator.pop(context); + ], + ), + Row( + children: [ + const Spacer(), + MensaButton( + onPressed: () async { + final temporalMessage = await imageAccess + .reportImage(widget._image, _reason); + if (!context.mounted) return; + Navigator.pop(context); - if (temporalMessage.isNotEmpty) { - final snackBar = SnackBar( - content: Text(FlutterI18n.translate( - context, temporalMessage)), - backgroundColor: - Theme.of(context).colorScheme.onError, - ); + if (temporalMessage.isNotEmpty) { + final snackBar = SnackBar( + content: Text(FlutterI18n.translate( + context, temporalMessage)), + backgroundColor: + Theme.of(context).colorScheme.onError, + ); - ScaffoldMessenger.of(context) - .showSnackBar(snackBar); - } - }, - text: FlutterI18n.translate( - context, "image.reportButton")), + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + } + }, + text: FlutterI18n.translate( + context, "image.reportButton")), + ], + ) ], - ) - ], - )), + )), ); } diff --git a/app/lib/view/mealplan/MealPlanDateSelect.dart b/app/lib/view/mealplan/MealPlanDateSelect.dart index 205f2134..b7e90841 100644 --- a/app/lib/view/mealplan/MealPlanDateSelect.dart +++ b/app/lib/view/mealplan/MealPlanDateSelect.dart @@ -5,11 +5,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:intl/intl.dart'; +/// This widget is used to select a date for the meal plan. class MealPlanDateSelect extends StatelessWidget { final DateTime _date; final Function(DateTime) _onDateChanged; - MealPlanDateSelect( + /// Creates a new meal plan date select. + /// @param key The key to identify this widget. + /// @param date The date to display. + /// @param onDateChanged The function to call when the date changes. + /// @returns A new meal plan date select. + const MealPlanDateSelect( {super.key, required DateTime date, required Function(DateTime) onDateChanged}) @@ -18,7 +24,8 @@ class MealPlanDateSelect extends StatelessWidget { @override Widget build(BuildContext context) { - DateFormat dateFormat = DateFormat('E dd.MM.yyyy', FlutterI18n.currentLocale(context)?.languageCode); + DateFormat dateFormat = DateFormat( + 'E dd.MM.yyyy', FlutterI18n.currentLocale(context)?.languageCode); return Row(children: [ MensaTapable( child: const Padding( diff --git a/app/lib/view/mealplan/MealPlanError.dart b/app/lib/view/mealplan/MealPlanError.dart index 5adc42a3..88216bc1 100644 --- a/app/lib/view/mealplan/MealPlanError.dart +++ b/app/lib/view/mealplan/MealPlanError.dart @@ -5,7 +5,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:provider/provider.dart'; +/// This widget is used to display the exception for no connection to the server. class MealPlanError extends StatelessWidget { + /// Creates a no connection widget. + /// @param key The key to identify this widget. + /// @return a widget that displays the exception for no connection to the server const MealPlanError({super.key}); @override diff --git a/app/lib/view/mealplan/MealPlanToolbar.dart b/app/lib/view/mealplan/MealPlanToolbar.dart index 03bb65c3..de5d8053 100644 --- a/app/lib/view/mealplan/MealPlanToolbar.dart +++ b/app/lib/view/mealplan/MealPlanToolbar.dart @@ -1,10 +1,17 @@ import 'package:flutter/material.dart'; +/// This class represents the toolbar of the meal plan. class MealPlanToolbar extends StatelessWidget implements PreferredSizeWidget { final Widget _child; final double _toolBarHeight; - MealPlanToolbar({super.key, double toolBarHeight = kToolbarHeight, required Widget child}) + /// Creates a new meal plan toolbar. + /// @param key The key to identify this widget. + /// @param toolBarHeight The height of the toolbar. + /// @param child The child of the toolbar. + /// @returns A new meal plan toolbar. + MealPlanToolbar( + {super.key, double toolBarHeight = kToolbarHeight, required Widget child}) : _toolBarHeight = toolBarHeight, preferredSize = _PreferredAppBarSize(toolBarHeight), _child = child; @@ -23,8 +30,7 @@ class MealPlanToolbar extends StatelessWidget implements PreferredSizeWidget { } class _PreferredAppBarSize extends Size { - _PreferredAppBarSize(this.appBarHeight) - : super.fromHeight(appBarHeight); + _PreferredAppBarSize(this.appBarHeight) : super.fromHeight(appBarHeight); final double appBarHeight; } diff --git a/app/lib/view/mealplan/MealPlanView.dart b/app/lib/view/mealplan/MealPlanView.dart index 83921a95..98c9ebd7 100644 --- a/app/lib/view/mealplan/MealPlanView.dart +++ b/app/lib/view/mealplan/MealPlanView.dart @@ -24,7 +24,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:provider/provider.dart'; +/// This class is the view for the meal plan. class MealPlanView extends StatelessWidget { + /// Creates a new meal plan view. + /// @param key The key to identify this widget. + /// @returns A new meal plan view. const MealPlanView({super.key}); @override @@ -56,7 +60,9 @@ class MealPlanView extends StatelessWidget { Result, MealPlanException> mealPlans = snapshot.requireData[3] as Result, MealPlanException>; - if (availableCanteens.indexWhere((element) => element.id == selectedCanteen.id) == -1) { + if (availableCanteens.indexWhere( + (element) => element.id == selectedCanteen.id) == + -1) { mealAccess.changeCanteen(availableCanteens[0]); } return Scaffold( diff --git a/app/lib/view/mealplan/MensaCanteenSelect.dart b/app/lib/view/mealplan/MensaCanteenSelect.dart index 372fd174..5e4dc183 100644 --- a/app/lib/view/mealplan/MensaCanteenSelect.dart +++ b/app/lib/view/mealplan/MensaCanteenSelect.dart @@ -2,11 +2,18 @@ import 'package:app/view/core/icons/navigation/NavigationArrowDownIcon.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Canteen.dart'; import 'package:flutter/material.dart'; +/// This class is the widget for selecting a canteen. class MensaCanteenSelect extends StatelessWidget { final List _availableCanteens; final Canteen _selectedCanteen; final Function(Canteen) _onCanteenSelected; + /// Creates a new MensaCanteenSelect. + /// @param key The key to identify this widget. + /// @param availableCanteens The canteens that can be selected. + /// @param selectedCanteen The canteen that is currently selected. + /// @param onCanteenSelected The function that is called when the canteen changes. + /// @returns A new MensaCanteenSelect. const MensaCanteenSelect( {super.key, required List availableCanteens, diff --git a/app/lib/view/settings/Settings.dart b/app/lib/view/settings/Settings.dart index bb2d2231..d3ed158d 100644 --- a/app/lib/view/settings/Settings.dart +++ b/app/lib/view/settings/Settings.dart @@ -13,7 +13,11 @@ import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; +/// This class is the settings page of the mensa app. class Settings extends StatelessWidget { + /// Creates a new settings page. + /// @param key The key to identify this widget. + /// @returns A new settings page. Settings({super.key}) { WidgetsFlutterBinding.ensureInitialized(); } @@ -27,99 +31,109 @@ class Settings extends StatelessWidget { child: Center( child: Text( FlutterI18n.translate(context, "common.settings"), - style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 20, fontWeight: FontWeight.bold), )), ), body: SingleChildScrollView( - child: Padding(padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Column(children: [ - SettingsDropdownEntry( - onChanged: (value) { - if (value != null && - value != storage.getColorScheme()) { - storage.setColorScheme(value); - } - }, - value: storage.getColorScheme(), - items: _getColorSchemeEntries(context), - heading: "settings.colorScheme"), - const SizedBox(height: 16), - SettingsDropdownEntry( - onChanged: (value) { - if (value != null && - value != storage.getPriceCategory()) { - storage.setPriceCategory(value); - } - }, - value: storage.getPriceCategory(), - items: _getPriceCategoryEntries(context), - heading: "settings.priceCategory"), - const SizedBox(height: 16), - SettingsSection(heading: "settings.about", children: [ - Row( - children: [ - Text( - FlutterI18n.translate(context, "settings.version")), - const Spacer(), - FutureBuilder( - future: Future.wait([PackageInfo.fromPlatform()]), - builder: (context, snapshot) { - if (!snapshot.hasData || snapshot.hasError) { - return const Text("42.3.141"); - } - final PackageInfo info = snapshot.requireData[0]; - return Text(info.version); - }) - ], - ), - const SizedBox(height: 8), - Text(FlutterI18n.translate(context, "settings.licence")), - const SizedBox(height: 8), - Row( - children: [ - Expanded( - child: MensaLink( - onPressed: () => _launchUrl(Uri.parse( - 'https://github.com/kronos-et-al/MensaApp')), - text: FlutterI18n.translate( - context, "settings.gitHubLink")), - ) - ], - ) - ]), - const SizedBox(height: 16), - SettingsSection( - heading: "settings.legalInformation", - children: [ + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Column(children: [ + SettingsDropdownEntry( + onChanged: (value) { + if (value != null && + value != storage.getColorScheme()) { + storage.setColorScheme(value); + } + }, + value: storage.getColorScheme(), + items: _getColorSchemeEntries(context), + heading: "settings.colorScheme"), + const SizedBox(height: 16), + SettingsDropdownEntry( + onChanged: (value) { + if (value != null && + value != storage.getPriceCategory()) { + storage.setPriceCategory(value); + } + }, + value: storage.getPriceCategory(), + items: _getPriceCategoryEntries(context), + heading: "settings.priceCategory"), + const SizedBox(height: 16), + SettingsSection(heading: "settings.about", children: [ Row( children: [ - Expanded( - child: MensaLink( - onPressed: () => _launchUrl(Uri.parse( - 'https://docs.flutter.io/flutter/services/UrlLauncher-class.html')), - text: FlutterI18n.translate( - context, "settings.privacyPolicy")), - ) + Text(FlutterI18n.translate( + context, "settings.version")), + const Spacer(), + FutureBuilder( + future: + Future.wait([PackageInfo.fromPlatform()]), + builder: (context, snapshot) { + if (!snapshot.hasData || snapshot.hasError) { + return const Text("42.3.141"); + } + final PackageInfo info = + snapshot.requireData[0]; + return Text(info.version); + }) ], ), const SizedBox(height: 8), + Text( + FlutterI18n.translate(context, "settings.licence")), + const SizedBox(height: 8), Row( children: [ Expanded( child: MensaLink( onPressed: () => _launchUrl(Uri.parse( - 'https://docs.flutter.io/flutter/services/UrlLauncher-class.html')), + 'https://github.com/kronos-et-al/MensaApp')), text: FlutterI18n.translate( - context, "settings.contactDetails")), + context, "settings.gitHubLink")), ) ], ) - ]) - ])), + ]), + const SizedBox(height: 16), + SettingsSection( + heading: "settings.legalInformation", + children: [ + Row( + children: [ + Expanded( + child: MensaLink( + onPressed: () => _launchUrl(Uri.parse( + 'https://docs.flutter.io/flutter/services/UrlLauncher-class.html')), + text: FlutterI18n.translate( + context, "settings.privacyPolicy")), + ) + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: MensaLink( + onPressed: () => _launchUrl(Uri.parse( + 'https://docs.flutter.io/flutter/services/UrlLauncher-class.html')), + text: FlutterI18n.translate( + context, "settings.contactDetails")), + ) + ], + ) + ]) + ])), ), )); } + /// Launches the given url. + /// @param url The url to launch. Future _launchUrl(Uri url) async { + // todo: throw Exception is not that good if (!await launchUrl(url, mode: LaunchMode.externalApplication)) { throw Exception('Could not launch $url'); } diff --git a/app/lib/view/settings/SettingsDropdownEntry.dart b/app/lib/view/settings/SettingsDropdownEntry.dart index d49ab6df..98b0dfcf 100644 --- a/app/lib/view/settings/SettingsDropdownEntry.dart +++ b/app/lib/view/settings/SettingsDropdownEntry.dart @@ -38,13 +38,12 @@ class SettingsDropdownEntry extends StatelessWidget { children: [ Text( FlutterI18n.translate(context, _heading), - style: const TextStyle( - fontSize: 16, fontWeight: FontWeight.bold), + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Row(children: [ Expanded( - child: MensaDropdown( + child: MensaDropdown( backgroundColor: Theme.of(context).colorScheme.surface, onChanged: _onChanged, value: _value, diff --git a/app/lib/view/settings/SettingsSection.dart b/app/lib/view/settings/SettingsSection.dart index d57021ba..4a776490 100644 --- a/app/lib/view/settings/SettingsSection.dart +++ b/app/lib/view/settings/SettingsSection.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; +/// This class represents a section in the settings. class SettingsSection extends StatelessWidget { final String _heading; final List _children; @@ -10,9 +11,7 @@ class SettingsSection extends StatelessWidget { /// @param heading The heading of the section. /// @param children The children of the section. /// @returns A new SettingsSection. - const SettingsSection({super.key, - required heading, - required children}) + const SettingsSection({super.key, required heading, required children}) : _heading = heading, _children = children; @@ -23,14 +22,11 @@ class SettingsSection extends StatelessWidget { children: [ Text( FlutterI18n.translate(context, _heading), - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold), + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), ..._children ], ); } - -} \ No newline at end of file +} From 8741400a4349d1d469760ec85203043f89481631 Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Fri, 28 Jul 2023 13:55:51 +0200 Subject: [PATCH 176/184] added MealImageDialog --- app/assets/icons/image/image_report.svg | 1 + app/assets/icons/image/thumb_down_filled.svg | 1 + .../icons/image/thumb_down_outlined.svg | 1 + app/assets/icons/image/thumb_up_filled.svg | 1 + app/assets/icons/image/thumb_up_outlined.svg | 1 + app/assets/icons/navigation/nav_close.svg | 1 + app/lib/main.dart | 36 ------ .../core/icons/image/ImageReportIcon.dart | 23 ++++ .../core/icons/image/ThumbDownFilledIcon.dart | 23 ++++ .../icons/image/ThumbDownOutlinedIcon.dart | 23 ++++ .../core/icons/image/ThumbUpFilledIcon.dart | 23 ++++ .../core/icons/image/ThumbUpOutlinedIcon.dart | 23 ++++ .../icons/navigation/NavigationCloseIcon.dart | 23 ++++ .../information_display/MealPreviewImage.dart | 65 ++++++----- .../core/meal_view_format/MealGridEntry.dart | 12 +- .../core/meal_view_format/MealListEntry.dart | 4 + app/lib/view/detail_view/DetailsPage.dart | 53 +++++---- app/lib/view/images/MealImageDialog.dart | 105 ++++++++++++++++++ 18 files changed, 336 insertions(+), 83 deletions(-) create mode 100644 app/assets/icons/image/image_report.svg create mode 100644 app/assets/icons/image/thumb_down_filled.svg create mode 100644 app/assets/icons/image/thumb_down_outlined.svg create mode 100644 app/assets/icons/image/thumb_up_filled.svg create mode 100644 app/assets/icons/image/thumb_up_outlined.svg create mode 100644 app/assets/icons/navigation/nav_close.svg create mode 100644 app/lib/view/core/icons/image/ImageReportIcon.dart create mode 100644 app/lib/view/core/icons/image/ThumbDownFilledIcon.dart create mode 100644 app/lib/view/core/icons/image/ThumbDownOutlinedIcon.dart create mode 100644 app/lib/view/core/icons/image/ThumbUpFilledIcon.dart create mode 100644 app/lib/view/core/icons/image/ThumbUpOutlinedIcon.dart create mode 100644 app/lib/view/core/icons/navigation/NavigationCloseIcon.dart diff --git a/app/assets/icons/image/image_report.svg b/app/assets/icons/image/image_report.svg new file mode 100644 index 00000000..e83a036b --- /dev/null +++ b/app/assets/icons/image/image_report.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/image/thumb_down_filled.svg b/app/assets/icons/image/thumb_down_filled.svg new file mode 100644 index 00000000..013850f6 --- /dev/null +++ b/app/assets/icons/image/thumb_down_filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/image/thumb_down_outlined.svg b/app/assets/icons/image/thumb_down_outlined.svg new file mode 100644 index 00000000..4702ffd3 --- /dev/null +++ b/app/assets/icons/image/thumb_down_outlined.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/image/thumb_up_filled.svg b/app/assets/icons/image/thumb_up_filled.svg new file mode 100644 index 00000000..ffaa96c4 --- /dev/null +++ b/app/assets/icons/image/thumb_up_filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/image/thumb_up_outlined.svg b/app/assets/icons/image/thumb_up_outlined.svg new file mode 100644 index 00000000..2b38c870 --- /dev/null +++ b/app/assets/icons/image/thumb_up_outlined.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/icons/navigation/nav_close.svg b/app/assets/icons/navigation/nav_close.svg new file mode 100644 index 00000000..01e5ca96 --- /dev/null +++ b/app/assets/icons/navigation/nav_close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/lib/main.dart b/app/lib/main.dart index 76004b32..c0b7088e 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -3,7 +3,6 @@ import 'package:app/model/api_server/config.dart'; import 'package:app/model/database/SQLiteDatabaseAccess.dart'; import 'package:app/model/local_storage/SharedPreferenceAccess.dart'; import 'package:app/view/core/MainPage.dart'; -import 'package:app/view/mealplan/MealPlanView.dart'; import 'package:app/view_model/logic/favorite/FavoriteMealAccess.dart'; import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; import 'package:app/view_model/logic/image/IImageAccess.dart'; @@ -153,38 +152,3 @@ class MensaApp extends StatelessWidget { }); } } - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Container( - color: Theme.of(context).colorScheme.background, - child: SafeArea( - child: Scaffold( - backgroundColor: Theme.of(context).colorScheme.background, - body: MealPlanView(), - ))); - } -} diff --git a/app/lib/view/core/icons/image/ImageReportIcon.dart b/app/lib/view/core/icons/image/ImageReportIcon.dart new file mode 100644 index 00000000..67644512 --- /dev/null +++ b/app/lib/view/core/icons/image/ImageReportIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class ImageReportIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const ImageReportIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/image/image_report.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/image/ThumbDownFilledIcon.dart b/app/lib/view/core/icons/image/ThumbDownFilledIcon.dart new file mode 100644 index 00000000..46928640 --- /dev/null +++ b/app/lib/view/core/icons/image/ThumbDownFilledIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class ThumbDownFilledIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const ThumbDownFilledIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/image/thumb_down_filled.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/image/ThumbDownOutlinedIcon.dart b/app/lib/view/core/icons/image/ThumbDownOutlinedIcon.dart new file mode 100644 index 00000000..9f3b659b --- /dev/null +++ b/app/lib/view/core/icons/image/ThumbDownOutlinedIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class ThumbDownOutlinedIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const ThumbDownOutlinedIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/image/thumb_down_outlined.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/image/ThumbUpFilledIcon.dart b/app/lib/view/core/icons/image/ThumbUpFilledIcon.dart new file mode 100644 index 00000000..a6d795e9 --- /dev/null +++ b/app/lib/view/core/icons/image/ThumbUpFilledIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class ThumbUpFilledIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const ThumbUpFilledIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/image/thumb_up_filled.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/image/ThumbUpOutlinedIcon.dart b/app/lib/view/core/icons/image/ThumbUpOutlinedIcon.dart new file mode 100644 index 00000000..373108d7 --- /dev/null +++ b/app/lib/view/core/icons/image/ThumbUpOutlinedIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class ThumbUpOutlinedIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const ThumbUpOutlinedIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/image/thumb_up_outlined.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/icons/navigation/NavigationCloseIcon.dart b/app/lib/view/core/icons/navigation/NavigationCloseIcon.dart new file mode 100644 index 00000000..af690f10 --- /dev/null +++ b/app/lib/view/core/icons/navigation/NavigationCloseIcon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// This widget is used to display the icon for Wheat +class NavigationCloseIcon extends StatelessWidget { + final double? _size; + final Color? _color; + + const NavigationCloseIcon({super.key, double size = 24, Color? color}) + : _size = size, + _color = color; + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'assets/icons/navigation/nav_close.svg', + width: _size, + height: _size, + colorFilter: ColorFilter.mode( + _color ?? Theme.of(context).colorScheme.onSurface, BlendMode.srcIn), + ); + } +} diff --git a/app/lib/view/core/information_display/MealPreviewImage.dart b/app/lib/view/core/information_display/MealPreviewImage.dart index 2f90f3ed..0c72d2d3 100644 --- a/app/lib/view/core/information_display/MealPreviewImage.dart +++ b/app/lib/view/core/information_display/MealPreviewImage.dart @@ -1,4 +1,5 @@ import 'package:app/view/core/buttons/MensaButton.dart'; +import 'package:app/view/images/MealImageDialog.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:flutter/material.dart'; @@ -11,6 +12,7 @@ class MealPreviewImage extends StatelessWidget { final double? _height; final double? _width; final Function? _onUploadButtonPressed; + final void Function()? _onImagePressed; /// Creates a MealPreviewImage. /// @param meal The Meal to display. @@ -20,6 +22,8 @@ class MealPreviewImage extends StatelessWidget { /// @param height The height of the image. /// @param width The width of the image. /// @param key The key to use for this widget. + /// @param onUploadButtonPressed The function to call when the upload button is pressed. + /// @param onImagePressed The function to call when the image is pressed. /// @return A MealPreviewImage. const MealPreviewImage( {super.key, @@ -30,18 +34,22 @@ class MealPreviewImage extends StatelessWidget { BorderRadius? borderRadius, double? height, double? width, - Function? onUploadButtonPressed}) + Function? onUploadButtonPressed, + void Function()? onImagePressed}) : _meal = meal, _enableUploadButton = enableUploadButton, _enableFavoriteButton = enableFavoriteButton, _borderRadius = borderRadius ?? const BorderRadius.all(Radius.zero), _height = height, _width = width, - _onUploadButtonPressed = onUploadButtonPressed; + _onUploadButtonPressed = onUploadButtonPressed, + _onImagePressed = onImagePressed; @override Widget build(BuildContext context) { - if (_meal.images == null || _meal.images!.isEmpty || _meal.images!.first.url.isEmpty) { + if (_meal.images == null || + _meal.images!.isEmpty || + _meal.images!.first.url.isEmpty) { return Container( width: _width, height: _height, @@ -83,29 +91,34 @@ class MealPreviewImage extends StatelessWidget { color: Theme.of(context).colorScheme.secondary))), ]))); } else { - return Container( - width: _width, - height: _height, - decoration: BoxDecoration( - borderRadius: _borderRadius, - image: DecorationImage( - image: NetworkImage(_meal.images!.first.url), - fit: BoxFit.cover, - ), - ), - child: Stack(children: [ - if (_enableFavoriteButton && _meal.isFavorite) - Align( - alignment: const Alignment(1.0, -1.0), - child: Padding( - padding: const EdgeInsets.all(4), - child: Icon( - _meal.isFavorite - ? Icons.favorite - : Icons.favorite_border, - size: 24, - color: Theme.of(context).colorScheme.secondary))), - ])); + return Material( + child: InkWell( + onTap: _onImagePressed, + child: Container( + width: _width, + height: _height, + decoration: BoxDecoration( + borderRadius: _borderRadius, + image: DecorationImage( + image: NetworkImage(_meal.images!.first.url), + fit: BoxFit.cover, + ), + ), + child: Stack(children: [ + if (_enableFavoriteButton && _meal.isFavorite) + Align( + alignment: const Alignment(1.0, -1.0), + child: Padding( + padding: const EdgeInsets.all(4), + child: Icon( + _meal.isFavorite + ? Icons.favorite + : Icons.favorite_border, + size: 24, + color: Theme.of(context) + .colorScheme + .secondary))), + ])))); } } } diff --git a/app/lib/view/core/meal_view_format/MealGridEntry.dart b/app/lib/view/core/meal_view_format/MealGridEntry.dart index 07236775..02806e1d 100644 --- a/app/lib/view/core/meal_view_format/MealGridEntry.dart +++ b/app/lib/view/core/meal_view_format/MealGridEntry.dart @@ -33,7 +33,10 @@ class MealGridEntry extends StatelessWidget { child: GestureDetector( onTap: () => { Navigator.of(context).push(MaterialPageRoute( - builder: (context) => DetailsPage(meal: _meal, line: _line,))) + builder: (context) => DetailsPage( + meal: _meal, + line: _line, + ))) }, child: Container( decoration: BoxDecoration( @@ -50,6 +53,13 @@ class MealGridEntry extends StatelessWidget { meal: _meal, height: 180, displayFavorite: true, + onImagePressed: () => { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => DetailsPage( + meal: _meal, + line: _line, + ))) + }, borderRadius: const BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8))), diff --git a/app/lib/view/core/meal_view_format/MealListEntry.dart b/app/lib/view/core/meal_view_format/MealListEntry.dart index 2d7ce145..f4e9f587 100644 --- a/app/lib/view/core/meal_view_format/MealListEntry.dart +++ b/app/lib/view/core/meal_view_format/MealListEntry.dart @@ -51,6 +51,10 @@ class MealListEntry extends StatelessWidget { meal: _meal, height: 86, width: 86, + onImagePressed: () => { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => DetailsPage(meal: _meal, line: _line,))) + }, displayFavorite: true, borderRadius: const BorderRadius.only( topLeft: Radius.circular(8), diff --git a/app/lib/view/detail_view/DetailsPage.dart b/app/lib/view/detail_view/DetailsPage.dart index 0443c1f3..db9377d1 100644 --- a/app/lib/view/detail_view/DetailsPage.dart +++ b/app/lib/view/detail_view/DetailsPage.dart @@ -15,6 +15,7 @@ import 'package:app/view/detail_view/MealAccordionInfo.dart'; import 'package:app/view/detail_view/MealRatingDialog.dart'; import 'package:app/view/detail_view/RatingsOverview.dart'; import 'package:app/view/detail_view/UploadImageDialog.dart'; +import 'package:app/view/images/MealImageDialog.dart'; import 'package:app/view_model/logic/favorite/IFavoriteMealAccess.dart'; import 'package:app/view_model/repository/data_classes/meal/Meal.dart'; import 'package:app/view_model/repository/data_classes/mealplan/Line.dart'; @@ -49,15 +50,15 @@ class DetailsPageState extends State { appBar: MensaAppBar( appBarHeight: kToolbarHeight * 1.25, child: Padding( - padding: EdgeInsets.symmetric(horizontal: 16), + padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ MensaIconButton( onPressed: () => Navigator.of(context).pop(), - icon: NavigationBackIcon()), - Spacer(), + icon: const NavigationBackIcon()), + const Spacer(), Consumer( builder: (context, favoriteMealAccess, child) => MensaIconButton( @@ -66,8 +67,8 @@ class DetailsPageState extends State { .addFavoriteMeal(widget._meal) }, icon: widget._meal.isFavorite - ? FavoriteFilledIcon() - : FavoriteOutlinedIcon())), + ? const FavoriteFilledIcon() + : const FavoriteOutlinedIcon())), MensaIconButton( onPressed: () => { showDialog( @@ -76,23 +77,23 @@ class DetailsPageState extends State { UploadImageDialog(meal: widget._meal), ) }, - icon: NavigationAddImageIcon()), + icon: const NavigationAddImageIcon()), ], )), ), body: Column( children: [ Padding( - padding: EdgeInsets.symmetric(horizontal: 28), + padding: const EdgeInsets.symmetric(horizontal: 28), child: Row(children: [ Expanded( child: Text( widget._meal.name, - style: - TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold), )) ])), - SizedBox(height: 16), + const SizedBox(height: 16), Expanded( child: Container( color: themeData.brightness == Brightness.light @@ -100,8 +101,8 @@ class DetailsPageState extends State { : themeData.colorScheme.surface, child: SingleChildScrollView( child: Padding( - padding: - EdgeInsets.symmetric(horizontal: 16, vertical: 8), + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 8), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -109,23 +110,35 @@ class DetailsPageState extends State { widget._line != null ? Row( children: [ - Padding( + const Padding( padding: EdgeInsets.all(8), child: MealLineIcon(), ), Text( widget._line!.name, - style: TextStyle(fontSize: 16), + style: const TextStyle(fontSize: 16), ) ], ) - : SizedBox(), - SizedBox(height: 8), + : const SizedBox(), + const SizedBox(height: 8), MealPreviewImage( + enableUploadButton: true, + onUploadButtonPressed: () => showDialog( + context: context, + builder: (context) => UploadImageDialog( + meal: widget._meal), + ), + onImagePressed: () => showDialog( + context: context, + builder: (context) => MealImageDialog( + images: widget._meal.images ?? [], + ), + ), borderRadius: BorderRadius.circular(4), meal: widget._meal, height: 250), - SizedBox(height: 8), + const SizedBox(height: 8), MealAccordion( backgroundColor: themeData.brightness == Brightness.light @@ -172,7 +185,7 @@ class DetailsPageState extends State { 1), )) .toList(), - SizedBox(height: 16), + const SizedBox(height: 16), RatingsOverview( meal: widget._meal, backgroundColor: @@ -180,7 +193,7 @@ class DetailsPageState extends State { ? themeData.colorScheme.surface : themeData.colorScheme.background, ), - SizedBox(height: 16), + const SizedBox(height: 16), Text( FlutterI18n.translate( context, "ratings.titlePersonalRating"), @@ -203,7 +216,7 @@ class DetailsPageState extends State { max: 5, onChanged: (int) {}, ), - Spacer(), + const Spacer(), MensaButton( text: FlutterI18n.translate( context, "ratings.editRating"), diff --git a/app/lib/view/images/MealImageDialog.dart b/app/lib/view/images/MealImageDialog.dart index e69de29b..9872c2da 100644 --- a/app/lib/view/images/MealImageDialog.dart +++ b/app/lib/view/images/MealImageDialog.dart @@ -0,0 +1,105 @@ +import 'package:app/view/core/MensaAppBar.dart'; +import 'package:app/view/core/buttons/MensaIconButton.dart'; +import 'package:app/view/core/dialogs/MensaFullscreenDialog.dart'; +import 'package:app/view/core/icons/image/ImageReportIcon.dart'; +import 'package:app/view/core/icons/image/ThumbDownFilledIcon.dart'; +import 'package:app/view/core/icons/image/ThumbDownOutlinedIcon.dart'; +import 'package:app/view/core/icons/image/ThumbUpFilledIcon.dart'; +import 'package:app/view/core/icons/image/ThumbUpOutlinedIcon.dart'; +import 'package:app/view/core/icons/navigation/NavigationCloseIcon.dart'; +import 'package:app/view/images/ImageReportDialog.dart'; +import 'package:app/view_model/logic/image/IImageAccess.dart'; +import 'package:app/view_model/repository/data_classes/meal/ImageData.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class MealImageDialog extends StatefulWidget { + final List _images; + + const MealImageDialog({super.key, required List images}) + : _images = images; + + @override + State createState() => _MealImageDialogState(); +} + +class _MealImageDialogState extends State { + int currentPage = 0; + final PageController pageController = PageController(); + + @override + void initState() { + super.initState(); + + pageController.addListener(() { + if (pageController.page?.round() != currentPage) { + setState(() { + currentPage = pageController.page!.round(); + }); + } + }); + } + + @override + Widget build(BuildContext context) { + return MensaFullscreenDialog( + appBar: MensaAppBar( + appBarHeight: kToolbarHeight, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), + child: Row( + children: [ + MensaIconButton( + onPressed: () => Navigator.of(context).pop(), + icon: const NavigationCloseIcon()), + const Spacer(), + ], + ), + )), + content: PageView.builder( + itemCount: widget._images.length, + controller: pageController, + itemBuilder: (context, index) { + return Center( + child: Image.network( + widget._images[index].url, + fit: BoxFit.contain, + )); + }, + ), + actions: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), + child: Row( + children: [ + MensaIconButton( + onPressed: () async { + await context + .read() + .upvoteImage(widget._images[currentPage]); + }, + icon: widget._images[currentPage].individualRating == 1 + ? const ThumbUpFilledIcon() + : const ThumbUpOutlinedIcon()), + MensaIconButton( + onPressed: () async { + await context + .read() + .downvoteImage(widget._images[currentPage]); + }, + icon: widget._images[currentPage].individualRating == -1 + ? const ThumbDownFilledIcon() + : const ThumbDownOutlinedIcon()), + const Spacer(), + MensaIconButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => ImageReportDialog( + image: widget._images[currentPage]), + ); + }, + icon: const ImageReportIcon()), + ], + ))); + } +} From b3411f71ff4ef1af28a5652baab594d3cec2d5ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 14:04:42 +0200 Subject: [PATCH 177/184] format and document rest of the view --- app/lib/view/core/buttons/MensaButton.dart | 6 +++++- app/lib/view/core/buttons/MensaCtaButton.dart | 9 +++++++-- app/lib/view/core/buttons/MensaLink.dart | 5 ++++- app/lib/view/core/buttons/MensaTapable.dart | 10 +++++++++- app/lib/view/core/dialogs/MensaDialog.dart | 7 +++++++ app/lib/view/core/dialogs/MensaFullscreenDialog.dart | 7 +++++++ .../core/icons/allergens/AllergenAlmondsIcon.dart | 8 ++++++-- .../view/core/icons/allergens/AllergenBarleyIcon.dart | 11 +++++++---- .../core/icons/allergens/AllergenBrazilNutsIcon.dart | 8 ++++++-- .../view/core/icons/allergens/AllergenCeleryIcon.dart | 11 +++++++---- .../core/icons/allergens/AllergenCrustaceansIcon.dart | 8 ++++++-- .../view/core/icons/allergens/AllergenEggsIcon.dart | 11 +++++++---- .../view/core/icons/allergens/AllergenFishIcon.dart | 11 +++++++---- .../core/icons/allergens/AllergenHazelnutsIcon.dart | 8 ++++++-- .../view/core/icons/allergens/AllergenKamutIcon.dart | 3 +-- .../view/core/icons/allergens/AllergenLoafIcon.dart | 11 +++++++---- .../view/core/icons/allergens/AllergenLupinIcon.dart | 3 +-- .../view/core/icons/allergens/AllergenMilkIcon.dart | 11 +++++++---- .../core/icons/allergens/AllergenMolluscsIcon.dart | 8 ++++++-- .../core/icons/allergens/AllergenMustardIcon.dart | 8 ++++++-- .../view/core/icons/allergens/AllergenOatsIcon.dart | 11 +++++++---- .../core/icons/allergens/AllergenPeanutsIcon.dart | 8 ++++++-- .../view/core/icons/allergens/AllergenPecansIcon.dart | 11 +++++++---- .../view/core/icons/allergens/AllergenRyeIcon.dart | 3 +-- .../view/core/icons/allergens/AllergenSesameIcon.dart | 11 +++++++---- .../view/core/icons/allergens/AllergenSoyaIcon.dart | 11 +++++++---- .../view/core/icons/allergens/AllergenSpeltIcon.dart | 3 +-- .../core/icons/allergens/AllergenWalnutsIcon.dart | 8 ++++++-- .../view/core/icons/allergens/AllergenWheatIcon.dart | 11 +++++++---- .../view/core/icons/favorites/FavoriteFilledIcon.dart | 7 ++++++- .../core/icons/favorites/FavoriteOutlinedIcon.dart | 7 ++++++- app/lib/view/core/icons/filter/FilterRestoreIcon.dart | 7 ++++++- app/lib/view/core/icons/filter/SortAscendingIcon.dart | 7 ++++++- app/lib/view/core/icons/filter/SortDecendingIcon.dart | 7 ++++++- app/lib/view/core/icons/meal/MealLineIcon.dart | 7 ++++++- 35 files changed, 204 insertions(+), 79 deletions(-) diff --git a/app/lib/view/core/buttons/MensaButton.dart b/app/lib/view/core/buttons/MensaButton.dart index 2f86d324..3b3b23d3 100644 --- a/app/lib/view/core/buttons/MensaButton.dart +++ b/app/lib/view/core/buttons/MensaButton.dart @@ -12,7 +12,11 @@ class MensaButton extends StatelessWidget { /// @param onLongPressed The function that is called when the button is long pressed. /// @param text The text that is displayed on the button. /// @returns A new MensaButton. - const MensaButton({super.key, required onPressed, onLongPressed, required text}): _onPressed = onPressed, _onLongPressed = onLongPressed, _text = text; + const MensaButton( + {super.key, required onPressed, onLongPressed, required text}) + : _onPressed = onPressed, + _onLongPressed = onLongPressed, + _text = text; /// Builds the widget. /// @param context The context in which the widget is built. diff --git a/app/lib/view/core/buttons/MensaCtaButton.dart b/app/lib/view/core/buttons/MensaCtaButton.dart index bafd2fcf..41330635 100644 --- a/app/lib/view/core/buttons/MensaCtaButton.dart +++ b/app/lib/view/core/buttons/MensaCtaButton.dart @@ -12,7 +12,11 @@ class MensaCtaButton extends StatelessWidget { /// @param onLongPressed The function that is called when the button is long pressed. /// @param text The text that is displayed on the button. /// @returns A new MensaCtaButton. - const MensaCtaButton({super.key, required onPressed, onLongPressed, required text}): _onPressed = onPressed, _onLongPressed = onLongPressed, _text = text; + const MensaCtaButton( + {super.key, required onPressed, onLongPressed, required text}) + : _onPressed = onPressed, + _onLongPressed = onLongPressed, + _text = text; /// Builds the widget. /// @param context The context in which the widget is built. @@ -29,7 +33,8 @@ class MensaCtaButton extends StatelessWidget { highlightElevation: 0, onPressed: _onPressed, onLongPress: _onLongPressed, - child: Text(_text, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + child: Text(_text, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), )); } } diff --git a/app/lib/view/core/buttons/MensaLink.dart b/app/lib/view/core/buttons/MensaLink.dart index d9f05a46..7be7ca39 100644 --- a/app/lib/view/core/buttons/MensaLink.dart +++ b/app/lib/view/core/buttons/MensaLink.dart @@ -12,7 +12,10 @@ class MensaLink extends StatelessWidget { /// @param onLongPressed The function that is called when the button is long pressed. /// @param text The text that is displayed on the button. /// @returns A new MensaLink. - const MensaLink({super.key, required onPressed, onLongPressed, required text}): _onPressed = onPressed, _onLongPressed = onLongPressed, _text = text; + const MensaLink({super.key, required onPressed, onLongPressed, required text}) + : _onPressed = onPressed, + _onLongPressed = onLongPressed, + _text = text; /// Builds the widget. /// @param context The context in which the widget is built. diff --git a/app/lib/view/core/buttons/MensaTapable.dart b/app/lib/view/core/buttons/MensaTapable.dart index 6fb72340..c3e481e1 100644 --- a/app/lib/view/core/buttons/MensaTapable.dart +++ b/app/lib/view/core/buttons/MensaTapable.dart @@ -1,12 +1,20 @@ import 'package:flutter/material.dart'; +/// This widget is used to display a tapable button. class MensaTapable extends StatelessWidget { final Widget _child; final Color? _color; final Function() _onTap; final Function()? _onLongPress; - MensaTapable( + /// Creates a tapable button. + /// @param key The key to use for this widget. + /// @param child The child of the button. + /// @param color The color of the button. + /// @param onTap The function to call when the icon is tapped. + /// @param onLongPress The function to call when the icon is long pressed. + /// @returns a widget that displays a tapable icon + const MensaTapable( {super.key, required Widget child, Color? color, diff --git a/app/lib/view/core/dialogs/MensaDialog.dart b/app/lib/view/core/dialogs/MensaDialog.dart index 11d927ee..84d2cbb9 100644 --- a/app/lib/view/core/dialogs/MensaDialog.dart +++ b/app/lib/view/core/dialogs/MensaDialog.dart @@ -1,10 +1,17 @@ import 'package:flutter/material.dart'; +/// This widget is used to display a dialog. class MensaDialog extends StatelessWidget { final String _title; final Widget? _content; final Widget? _actions; + /// Creates a dialog. + /// @param key The key to use for this widget. + /// @param title The title to display. + /// @param content The content to display. + /// @param actions The actions to display. + /// @returns a widget that displays a dialog const MensaDialog( {super.key, required String title, Widget? content, Widget? actions}) : _title = title, diff --git a/app/lib/view/core/dialogs/MensaFullscreenDialog.dart b/app/lib/view/core/dialogs/MensaFullscreenDialog.dart index e8a99768..51910fe6 100644 --- a/app/lib/view/core/dialogs/MensaFullscreenDialog.dart +++ b/app/lib/view/core/dialogs/MensaFullscreenDialog.dart @@ -1,10 +1,17 @@ import 'package:flutter/material.dart'; +/// This widget is used to display a fullscreen dialog. class MensaFullscreenDialog extends StatelessWidget { final PreferredSizeWidget? _appBar; final Widget? _content; final Widget? _actions; + /// Creates a fullscreen dialog. + /// @param key The key to use for this widget. + /// @param appBar The app bar to display. + /// @param content The content to display. + /// @param actions The actions to display. + /// @returns a widget that displays a fullscreen dialog const MensaFullscreenDialog( {super.key, PreferredSizeWidget? appBar, diff --git a/app/lib/view/core/icons/allergens/AllergenAlmondsIcon.dart b/app/lib/view/core/icons/allergens/AllergenAlmondsIcon.dart index fb77f21e..aae2ae60 100644 --- a/app/lib/view/core/icons/allergens/AllergenAlmondsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenAlmondsIcon.dart @@ -10,7 +10,11 @@ class AllergenAlmondsIcon extends IAllergenIcon { @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/ma.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/ma.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenBarleyIcon.dart b/app/lib/view/core/icons/allergens/AllergenBarleyIcon.dart index c3ccbfef..eac2534a 100644 --- a/app/lib/view/core/icons/allergens/AllergenBarleyIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenBarleyIcon.dart @@ -5,12 +5,15 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Barley class AllergenBarleyIcon extends IAllergenIcon { - const AllergenBarleyIcon( - {super.key, super.width, super.height, super.color}); + const AllergenBarleyIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/ge.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/ge.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenBrazilNutsIcon.dart b/app/lib/view/core/icons/allergens/AllergenBrazilNutsIcon.dart index c3acabf3..98fb50ed 100644 --- a/app/lib/view/core/icons/allergens/AllergenBrazilNutsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenBrazilNutsIcon.dart @@ -10,7 +10,11 @@ class AllergenBrazilNutsIcon extends IAllergenIcon { @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/pa.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/pa.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenCeleryIcon.dart b/app/lib/view/core/icons/allergens/AllergenCeleryIcon.dart index df39936f..e08ea51b 100644 --- a/app/lib/view/core/icons/allergens/AllergenCeleryIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenCeleryIcon.dart @@ -5,12 +5,15 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Cashews class AllergenCeleryIcon extends IAllergenIcon { - const AllergenCeleryIcon( - {super.key, super.width, super.height, super.color}); + const AllergenCeleryIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/se.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/se.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenCrustaceansIcon.dart b/app/lib/view/core/icons/allergens/AllergenCrustaceansIcon.dart index 667fb66f..e3b4b1fd 100644 --- a/app/lib/view/core/icons/allergens/AllergenCrustaceansIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenCrustaceansIcon.dart @@ -10,7 +10,11 @@ class AllergenCrustaceansIcon extends IAllergenIcon { @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/kr.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/kr.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenEggsIcon.dart b/app/lib/view/core/icons/allergens/AllergenEggsIcon.dart index 8fdec6d2..005740c9 100644 --- a/app/lib/view/core/icons/allergens/AllergenEggsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenEggsIcon.dart @@ -5,12 +5,15 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Eggs class AllergenEggsIcon extends IAllergenIcon { - const AllergenEggsIcon( - {super.key, super.width, super.height, super.color}); + const AllergenEggsIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/ei.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/ei.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenFishIcon.dart b/app/lib/view/core/icons/allergens/AllergenFishIcon.dart index d0b736f8..9c3a3499 100644 --- a/app/lib/view/core/icons/allergens/AllergenFishIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenFishIcon.dart @@ -5,12 +5,15 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Fish class AllergenFishIcon extends IAllergenIcon { - const AllergenFishIcon( - {super.key, super.width, super.height, super.color}); + const AllergenFishIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/fi.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/fi.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenHazelnutsIcon.dart b/app/lib/view/core/icons/allergens/AllergenHazelnutsIcon.dart index 5a420007..96389a39 100644 --- a/app/lib/view/core/icons/allergens/AllergenHazelnutsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenHazelnutsIcon.dart @@ -10,7 +10,11 @@ class AllergenHazelnutsIcon extends IAllergenIcon { @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/ha.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/ha.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenKamutIcon.dart b/app/lib/view/core/icons/allergens/AllergenKamutIcon.dart index 5f1dcf94..130bf9e0 100644 --- a/app/lib/view/core/icons/allergens/AllergenKamutIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenKamutIcon.dart @@ -3,8 +3,7 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Kamut class AllergenKamutIcon extends IAllergenIcon { - const AllergenKamutIcon( - {super.key, super.width, super.height, super.color}); + const AllergenKamutIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { diff --git a/app/lib/view/core/icons/allergens/AllergenLoafIcon.dart b/app/lib/view/core/icons/allergens/AllergenLoafIcon.dart index 8a58d55d..d0432baa 100644 --- a/app/lib/view/core/icons/allergens/AllergenLoafIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenLoafIcon.dart @@ -5,12 +5,15 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Loaf class AllergenLoafIcon extends IAllergenIcon { - const AllergenLoafIcon( - {super.key, super.width, super.height, super.color}); + const AllergenLoafIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/la.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/la.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenLupinIcon.dart b/app/lib/view/core/icons/allergens/AllergenLupinIcon.dart index 559d91b3..580221ad 100644 --- a/app/lib/view/core/icons/allergens/AllergenLupinIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenLupinIcon.dart @@ -3,8 +3,7 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Lupin class AllergenLupinIcon extends IAllergenIcon { - const AllergenLupinIcon( - {super.key, super.width, super.height, super.color}); + const AllergenLupinIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { diff --git a/app/lib/view/core/icons/allergens/AllergenMilkIcon.dart b/app/lib/view/core/icons/allergens/AllergenMilkIcon.dart index 3ca3fe1c..65aaaa16 100644 --- a/app/lib/view/core/icons/allergens/AllergenMilkIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenMilkIcon.dart @@ -5,12 +5,15 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Milk class AllergenMilkIcon extends IAllergenIcon { - const AllergenMilkIcon( - {super.key, super.width, super.height, super.color}); + const AllergenMilkIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/ml.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/ml.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenMolluscsIcon.dart b/app/lib/view/core/icons/allergens/AllergenMolluscsIcon.dart index 3973b439..5e81a262 100644 --- a/app/lib/view/core/icons/allergens/AllergenMolluscsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenMolluscsIcon.dart @@ -10,7 +10,11 @@ class AllergenMolluscsIcon extends IAllergenIcon { @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/wt.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/wt.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenMustardIcon.dart b/app/lib/view/core/icons/allergens/AllergenMustardIcon.dart index da12e7bf..3dd3ecd7 100644 --- a/app/lib/view/core/icons/allergens/AllergenMustardIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenMustardIcon.dart @@ -10,7 +10,11 @@ class AllergenMustardIcon extends IAllergenIcon { @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/sn.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/sn.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenOatsIcon.dart b/app/lib/view/core/icons/allergens/AllergenOatsIcon.dart index 04070d13..ba854442 100644 --- a/app/lib/view/core/icons/allergens/AllergenOatsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenOatsIcon.dart @@ -5,12 +5,15 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Mustard class AllergenOatsIcon extends IAllergenIcon { - const AllergenOatsIcon( - {super.key, super.width, super.height, super.color}); + const AllergenOatsIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/hf.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/hf.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenPeanutsIcon.dart b/app/lib/view/core/icons/allergens/AllergenPeanutsIcon.dart index 0a004c6b..303b3c14 100644 --- a/app/lib/view/core/icons/allergens/AllergenPeanutsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenPeanutsIcon.dart @@ -10,7 +10,11 @@ class AllergenPeanutsIcon extends IAllergenIcon { @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/er.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/er.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenPecansIcon.dart b/app/lib/view/core/icons/allergens/AllergenPecansIcon.dart index 10edee41..44bb2d9e 100644 --- a/app/lib/view/core/icons/allergens/AllergenPecansIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenPecansIcon.dart @@ -5,12 +5,15 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Peanuts class AllergenPecansIcon extends IAllergenIcon { - const AllergenPecansIcon( - {super.key, super.width, super.height, super.color}); + const AllergenPecansIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/pe.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/pe.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenRyeIcon.dart b/app/lib/view/core/icons/allergens/AllergenRyeIcon.dart index a4afa2dc..b3b02512 100644 --- a/app/lib/view/core/icons/allergens/AllergenRyeIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenRyeIcon.dart @@ -3,8 +3,7 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Rye class AllergenRyeIcon extends IAllergenIcon { - const AllergenRyeIcon( - {super.key, super.width, super.height, super.color}); + const AllergenRyeIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { diff --git a/app/lib/view/core/icons/allergens/AllergenSesameIcon.dart b/app/lib/view/core/icons/allergens/AllergenSesameIcon.dart index bab0af44..537b0315 100644 --- a/app/lib/view/core/icons/allergens/AllergenSesameIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenSesameIcon.dart @@ -5,12 +5,15 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Sesame class AllergenSesameIcon extends IAllergenIcon { - const AllergenSesameIcon( - {super.key, super.width, super.height, super.color}); + const AllergenSesameIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/sa.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/sa.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenSoyaIcon.dart b/app/lib/view/core/icons/allergens/AllergenSoyaIcon.dart index cc9b4f0b..5af47f3a 100644 --- a/app/lib/view/core/icons/allergens/AllergenSoyaIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenSoyaIcon.dart @@ -5,12 +5,15 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Soya class AllergenSoyaIcon extends IAllergenIcon { - const AllergenSoyaIcon( - {super.key, super.width, super.height, super.color}); + const AllergenSoyaIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/so.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/so.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenSpeltIcon.dart b/app/lib/view/core/icons/allergens/AllergenSpeltIcon.dart index 08036626..67e083a9 100644 --- a/app/lib/view/core/icons/allergens/AllergenSpeltIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenSpeltIcon.dart @@ -3,8 +3,7 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Spelt class AllergenSpeltIcon extends IAllergenIcon { - const AllergenSpeltIcon( - {super.key, super.width, super.height, super.color}); + const AllergenSpeltIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { diff --git a/app/lib/view/core/icons/allergens/AllergenWalnutsIcon.dart b/app/lib/view/core/icons/allergens/AllergenWalnutsIcon.dart index 03ffdf21..e375ce83 100644 --- a/app/lib/view/core/icons/allergens/AllergenWalnutsIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenWalnutsIcon.dart @@ -10,7 +10,11 @@ class AllergenWalnutsIcon extends IAllergenIcon { @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/wa.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/wa.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/allergens/AllergenWheatIcon.dart b/app/lib/view/core/icons/allergens/AllergenWheatIcon.dart index fd93d798..61a53678 100644 --- a/app/lib/view/core/icons/allergens/AllergenWheatIcon.dart +++ b/app/lib/view/core/icons/allergens/AllergenWheatIcon.dart @@ -5,12 +5,15 @@ import 'IAllergenIcon.dart'; /// This widget is used to display the icon for Wheat class AllergenWheatIcon extends IAllergenIcon { - const AllergenWheatIcon( - {super.key, super.width, super.height, super.color}); + const AllergenWheatIcon({super.key, super.width, super.height, super.color}); @override Widget build(BuildContext context) { - return SvgPicture.asset('assets/icons/allergens/we.svg', - width: width, height: height, colorFilter: ColorFilter.mode(color, BlendMode.srcIn),); + return SvgPicture.asset( + 'assets/icons/allergens/we.svg', + width: width, + height: height, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); } } diff --git a/app/lib/view/core/icons/favorites/FavoriteFilledIcon.dart b/app/lib/view/core/icons/favorites/FavoriteFilledIcon.dart index 27dc93f0..f612bc71 100644 --- a/app/lib/view/core/icons/favorites/FavoriteFilledIcon.dart +++ b/app/lib/view/core/icons/favorites/FavoriteFilledIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for deleting it from the favorites. class FavoriteFilledIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for deleting it from the favorites + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [FavoriteFilledIcon] widget const FavoriteFilledIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/favorites/FavoriteOutlinedIcon.dart b/app/lib/view/core/icons/favorites/FavoriteOutlinedIcon.dart index 081834ec..5764841e 100644 --- a/app/lib/view/core/icons/favorites/FavoriteOutlinedIcon.dart +++ b/app/lib/view/core/icons/favorites/FavoriteOutlinedIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for making it a favorite class FavoriteOutlinedIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for making it a favorite + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [FavoriteOutlinedIcon] widget const FavoriteOutlinedIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/filter/FilterRestoreIcon.dart b/app/lib/view/core/icons/filter/FilterRestoreIcon.dart index 4827ccfd..37e807a6 100644 --- a/app/lib/view/core/icons/filter/FilterRestoreIcon.dart +++ b/app/lib/view/core/icons/filter/FilterRestoreIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for restoring the filter class FilterRestoreIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for restoring the filter + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [FilterRestoreIcon] widget const FilterRestoreIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/filter/SortAscendingIcon.dart b/app/lib/view/core/icons/filter/SortAscendingIcon.dart index 20477a29..143ce25b 100644 --- a/app/lib/view/core/icons/filter/SortAscendingIcon.dart +++ b/app/lib/view/core/icons/filter/SortAscendingIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for sorting ascending class SortAscendingIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for sorting ascending + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [SortAscendingIcon] widget const SortAscendingIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/filter/SortDecendingIcon.dart b/app/lib/view/core/icons/filter/SortDecendingIcon.dart index a8dccda4..241a7953 100644 --- a/app/lib/view/core/icons/filter/SortDecendingIcon.dart +++ b/app/lib/view/core/icons/filter/SortDecendingIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for sorting descending class SortDescendingIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for sorting descending + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [SortDescendingIcon] widget const SortDescendingIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/meal/MealLineIcon.dart b/app/lib/view/core/icons/meal/MealLineIcon.dart index 437bba88..14084e13 100644 --- a/app/lib/view/core/icons/meal/MealLineIcon.dart +++ b/app/lib/view/core/icons/meal/MealLineIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for the line of a meal class MealLineIcon extends StatelessWidget { final double? _size; final Color? _color; + /// This widget is used to display the icon for the line of a meal + /// @param key is the key of the widget + /// @param size is the size of the icon + /// @param color is the color of the icon + /// @returns a [MealLineIcon] widget const MealLineIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; From 637bf53e025250ab089104e00b6951d784e9158b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 14:35:27 +0200 Subject: [PATCH 178/184] format and document model --- .../model/api_server/GraphQlServerAccess.dart | 5 + .../model/database/SQLiteDatabaseAccess.dart | 7 +- .../model/database/model/database_model.dart | 76 ++++++++---- app/lib/model/database/model/db_canteen.dart | 24 +++- app/lib/model/database/model/db_favorite.dart | 70 ++++++++++- app/lib/model/database/model/db_image.dart | 56 ++++++++- app/lib/model/database/model/db_line.dart | 34 +++++- app/lib/model/database/model/db_meal.dart | 114 +++++++++++++++++- .../database/model/db_meal_additive.dart | 25 +++- .../database/model/db_meal_allergen.dart | 28 +++-- .../model/database/model/db_meal_plan.dart | 34 +++++- app/lib/model/database/model/db_side.dart | 70 ++++++++++- .../database/model/db_side_additive.dart | 28 +++-- .../database/model/db_side_allergen.dart | 25 +++- 14 files changed, 514 insertions(+), 82 deletions(-) diff --git a/app/lib/model/api_server/GraphQlServerAccess.dart b/app/lib/model/api_server/GraphQlServerAccess.dart index e25d752d..43b9ef0a 100644 --- a/app/lib/model/api_server/GraphQlServerAccess.dart +++ b/app/lib/model/api_server/GraphQlServerAccess.dart @@ -50,6 +50,11 @@ class GraphQlServerAccess implements IServerAccess { /// To connect to the server, its address has to be provided as `address`. /// To authenticate commands to the server, an api key has also to be specified. /// The client identifier is necessary to request user specific information from the server. + /// + /// @param address the address of the server + /// @param apiKey the api key to authenticate commands to the server + /// @param clientId the client identifier to request user specific information from the server + /// @return an instance of the server access class factory GraphQlServerAccess(String address, String apiKey, String clientId) { return GraphQlServerAccess._(clientId, address, apiKey); } diff --git a/app/lib/model/database/SQLiteDatabaseAccess.dart b/app/lib/model/database/SQLiteDatabaseAccess.dart index c51f05aa..dc37b723 100644 --- a/app/lib/model/database/SQLiteDatabaseAccess.dart +++ b/app/lib/model/database/SQLiteDatabaseAccess.dart @@ -30,6 +30,7 @@ import 'model/db_side.dart'; import 'model/db_side_additive.dart'; import 'model/db_side_allergen.dart'; +/// This class accesses the database and uses it as local storage. class SQLiteDatabaseAccess implements IDatabaseAccess { static List _getDatabaseBuilder() { return [ @@ -51,6 +52,8 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { static final SQLiteDatabaseAccess _databaseAccess = SQLiteDatabaseAccess._internal(); + /// Returns the singleton instance of this class. + /// @returns the singleton instance of this class factory SQLiteDatabaseAccess() { return _databaseAccess; } @@ -154,7 +157,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { return DatabaseTransformer.fromDBMealPlan(dbMealPlan, dbLine, dbCanteen!, meals); })); return Success(mealPlans);*/ - int i =0; + int i = 0; List mealPlans = List.filled(result.length, null); for (DBMealPlan dbMealPlan in result.map((plan) => DBMealPlan.fromMap(plan))) { @@ -227,7 +230,7 @@ class SQLiteDatabaseAccess implements IDatabaseAccess { var db = await database; var result = await db.query(DBMealPlan.tableName, where: - '${DBMealPlan.columnLineID} = ? AND ${DBMealPlan.columnDate} = ?', + '${DBMealPlan.columnLineID} = ? AND ${DBMealPlan.columnDate} = ?', whereArgs: [mealPlan.line.id, mealPlan.date.toString()]); if (result.isNotEmpty) return 0; /*var result = await db.query(DBMealPlan.tableName, diff --git a/app/lib/model/database/model/database_model.dart b/app/lib/model/database/model/database_model.dart index a5117f87..16cdc2bd 100644 --- a/app/lib/model/database/model/database_model.dart +++ b/app/lib/model/database/model/database_model.dart @@ -15,11 +15,24 @@ import 'db_meal.dart'; import 'db_meal_plan.dart'; import 'db_side.dart'; +/// This class represents a model in the database. abstract class DatabaseModel { + /// This method returns the map of the model. + /// @returns The map of the model. Map toMap(); } +/// This class transforms the database models to the data classes. class DatabaseTransformer { + /// This method transforms a meal plan from the database to a data class. + /// @param dbMealPlan The meal plan from the database. + /// @param dbLines The lines of the meal plan from the database. + /// @param dbMeals The meals of the meal plan from the database. + /// @param dbSides The sides of the meal plan from the database. + /// @param dbSideAllergens The allergens of the sides of the meal plan from the database. + /// @param dbSideAdditives The additives of the sides of the meal plan from the database. + /// @param dbImages The images of the meal plan from the database. + /// @returns The meal plan as a data class. static Meal fromDBMeal( DBMeal dbMeal, List allergens, @@ -28,8 +41,7 @@ class DatabaseTransformer { Map> sideAllergens, Map> sideAdditives, List images, - bool isFavorite - ) { + bool isFavorite) { return Meal( id: dbMeal.mealID, name: dbMeal.name, @@ -38,11 +50,13 @@ class DatabaseTransformer { student: dbMeal.priceStudent, employee: dbMeal.priceEmployee, pupil: dbMeal.pricePupil, - guest: dbMeal.priceGuest - ), + guest: dbMeal.priceGuest), additives: additives, allergens: allergens, - sides: sides.map((side) => fromDBSide(side, sideAllergens[side]!, sideAdditives[side]!)).toList(), + sides: sides + .map((side) => + fromDBSide(side, sideAllergens[side]!, sideAdditives[side]!)) + .toList(), individualRating: dbMeal.individualRating, numberOfRatings: dbMeal.numberOfRatings, averageRating: dbMeal.averageRating, @@ -50,11 +64,16 @@ class DatabaseTransformer { nextServed: DateTime.tryParse(dbMeal.nextServed), relativeFrequency: dbMeal.relativeFrequency, images: images.map((image) => fromDBImage(image)).toList(), - isFavorite: isFavorite - ); + isFavorite: isFavorite); } - static Side fromDBSide(DBSide side, List allergens, List additives) { + /// This method transforms a side from the database to a data class. + /// @param side The side from the database. + /// @param allergens The allergens of the side. + /// @param additives The additives of the side. + /// @returns The side as a data class. + static Side fromDBSide( + DBSide side, List allergens, List additives) { return Side( id: side.sideID, name: side.name, @@ -63,13 +82,14 @@ class DatabaseTransformer { student: side.priceStudent, employee: side.priceEmployee, pupil: side.pricePupil, - guest: side.priceGuest - ), + guest: side.priceGuest), allergens: allergens, - additives: additives - ); + additives: additives); } + /// This method transforms an image from the database to a data class. + /// @param image The image from the database. + /// @returns The image as a data class. static ImageData fromDBImage(DBImage image) { return ImageData( id: image.imageID, @@ -77,32 +97,40 @@ class DatabaseTransformer { imageRank: image.imageRank, individualRating: image.individualRating, positiveRating: image.positiveRating, - negativeRating: image.positiveRating - ); + negativeRating: image.positiveRating); } + /// This method transforms a line from the database to a data class. + /// @param line The line from the database. + /// @param canteen The canteen of the line. + /// @returns The line as a data class. static Line fromDBLine(DBLine line, DBCanteen canteen) { return Line( id: line.lineID, name: line.name, canteen: DatabaseTransformer.fromDBCanteen(canteen), - position: line.position - ); + position: line.position); } + /// This method transforms a canteen from the database to a data class. + /// @param canteen The canteen from the database. + /// @returns The canteen as a data class. static Canteen fromDBCanteen(DBCanteen canteen) { - return Canteen( - id: canteen.canteenID, - name: canteen.name - ); + return Canteen(id: canteen.canteenID, name: canteen.name); } - static MealPlan fromDBMealPlan(DBMealPlan plan, DBLine line, DBCanteen canteen, List meals) { + /// This method transforms a meal plan from the database to a data class. + /// @param plan The meal plan from the database. + /// @param line The line of the meal plan. + /// @param canteen The canteen of the meal plan. + /// @param meals The meals of the meal plan. + /// @returns The meal plan as a data class. + static MealPlan fromDBMealPlan( + DBMealPlan plan, DBLine line, DBCanteen canteen, List meals) { return MealPlan( date: DateTime.tryParse(plan.date)!, line: DatabaseTransformer.fromDBLine(line, canteen), isClosed: plan.isClosed, - meals: meals - ); + meals: meals); } -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/db_canteen.dart b/app/lib/model/database/model/db_canteen.dart index d2094177..b83f804a 100644 --- a/app/lib/model/database/model/db_canteen.dart +++ b/app/lib/model/database/model/db_canteen.dart @@ -1,29 +1,39 @@ import 'database_model.dart'; +/// This class represents a canteen in the database. class DBCanteen implements DatabaseModel { final String _canteenID; final String _name; + /// The name of the table in the database. static const String tableName = 'canteen'; + /// The name of the column for the canteen id. static const String columnCanteenID = 'canteenID'; + + /// The name of the column for the name. static const String columnName = 'name'; + /// Creates a new instance of a canteen. + /// @param _canteenID The id of the canteen. + /// @param _name The name of the canteen. + /// @returns A new instance of a canteen. DBCanteen(this._canteenID, this._name); @override Map toMap() { - return { - columnCanteenID: _canteenID, - columnName: _name - }; + return {columnCanteenID: _canteenID, columnName: _name}; } + /// Creates a new instance of a canteen from a map. + /// @param map The map to create the instance from. + /// @returns A new instance of a canteen. static DBCanteen fromMap(Map map) { return DBCanteen(map[columnCanteenID], map[columnName]); } /// The string to create a table for the canteen. + /// @returns The string to create a table for the canteen. static String initTable() { return ''' CREATE TABLE $tableName ( @@ -33,7 +43,11 @@ class DBCanteen implements DatabaseModel { '''; } + /// This method returns the name of the canteen. + /// @returns The name of the canteen. String get name => _name; + /// This method returns the id of the canteen. + /// @returns The id of the canteen. String get canteenID => _canteenID; -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/db_favorite.dart b/app/lib/model/database/model/db_favorite.dart index 0def841a..ff9fd8bb 100644 --- a/app/lib/model/database/model/db_favorite.dart +++ b/app/lib/model/database/model/db_favorite.dart @@ -1,9 +1,9 @@ - import 'package:app/model/database/model/database_model.dart'; import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'db_line.dart'; +/// This class represents a favorite in the database. class DBFavorite implements DatabaseModel { final String _favoriteID; final String _lineID; @@ -14,18 +14,52 @@ class DBFavorite implements DatabaseModel { final int _pricePupil; final int _priceGuest; + /// The name of the table in the database. static const String tableName = 'favorite'; + /// The name of the column for the favorite id. static const String columnFavoriteID = 'favoriteID'; + + /// The name of the column for the line id. static const String columnLineID = 'lineID'; + + /// The name of the column for the last date. static const String columnLastDate = 'lastDate'; + + /// The name of the column for the food type. static const String columnFoodType = 'foodType'; + + /// The name of the column for the price for students. static const String columnPriceStudent = 'priceStudent'; + + /// The name of the column for the price for employees. static const String columnPriceEmployee = 'priceEmployee'; + + /// The name of the column for the price for pupils. static const String columnPricePupil = 'pricePupil'; + + /// The name of the column for the price for guests. static const String columnPriceGuest = 'priceGuest'; - DBFavorite(this._favoriteID, this._lineID, this._lastDate, this._foodType, this._priceStudent, this._priceEmployee, this._pricePupil, this._priceGuest); + /// Creates a new instance of a favorite. + /// @param _favoriteID The id of the favorite. + /// @param _lineID The id of the line. + /// @param _lastDate The last date of the favorite. + /// @param _foodType The food type of the favorite. + /// @param _priceStudent The price for students. + /// @param _priceEmployee The price for employees. + /// @param _pricePupil The price for pupils. + /// @param _priceGuest The price for guests. + /// @returns A new instance of a favorite. + DBFavorite( + this._favoriteID, + this._lineID, + this._lastDate, + this._foodType, + this._priceStudent, + this._priceEmployee, + this._pricePupil, + this._priceGuest); @override Map toMap() { @@ -41,11 +75,23 @@ class DBFavorite implements DatabaseModel { }; } + /// Creates a favorite from a map. + /// @param map The map to create a favorite from. + /// @returns A new instance of a favorite. static DBFavorite fromMap(Map map) { - return DBFavorite(map[columnFavoriteID], map[columnLineID], map[columnLastDate], FoodType.values.byName(map[columnFoodType]), map[columnPriceStudent], map[columnPriceEmployee], map[columnPricePupil], map[columnPriceGuest]); + return DBFavorite( + map[columnFavoriteID], + map[columnLineID], + map[columnLastDate], + FoodType.values.byName(map[columnFoodType]), + map[columnPriceStudent], + map[columnPriceEmployee], + map[columnPricePupil], + map[columnPriceGuest]); } /// The string to create a table for a favorite. + /// @returns The string to create a table for a favorite. static String initTable() { return ''' CREATE TABLE $tableName ( @@ -62,19 +108,35 @@ class DBFavorite implements DatabaseModel { '''; } + /// This method returns the price for guests. + /// @returns The price for guests. int get priceGuest => _priceGuest; + /// This method returns the price for pupils. + /// @returns The price for pupils. int get pricePupil => _pricePupil; + /// This method returns the price for employees. + /// @returns The price for employees. int get priceEmployee => _priceEmployee; + /// This method returns the price for students. + /// @returns The price for students. int get priceStudent => _priceStudent; + /// This method returns the food type of the favorite. + /// @returns The food type of the favorite. FoodType get foodType => _foodType; + /// This method returns the last date of the favorite. + /// @returns The last date of the favorite. String get lastDate => _lastDate; + /// This method returns the id of the line. + /// @returns The id of the line. String get lineID => _lineID; + /// This method returns the id of the favorite. + /// @returns The id of the favorite. String get favoriteID => _favoriteID; -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/db_image.dart b/app/lib/model/database/model/db_image.dart index 66557f2f..ae5431fb 100644 --- a/app/lib/model/database/model/db_image.dart +++ b/app/lib/model/database/model/db_image.dart @@ -2,8 +2,8 @@ import 'package:app/model/database/model/database_model.dart'; import 'db_meal.dart'; +/// This class represents an image of a meal in the database. class DBImage implements DatabaseModel { - final String _imageID; final String _mealID; final String _url; @@ -12,17 +12,41 @@ class DBImage implements DatabaseModel { final int _negativeRating; final int _individualRating; + /// The name of the table in the database. static const String tableName = 'image'; + /// The name of the column for the image id. static const String columnImageID = 'imageID'; + + /// The name of the column for the meal id. static const String columnMealID = 'mealID'; + + /// The name of the column for the url. static const String columnUrl = 'url'; + + /// The name of the column for the image rank. static const String columnImageRank = 'imageRank'; + + /// The name of the column for the positive rating. static const String columnPositiveRating = 'positiveRating'; + + /// The name of the column for the negative rating. static const String columnNegativeRating = 'negativeRating'; + + /// The name of the column for the individual rating. static const String columnIndividualRating = 'individualRating'; - DBImage(this._imageID, this._mealID, this._url, this._imageRank, this._positiveRating, this._negativeRating, this._individualRating); + /// Creates a new image. + /// @param imageID The id of the image. + /// @param mealID The id of the meal. + /// @param url The url of the image. + /// @param imageRank The rank of the image. + /// @param positiveRating The positive rating of the image. + /// @param negativeRating The negative rating of the image. + /// @param individualRating The individual rating of the image. + /// @returns A new image. + DBImage(this._imageID, this._mealID, this._url, this._imageRank, + this._positiveRating, this._negativeRating, this._individualRating); @override Map toMap() { @@ -37,11 +61,21 @@ class DBImage implements DatabaseModel { }; } + /// Creates a new image from a map. + /// @param map The map to create the image from. static DBImage fromMap(Map map) { - return DBImage(map[columnImageID], map[columnMealID], map[columnUrl], map[columnImageRank], map[columnPositiveRating], map[columnNegativeRating], map[columnIndividualRating]); + return DBImage( + map[columnImageID], + map[columnMealID], + map[columnUrl], + map[columnImageRank], + map[columnPositiveRating], + map[columnNegativeRating], + map[columnIndividualRating]); } /// The string to create a table for an image. + /// @returns The string to create a table for an image. static String initTable() { return ''' CREATE TABLE $tableName ( @@ -57,17 +91,31 @@ class DBImage implements DatabaseModel { '''; } + /// This method returns the url of the image. + /// @returns The url of the image. String get url => _url; + /// This method returns the id of the meal. + /// @returns The id of the meal. String get mealID => _mealID; + /// This method returns the id of the image. + /// @returns The id of the image. String get imageID => _imageID; + /// This method returns the individual rating of the image. + /// @returns The individual rating of the image. int get individualRating => _individualRating; + /// This method returns the negative rating of the image. + /// @returns The negative rating of the image. int get negativeRating => _negativeRating; + /// This method returns the positive rating of the image. + /// @returns The positive rating of the image. int get positiveRating => _positiveRating; + /// This method returns the rank of the image. + /// @returns The rank of the image. double get imageRank => _imageRank; -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/db_line.dart b/app/lib/model/database/model/db_line.dart index cfe275ba..0d1906fc 100644 --- a/app/lib/model/database/model/db_line.dart +++ b/app/lib/model/database/model/db_line.dart @@ -2,20 +2,34 @@ import 'package:app/model/database/model/db_canteen.dart'; import 'database_model.dart'; +/// This class represents a line of a canteen in the database. class DBLine implements DatabaseModel { - final String _lineID; final String _canteenID; final String _name; final int _position; + /// The name of the table in the database. static const String tableName = 'line'; + /// The name of the column for the line id. static const String columnLineID = 'lineID'; + + /// The name of the column for the canteen id. static const String columnCanteenID = 'canteenID'; + + /// The name of the column for the name. static const String columnName = 'name'; + + /// The name of the column for the position. static const String columnPosition = 'position'; + /// Creates a new instance of a line of a canteen. + /// @param _lineID The id of the line. + /// @param _canteenID The id of the canteen. + /// @param _name The name of the line. + /// @param _position The position of the line. + /// @returns A new instance of a line of a canteen. DBLine(this._lineID, this._canteenID, this._name, this._position); @override @@ -28,11 +42,15 @@ class DBLine implements DatabaseModel { }; } + /// Creates a new instance of a line of a canteen from a map. + /// @param map The map to create the instance from. static DBLine fromMap(Map map) { - return DBLine(map[columnLineID], map[columnCanteenID], map[columnName], map[columnPosition]); + return DBLine(map[columnLineID], map[columnCanteenID], map[columnName], + map[columnPosition]); } /// The string to create a table for a line of a canteen. + /// @returns The string to create a table for a line of a canteen. static String initTable() { return ''' CREATE TABLE $tableName ( @@ -45,13 +63,19 @@ class DBLine implements DatabaseModel { '''; } + /// This method returns the position of the line. + /// @returns The position of the line. int get position => _position; + /// This method returns the name of the line. + /// @returns The name of the line. String get name => _name; + /// This method returns the id of the canteen. + /// @returns The id of the canteen. String get canteenID => _canteenID; + /// This method returns the id of the line. + /// @returns The id of the line. String get lineID => _lineID; - - -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/db_meal.dart b/app/lib/model/database/model/db_meal.dart index 65b13564..e2ee4f49 100644 --- a/app/lib/model/database/model/db_meal.dart +++ b/app/lib/model/database/model/db_meal.dart @@ -4,8 +4,8 @@ import '../../../view_model/repository/data_classes/filter/Frequency.dart'; import '../../../view_model/repository/data_classes/meal/FoodType.dart'; import 'db_meal_plan.dart'; +/// This class represents a meal in the database. class DBMeal implements DatabaseModel { - final String _mealID; final String _mealPlanID; final String _name; @@ -21,24 +21,82 @@ class DBMeal implements DatabaseModel { final String _nextServed; final Frequency _relativeFrequency; + /// The name of the table in the database. static const String tableName = 'meal'; + /// The name of the column for the meal id. static const String columnMealID = 'mealID'; + + /// The name of the column for the meal plan id. static const String columnMealPlanID = 'mealPlanID'; + + /// The name of the column for the name. static const String columnName = 'name'; + + /// The name of the column for the food type. static const String columnFoodType = 'foodType'; + + /// The name of the column for the price for students. static const String columnPriceStudent = 'priceStudent'; + + /// The name of the column for the price for employees. static const String columnPriceEmployee = 'priceEmployee'; + + /// The name of the column for the price for pupils. static const String columnPricePupil = 'pricePupil'; + + /// The name of the column for the price for guests. static const String columnPriceGuest = 'priceGuest'; + + /// The name of the column for the individual rating. static const String columnIndividualRating = 'individualRating'; + + /// The name of the column for the number of ratings. static const String columnNumberOfRatings = 'numberOfRatings'; + + /// The name of the column for the average rating. static const String columnAverageRating = 'averageRating'; + + /// The name of the column for the last served date. static const String columnLastServed = 'lastServed'; + + /// The name of the column for the next served date. static const String columnNextServed = 'nextServed'; + + /// The name of the column for the relative frequency. static const String columnRelativeFrequency = 'relativeFrequency'; - DBMeal(this._mealID, this._mealPlanID, this._name, this._foodType, this._priceStudent, this._priceEmployee, this._pricePupil, this._priceGuest, this._individualRating, this._numberOfRatings, this._averageRating, this._lastServed, this._nextServed, this._relativeFrequency); + /// Creates a new instance of a meal. + /// @param _mealID The id of the meal. + /// @param _mealPlanID The id of the meal plan. + /// @param _name The name of the meal. + /// @param _foodType The food type of the meal. + /// @param _priceStudent The price for students. + /// @param _priceEmployee The price for employees. + /// @param _pricePupil The price for pupils. + /// @param _priceGuest The price for guests. + /// @param _individualRating The individual rating. + /// @param _numberOfRatings The number of ratings. + /// @param _averageRating The average rating. + /// @param _lastServed The last served date. + /// @param _nextServed The next served date. + /// @param _relativeFrequency The relative frequency. + /// @return A new instance of a meal. + DBMeal( + this._mealID, + this._mealPlanID, + this._name, + this._foodType, + this._priceStudent, + this._priceEmployee, + this._pricePupil, + this._priceGuest, + this._individualRating, + this._numberOfRatings, + this._averageRating, + this._lastServed, + this._nextServed, + this._relativeFrequency); @override Map toMap() { @@ -60,12 +118,30 @@ class DBMeal implements DatabaseModel { }; } + /// Creates a new instance of a meal from a map. + /// @param map The map to create a meal from. + /// @return A new instance of a meal. static DBMeal fromMap(Map map) { - return DBMeal(map[columnMealID], map[columnMealPlanID], map[columnName], FoodType.values.byName(map[columnFoodType]), map[columnPriceStudent], map[columnPriceEmployee], map[columnPricePupil], map[columnPriceGuest], map[columnIndividualRating], map[columnNumberOfRatings], map[columnAverageRating], map[columnLastServed], map[columnNextServed], Frequency.values.byName(map[columnRelativeFrequency])); + return DBMeal( + map[columnMealID], + map[columnMealPlanID], + map[columnName], + FoodType.values.byName(map[columnFoodType]), + map[columnPriceStudent], + map[columnPriceEmployee], + map[columnPricePupil], + map[columnPriceGuest], + map[columnIndividualRating], + map[columnNumberOfRatings], + map[columnAverageRating], + map[columnLastServed], + map[columnNextServed], + Frequency.values.byName(map[columnRelativeFrequency])); } + /// The string to create a table for a meal. + /// @return The string to create a table for a meal. static String initTable() { - /// The string to create a table for a meal. return ''' CREATE TABLE $tableName ( $columnMealID TEXT PRIMARY KEY, @@ -87,31 +163,59 @@ class DBMeal implements DatabaseModel { '''; } + /// This method is used to get the relative frequency. + /// @return The relative frequency. Frequency get relativeFrequency => _relativeFrequency; + /// This method is used to get the date when the meal will be served next. + /// @return The date when the meal will be served next. String get nextServed => _nextServed; + /// This method is used to get the date when the meal was last served. + /// @return The date when the meal was last served. String get lastServed => _lastServed; + /// This method is used to get the average rating of the meal. + /// @return The average rating of the meal. double get averageRating => _averageRating; + /// This method is used to get the number of ratings. + /// @return The number of ratings. int get numberOfRatings => _numberOfRatings; + /// This method is used to get the individual rating of the meal. + /// @return The individual rating of the meal. int get individualRating => _individualRating; + /// This method is used to get the price for guests. + /// @return The price for guests. int get priceGuest => _priceGuest; + /// This method is used to get the price for pupils. + /// @return The price for pupils. int get pricePupil => _pricePupil; + /// This method is used to get the price for employees. + /// @return The price for employees. int get priceEmployee => _priceEmployee; + /// This method is used to get the price for students. + /// @return The price for students. int get priceStudent => _priceStudent; + /// This method is used to get the food type of the meal. + /// @return The food type of the meal. FoodType get foodType => _foodType; + /// This method is used to get the name of the meal. + /// @return The name of the meal. String get name => _name; + /// This method is used to get the meal plan id. + /// @return The meal plan id. String get mealPlanID => _mealPlanID; + /// This method is used to get the meal id. + /// @return The meal id. String get mealID => _mealID; -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/db_meal_additive.dart b/app/lib/model/database/model/db_meal_additive.dart index 5455fa66..197daecd 100644 --- a/app/lib/model/database/model/db_meal_additive.dart +++ b/app/lib/model/database/model/db_meal_additive.dart @@ -3,31 +3,40 @@ import 'package:app/view_model/repository/data_classes/meal/Additive.dart'; import 'db_meal.dart'; +/// This class represents an additive of a meal in the database. class DBMealAdditive implements DatabaseModel { - final String _mealID; final Additive _additive; + /// The name of the table in the database. static const String tableName = 'mealAdditive'; + /// The name of the column for the meal id. static const String columnMealID = 'mealID'; + + /// The name of the column for the additive. static const String columnAdditive = 'additive'; + /// Creates a new instance of a meal additive. + /// @param _mealID The id of the meal. + /// @param _additive The additive of the meal. + /// @returns A new instance of a meal additive. DBMealAdditive(this._mealID, this._additive); @override Map toMap() { - return { - columnMealID: _mealID, - columnAdditive: _additive.name - }; + return {columnMealID: _mealID, columnAdditive: _additive.name}; } + /// Creates a new instance of a meal additive from a map. + /// @param map The map to create the instance from. + /// @returns A new instance of a meal additive. static DBMealAdditive fromMap(Map map) { return DBMealAdditive(map[columnMealID], map[columnAdditive]); } /// The string to create a table for an additive of a meal. + /// @returns The string to create a table for an additive of a meal. static String initTable() { return ''' CREATE TABLE $tableName ( @@ -39,7 +48,11 @@ class DBMealAdditive implements DatabaseModel { '''; } + /// This method returns the additive of the meal. + /// @returns The additive of the meal. Additive get additive => _additive; + /// This method returns the id of the meal. + /// @returns The id of the meal. String get mealID => _mealID; -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/db_meal_allergen.dart b/app/lib/model/database/model/db_meal_allergen.dart index 816a643c..8f321aa4 100644 --- a/app/lib/model/database/model/db_meal_allergen.dart +++ b/app/lib/model/database/model/db_meal_allergen.dart @@ -3,31 +3,41 @@ import 'package:app/model/database/model/database_model.dart'; import '../../../view_model/repository/data_classes/meal/Allergen.dart'; import 'db_meal.dart'; +/// This class represents an allergen of a meal in the database. class DBMealAllergen implements DatabaseModel { - final String _mealID; final Allergen _allergen; + /// The name of the table in the database. static const String tableName = 'mealAllergen'; + /// The name of the column for the meal id. static const String columnMealID = 'mealID'; + + /// The name of the column for the allergen. static const String columnAllergen = 'allergen'; + /// Creates a new instance of a meal allergen. + /// @param _mealID The id of the meal. + /// @param _allergen The allergen of the meal. + /// @returns A new instance of a meal allergen. DBMealAllergen(this._mealID, this._allergen); @override Map toMap() { - return { - columnMealID: _mealID, - columnAllergen: _allergen.name - }; + return {columnMealID: _mealID, columnAllergen: _allergen.name}; } + /// Creates a new instance of a meal allergen from a map. + /// @param map The map to create the instance from. + /// @returns A new instance of a meal allergen. static DBMealAllergen fromMap(Map map) { - return DBMealAllergen(map[columnMealID], Allergen.values.byName(map[columnAllergen])); + return DBMealAllergen( + map[columnMealID], Allergen.values.byName(map[columnAllergen])); } /// The string to create a table for an allergen of a meal. + /// @returns The string to create a table for an allergen of a meal. static String initTable() { return ''' CREATE TABLE $tableName ( @@ -39,7 +49,11 @@ class DBMealAllergen implements DatabaseModel { '''; } + /// This method returns the allergen of the meal. + /// @returns The allergen of the meal. Allergen get allergen => _allergen; + /// This method returns the id of the meal. + /// @returns The id of the meal. String get mealID => _mealID; -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/db_meal_plan.dart b/app/lib/model/database/model/db_meal_plan.dart index a0d11069..42d2b2d5 100644 --- a/app/lib/model/database/model/db_meal_plan.dart +++ b/app/lib/model/database/model/db_meal_plan.dart @@ -2,19 +2,34 @@ import 'package:app/model/database/model/database_model.dart'; import 'db_line.dart'; +/// This class represents a meal plan in the database. class DBMealPlan implements DatabaseModel { final String _mealPlanID; final String _lineID; final String _date; final bool _isClosed; + /// The name of the table in the database. static const String tableName = 'mealPlan'; + /// The name of the column for the meal plan id. static const String columnMealPlanID = 'mealPlanID'; + + /// The name of the column for the line id. static const String columnLineID = 'lineID'; + + /// The name of the column for the date. static const String columnDate = 'date'; + + /// The name of the column that stores if the line is closed. static const String columnIsClosed = 'isClosed'; + /// Creates a new instance of a meal plan. + /// @param _mealPlanID The id of the meal plan. + /// @param _lineID The id of the line. + /// @param _date The date of the meal plan. + /// @param _isClosed If the line is closed. + /// @returns A new instance of a meal plan. DBMealPlan(this._mealPlanID, this._lineID, this._date, this._isClosed); @override @@ -26,13 +41,18 @@ class DBMealPlan implements DatabaseModel { columnIsClosed: _isClosed }; } - + + /// Creates a new instance of a meal plan from a map. + /// @param map The map to create the instance from. + /// @returns A new instance of a meal plan. static DBMealPlan fromMap(Map map) { print(map[columnIsClosed]); - return DBMealPlan(map[columnMealPlanID], map[columnLineID], map[columnDate], map[columnIsClosed] == 1); + return DBMealPlan(map[columnMealPlanID], map[columnLineID], map[columnDate], + map[columnIsClosed] == 1); } /// The string to create a table for a meal plan. + /// @returns The string to create a table for a meal plan. static String initTable() { return ''' CREATE TABLE $tableName ( @@ -46,11 +66,19 @@ class DBMealPlan implements DatabaseModel { '''; } + /// This method returns if the line is closed. + /// @returns If the line is closed. bool get isClosed => _isClosed; + /// This method returns the date of the meal plan. + /// @returns The date of the meal plan. String get date => _date; + /// This method returns the id of the line. + /// @returns The id of the line. String get lineID => _lineID; + /// This method returns the id of the meal plan. + /// @returns The id of the meal plan. String get mealPlanID => _mealPlanID; -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/db_side.dart b/app/lib/model/database/model/db_side.dart index e26196a8..8ce2ea94 100644 --- a/app/lib/model/database/model/db_side.dart +++ b/app/lib/model/database/model/db_side.dart @@ -3,8 +3,8 @@ import 'package:app/view_model/repository/data_classes/meal/FoodType.dart'; import 'database_model.dart'; import 'db_meal.dart'; +/// This class represents a side in the database. class DBSide implements DatabaseModel { - final String _sideID; final String _mealID; final String _name; @@ -14,18 +14,52 @@ class DBSide implements DatabaseModel { final int _pricePupil; final int _priceGuest; + /// The name of the table in the database. static const String tableName = 'side'; + /// The name of the column for the side id. static const String columnSideID = 'sideID'; + + /// The name of the column for the meal id. static const String columnMealID = 'mealID'; + + /// The name of the column for the name. static const String columnName = 'name'; + + /// The name of the column for the food type. static const String columnFoodType = 'foodType'; + + /// The name of the column for the price for students. static const String columnPriceStudent = 'priceStudent'; + + /// The name of the column for the price for employees. static const String columnPriceEmployee = 'priceEmployee'; + + /// The name of the column for the price for pupils. static const String columnPricePupil = 'pricePupil'; + + /// The name of the column for the price for guests. static const String columnPriceGuest = 'priceGuest'; - DBSide(this._sideID, this._mealID, this._name, this._foodType, this._priceStudent, this._priceEmployee, this._pricePupil, this._priceGuest); + /// Creates a new instance of a side. + /// @param _sideID The id of the side. + /// @param _mealID The id of the meal. + /// @param _name The name of the side. + /// @param _foodType The food type of the side. + /// @param _priceStudent The price for students. + /// @param _priceEmployee The price for employees. + /// @param _pricePupil The price for pupils. + /// @param _priceGuest The price for guests. + /// @returns A new instance of a side. + DBSide( + this._sideID, + this._mealID, + this._name, + this._foodType, + this._priceStudent, + this._priceEmployee, + this._pricePupil, + this._priceGuest); @override Map toMap() { @@ -41,11 +75,23 @@ class DBSide implements DatabaseModel { }; } + /// Creates a side from a map. + /// @param map The map to create the side from. + /// @returns The created side. static DBSide fromMap(Map map) { - return DBSide(map[columnSideID], map[columnMealID], map[columnName], map[columnFoodType], map[columnPriceStudent], map[columnPriceEmployee], map[columnPricePupil], map[columnPriceGuest]); + return DBSide( + map[columnSideID], + map[columnMealID], + map[columnName], + map[columnFoodType], + map[columnPriceStudent], + map[columnPriceEmployee], + map[columnPricePupil], + map[columnPriceGuest]); } /// The string to create a table for a side. + /// @returns The string to create a table for a side. static String initTable() { return ''' CREATE TABLE $tableName ( @@ -62,19 +108,35 @@ class DBSide implements DatabaseModel { '''; } + /// This method returns the price for a guest. + /// @returns The price for a guest. int get priceGuest => _priceGuest; + /// This method returns the price for a pupil. + /// @returns The price for a pupil. int get pricePupil => _pricePupil; + /// This method returns the price for an employee. + /// @returns The price for an employee. int get priceEmployee => _priceEmployee; + /// This method returns the price for a student. + /// @returns The price for a student. int get priceStudent => _priceStudent; + /// This method returns the food type of the side. + /// @returns The food type of the side. FoodType get foodType => _foodType; + /// This method returns the name of the side. + /// @returns The name of the side. String get name => _name; + /// This method returns the id of the meal. + /// @returns The id of the meal. String get mealID => _mealID; + /// This method returns the id of the side. + /// @returns The id of the side. String get sideID => _sideID; -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/db_side_additive.dart b/app/lib/model/database/model/db_side_additive.dart index 02f044cc..115a814c 100644 --- a/app/lib/model/database/model/db_side_additive.dart +++ b/app/lib/model/database/model/db_side_additive.dart @@ -3,31 +3,41 @@ import 'package:app/model/database/model/db_side.dart'; import '../../../view_model/repository/data_classes/meal/Additive.dart'; +/// This class represents an additive of a side in the database. class DBSideAdditive implements DatabaseModel { - final String _sideID; final Additive _additive; + /// The name of the table in the database. static const String tableName = 'sideAdditive'; + /// The name of the column for the side id. static const String columnSideID = 'sideID'; + + /// The name of the column for the additive. static const String columnAdditive = 'additive'; + /// Creates a new instance of a side additive. + /// @param _sideID The id of the side. + /// @param _additive The additive of the side. + /// @returns A new instance of a side additive. DBSideAdditive(this._sideID, this._additive); @override Map toMap() { - return { - columnSideID: _sideID, - columnAdditive: _additive - }; + return {columnSideID: _sideID, columnAdditive: _additive}; } + /// Creates a new instance of a side additive from a map. + /// @param map The map to create the instance from. + /// @returns A new instance of a side additive. static DBSideAdditive fromMap(Map map) { - return DBSideAdditive(map[columnSideID], Additive.values.byName(map[columnAdditive])); + return DBSideAdditive( + map[columnSideID], Additive.values.byName(map[columnAdditive])); } /// The string to create a table for an additive of a side. + /// @returns The string to create a table for an additive of a side. static String initTable() { return ''' CREATE TABLE $tableName ( @@ -39,7 +49,11 @@ class DBSideAdditive implements DatabaseModel { '''; } + /// This method returns the additive of the side. + /// @returns The additive of the side. Additive get additive => _additive; + /// This method returns the id of the side. + /// @returns The id of the side. String get sideID => _sideID; -} \ No newline at end of file +} diff --git a/app/lib/model/database/model/db_side_allergen.dart b/app/lib/model/database/model/db_side_allergen.dart index e9ee6af7..cd1c32b1 100644 --- a/app/lib/model/database/model/db_side_allergen.dart +++ b/app/lib/model/database/model/db_side_allergen.dart @@ -3,31 +3,40 @@ import 'package:app/model/database/model/db_side.dart'; import '../../../view_model/repository/data_classes/meal/Allergen.dart'; +/// This class represents an allergen of a side in the database. class DBSideAllergen implements DatabaseModel { - final String _sideID; final Allergen _allergen; + /// The name of the table in the database. static const String tableName = 'sideAllergen'; + /// The name of the column for the side id. static const String columnSideID = 'sideID'; + + /// The name of the column for the allergen. static const String columnAllergen = 'allergen'; + /// Creates a new instance of a side allergen. + /// @param _sideID The id of the side. + /// @param _allergen The allergen of the side. + /// @returns A new instance of a side allergen. DBSideAllergen(this._sideID, this._allergen); @override Map toMap() { - return { - columnSideID: _sideID, - columnAllergen: _allergen - }; + return {columnSideID: _sideID, columnAllergen: _allergen}; } + /// Creates a new instance of a side allergen from a map. + /// @param map The map to create the instance from. + /// @returns A new instance of a side allergen. static DBSideAllergen fromMap(Map map) { return DBSideAllergen(map[columnSideID], map[columnAllergen]); } /// The string to create a table for an allergen of a side. + /// @returns The string to create a table for an allergen of a side. static String initTable() { return ''' CREATE TABLE $tableName ( @@ -39,7 +48,11 @@ class DBSideAllergen implements DatabaseModel { '''; } + /// This method returns the allergen of the side. + /// @returns The allergen of the side. Allergen get allergen => _allergen; + /// This method returns the id of the side. + /// @returns The id of the side. String get sideID => _sideID; -} \ No newline at end of file +} From c2336970389c6b696036539bc64d3e2d321ec585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 14:36:59 +0200 Subject: [PATCH 179/184] format and document main file --- app/lib/main.dart | 113 ++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/app/lib/main.dart b/app/lib/main.dart index 76004b32..5b15c7aa 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -23,6 +23,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; +/// The main function of the app. void main() { final FlutterI18nDelegate delegate = FlutterI18nDelegate( translationLoader: NamespaceFileTranslationLoader(namespaces: [ @@ -52,9 +53,14 @@ void main() { )); } +/// The main app widget. class MensaApp extends StatelessWidget { final FlutterI18nDelegate _delegate; + /// Creates a new [MensaApp] + /// [delegate] is the [FlutterI18nDelegate] used for localization. + /// [key] is the key of the widget. + /// @returns a new [MensaApp] const MensaApp({super.key, required FlutterI18nDelegate delegate}) : _delegate = delegate; @@ -95,60 +101,59 @@ class MensaApp extends StatelessWidget { ], child: Consumer( builder: (context, preferenceAccess, child) => MaterialApp( - title: 'Mensa App', - themeMode: (() { - switch (preferenceAccess.getColorScheme()) { - case MensaColorScheme.light: - return ThemeMode.light; - case MensaColorScheme.dark: - return ThemeMode.dark; - case MensaColorScheme.system: - return ThemeMode.system; - } - }()), - localizationsDelegates: [ - _delegate, - ...GlobalMaterialLocalizations.delegates, - GlobalWidgetsLocalizations.delegate, - ], - supportedLocales: const [ - Locale('de'), - ], - theme: ThemeData( - useMaterial3: true, - brightness: Brightness.light, - colorScheme: const ColorScheme( - brightness: Brightness.light, - primary: Color(0xFF7AAC2B), - onPrimary: Color(0xFFFFFFFF), - secondary: Color(0xFF7AAC2B), - onSecondary: Color(0xFFFFFFFF), - error: Color(0xFFD32F2F), - onError: Color(0xFFFFFFFF), - background: Color(0xFFFFFFFF), - onBackground: Color(0xFF000000), - surface: Color(0xFFF6F6F6), - onSurface: Color(0xFF000000)), - ), - darkTheme: ThemeData( - useMaterial3: true, - brightness: Brightness.dark, - colorScheme: const ColorScheme( - brightness: Brightness.dark, - primary: Color(0xFF7AAC2B), - onPrimary: Color(0xFFFFFFFF), - secondary: Color(0xFF7AAC2B), - onSecondary: Color(0xFFFFFFFF), - error: Color(0xFFD32F2F), - onError: Color(0xFFFFFFFF), - background: Color(0xFF1E1E1E), - onBackground: Color(0xFFFFFFFF), - surface: Color(0xFF333333), - surfaceTint: Color(0xFF202020), - onSurface: Color(0xFFFFFFFF)), - ), - home: MainPage() - ), + title: 'Mensa App', + themeMode: (() { + switch (preferenceAccess.getColorScheme()) { + case MensaColorScheme.light: + return ThemeMode.light; + case MensaColorScheme.dark: + return ThemeMode.dark; + case MensaColorScheme.system: + return ThemeMode.system; + } + }()), + localizationsDelegates: [ + _delegate, + ...GlobalMaterialLocalizations.delegates, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: const [ + Locale('de'), + ], + theme: ThemeData( + useMaterial3: true, + brightness: Brightness.light, + colorScheme: const ColorScheme( + brightness: Brightness.light, + primary: Color(0xFF7AAC2B), + onPrimary: Color(0xFFFFFFFF), + secondary: Color(0xFF7AAC2B), + onSecondary: Color(0xFFFFFFFF), + error: Color(0xFFD32F2F), + onError: Color(0xFFFFFFFF), + background: Color(0xFFFFFFFF), + onBackground: Color(0xFF000000), + surface: Color(0xFFF6F6F6), + onSurface: Color(0xFF000000)), + ), + darkTheme: ThemeData( + useMaterial3: true, + brightness: Brightness.dark, + colorScheme: const ColorScheme( + brightness: Brightness.dark, + primary: Color(0xFF7AAC2B), + onPrimary: Color(0xFFFFFFFF), + secondary: Color(0xFF7AAC2B), + onSecondary: Color(0xFFFFFFFF), + error: Color(0xFFD32F2F), + onError: Color(0xFFFFFFFF), + background: Color(0xFF1E1E1E), + onBackground: Color(0xFFFFFFFF), + surface: Color(0xFF333333), + surfaceTint: Color(0xFF202020), + onSurface: Color(0xFFFFFFFF)), + ), + home: MainPage()), )); }); } From c1a99c54fa0aea804fc7b4db8fb43fee4c8161b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20H=C3=A4u=C3=9Fler?= Date: Fri, 28 Jul 2023 15:09:50 +0200 Subject: [PATCH 180/184] format and document new icon files --- app/lib/view/core/icons/image/ImageReportIcon.dart | 7 ++++++- app/lib/view/core/icons/image/ThumbDownFilledIcon.dart | 7 ++++++- app/lib/view/core/icons/image/ThumbDownOutlinedIcon.dart | 7 ++++++- app/lib/view/core/icons/image/ThumbUpFilledIcon.dart | 7 ++++++- app/lib/view/core/icons/image/ThumbUpOutlinedIcon.dart | 7 ++++++- .../view/core/icons/navigation/NavigationCloseIcon.dart | 8 +++++++- 6 files changed, 37 insertions(+), 6 deletions(-) diff --git a/app/lib/view/core/icons/image/ImageReportIcon.dart b/app/lib/view/core/icons/image/ImageReportIcon.dart index 67644512..3295c2e6 100644 --- a/app/lib/view/core/icons/image/ImageReportIcon.dart +++ b/app/lib/view/core/icons/image/ImageReportIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for a report of an image class ImageReportIcon extends StatelessWidget { final double? _size; final Color? _color; + /// Creates a new report icon. + /// @param key The key to identify this widget. + /// @param size The size of the icon. + /// @param color The color of the icon. + /// @return a widget that displays the icon for a report of an image const ImageReportIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/image/ThumbDownFilledIcon.dart b/app/lib/view/core/icons/image/ThumbDownFilledIcon.dart index 46928640..fa809536 100644 --- a/app/lib/view/core/icons/image/ThumbDownFilledIcon.dart +++ b/app/lib/view/core/icons/image/ThumbDownFilledIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for a filled thumb down class ThumbDownFilledIcon extends StatelessWidget { final double? _size; final Color? _color; + /// Creates a new filled thumb down icon. + /// @param key The key to identify this widget. + /// @param size The size of the icon. + /// @param color The color of the icon. + /// @return a widget that displays the icon for a filled thumb down const ThumbDownFilledIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/image/ThumbDownOutlinedIcon.dart b/app/lib/view/core/icons/image/ThumbDownOutlinedIcon.dart index 9f3b659b..75a6ecee 100644 --- a/app/lib/view/core/icons/image/ThumbDownOutlinedIcon.dart +++ b/app/lib/view/core/icons/image/ThumbDownOutlinedIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for a thumb down class ThumbDownOutlinedIcon extends StatelessWidget { final double? _size; final Color? _color; + /// Creates a new thumb down icon. + /// @param key The key to identify this widget. + /// @param size The size of the icon. + /// @param color The color of the icon. + /// @return a widget that displays the icon for a thumb down const ThumbDownOutlinedIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/image/ThumbUpFilledIcon.dart b/app/lib/view/core/icons/image/ThumbUpFilledIcon.dart index a6d795e9..de473d2d 100644 --- a/app/lib/view/core/icons/image/ThumbUpFilledIcon.dart +++ b/app/lib/view/core/icons/image/ThumbUpFilledIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for a filled thumb up class ThumbUpFilledIcon extends StatelessWidget { final double? _size; final Color? _color; + /// Creates a new thump up icon. + /// @param key The key to identify this widget. + /// @param size The size of the icon. + /// @param color The color of the icon. + /// @return a widget that displays the icon for a filled thumb up const ThumbUpFilledIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/image/ThumbUpOutlinedIcon.dart b/app/lib/view/core/icons/image/ThumbUpOutlinedIcon.dart index 373108d7..ab13f07e 100644 --- a/app/lib/view/core/icons/image/ThumbUpOutlinedIcon.dart +++ b/app/lib/view/core/icons/image/ThumbUpOutlinedIcon.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for upvoting class ThumbUpOutlinedIcon extends StatelessWidget { final double? _size; final Color? _color; + /// Creates a new upvote icon. + /// @param key The key to identify this widget. + /// @param size The size of the icon. + /// @param color The color of the icon. + /// @return a widget that displays the icon for upvoting const ThumbUpOutlinedIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; diff --git a/app/lib/view/core/icons/navigation/NavigationCloseIcon.dart b/app/lib/view/core/icons/navigation/NavigationCloseIcon.dart index af690f10..befb622d 100644 --- a/app/lib/view/core/icons/navigation/NavigationCloseIcon.dart +++ b/app/lib/view/core/icons/navigation/NavigationCloseIcon.dart @@ -1,11 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -/// This widget is used to display the icon for Wheat +/// This widget is used to display the icon for closing class NavigationCloseIcon extends StatelessWidget { final double? _size; final Color? _color; + /// Creates a new closing icon. + /// + /// @param key The key to identify this widget. + /// @param size The size of the icon. + /// @param color The color of the icon. + /// @return a widget that displays the icon for closing const NavigationCloseIcon({super.key, double size = 24, Color? color}) : _size = size, _color = color; From d3939d6c46bfc8d56f89cbbf9219f991afff663c Mon Sep 17 00:00:00 2001 From: Alexander Kutschera Date: Fri, 28 Jul 2023 17:21:57 +0200 Subject: [PATCH 181/184] final commit including app icon and final package name --- app/android/app/build.gradle | 2 +- app/android/app/src/debug/AndroidManifest.xml | 2 +- app/android/app/src/main/AndroidManifest.xml | 4 +- .../app/src/main/ic_launcher-playstore.png | Bin 0 -> 15131 bytes .../kotlin/com/example/app/MainActivity.kt | 6 - .../res/drawable/ic_launcher_foreground.xml | 23 +++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 3839 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 3839 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 2470 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2470 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 5443 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 5443 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 8544 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 8544 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 12386 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 12386 bytes .../res/values/ic_launcher_background.xml | 4 + .../app/src/profile/AndroidManifest.xml | 2 +- app/assets/locales/de/image.json | 2 +- app/assets/locales/de/mensaColorScheme.json | 6 +- app/lib/main.dart | 2 +- app/lib/view/core/dialogs/MensaDialog.dart | 4 +- .../view/detail_view/MealRatingDialog.dart | 77 +++++---- .../view/detail_view/UploadImageDialog.dart | 158 +++++++++--------- app/lib/view/images/ImageReportDialog.dart | 101 +++++------ app/pubspec.yaml | 1 + 28 files changed, 228 insertions(+), 176 deletions(-) create mode 100644 app/android/app/src/main/ic_launcher-playstore.png delete mode 100644 app/android/app/src/main/kotlin/com/example/app/MainActivity.kt create mode 100644 app/android/app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/android/app/src/main/res/values/ic_launcher_background.xml diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index d66764a8..e6c1394f 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -44,7 +44,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.app" + applicationId "de.mensa_ka.app" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion 19 diff --git a/app/android/app/src/debug/AndroidManifest.xml b/app/android/app/src/debug/AndroidManifest.xml index e7e8338b..0db8f45c 100644 --- a/app/android/app/src/debug/AndroidManifest.xml +++ b/app/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="de.mensa_ka.app"> XB;Zr*)$Z&<+JG2v| zRsq1U$(?og19*(JwAw<(E=Qm=qp%6+SVf!OZY zrIg_*#Ko+lEoo%nwAh*8Haj;lP|mwO@x?)qwQq!F&MwR6OpuRf!Ju2+Z$%4?DfO)a z`Va2n(OSbLxc#5DGUd22GYs3%K|I`dos32UW>VT|v!Ow7~Ew+Rp^E zDB$XTT|J7loPx&xl>GBBKeybG_!#9hnseK9vgWW5sqN^pgzWbvzEop~r!hE+X?sKg=asX^e z%geii76kx$ZORGGCNE9?DCAGA@UUKMx8_HrtRb{$vv`Y)7hOC!Td;#Po5&Z)Y@%}{8kzM(xzwwq7*DkQ~mxHl6hBqn;cnpm>W?PE*jTfPOc0^=Q-24iS1jl=5|QR@?sIFfqG{*uRMs z(9YBjKkXiO>!OZJ@!+xEvtZh?406@g+Q`8Xrd&=_(5e8K#QYW7>ac^4er&uAD~w+a z?l_0qM!!f_s{y_sA|$+3dcoS%4|zqptqZ0u)usR(UEcnPHgdEo0K1|S@7BKO1c-x| ziT5Xe9M-NS7G4pmV*e&mfT=grVEZBYvH7!in6bFfZJYYGJ2O~^3W&{OF>FjsQtR&dt5c7N zWwxRr8lhPM)L~Cs>U#y?s-#?wO9(FL>uvWZeHPUrs28}3w0aQvG?7{)WQ5@#8YErTJ4d=+oiI?{&zz}B$G6WlNuO;o4g^9bd*uat-OK(Nv zR00dDn~OV0V9s`%GMr(~PGx>iP`XnoTMD(a zi}$qwPd<*B+u=pZMJ71m+&DjO)G=KamSFP-bfR_6Tax^$UP|x>2=X!e$5ti2K{?0* z7n~E_?Y^_bo~Dd3hD6fW#Fn%Hxr>uu6XwVppU~`Kv-uY=@^7i?_$KXp4{*S_V9!}Q zFy+IXnj=)y>EGta*q}Z+l?jg(tTPdIpo8Dp=evc94mTIjwy}pPYHhRj_O+>Gi3tJ+2{lM!uu4=``GNrmnJMk0)wN1qpt|NBANbe2?`DBc~e3Bs>u%~ zpJ0(vEi+5M@fq>^_y}&}`Qt(o*A55Q0Mu$;_}TrGjmBID#pO`4ida zwKhj8l>vn?(&;*d*7X_cMBTIvcyb*h9>gc1jc6-xvtJW0Pq3eu%E7RyJX(TjvQiQ{ zwQC>JzfVq7@rXGIJ0{$n^wzW$DPI?ENM#4yAP&!1fkgXEaRy;#r~>f|Rj}!W8%-DT zSEYQFOJzbf>Oftn6Lq5vXbZRVARcY>8(Z&55{hVzcseK|D2~R~VpgPs7>e#4GsB1U z85uom@Qn6LN0lY)xn)t}OEeyTXUc=d|4dntay)-^%E_6Jn7#qy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..705edec04e9283d8249212d11e43700a43d5b48c GIT binary patch literal 5443 zcmV-J6};++P)WIq6-N}25!^sgR0!78S6$V4{a$zaz3$hT_NUJIPN%!8ZdHA^>fT$+ z>lh4|(xv1!5ko*#m7xWYQVSY7-rQkcO{Qsnoy7{^Z+wRDkVjrlB!@aK&5*_sDkr0= z#@cG$qv_^~+Vl|>_11ZF>#SQUYOOC+)}_B+S!X>~S#SN0zWzvYmg7hM+2_cEysQi@ z3mK3FnbJC#S@Uv+`E7F6@zTdlv2*LwM_1OTuc1V~Lve&cJtd+IC1zwropl!_>V%YV37HKP=Ow`VhKd@?1qX=Fm3f^hFW;FFPCIxP z#py=kg}JyGq`LyE+it43BGAUW&}L_SY5s|h7=(J_E$!6Jv_qdrh*#L=W&+_qgd#qn zK4OgM2kTRFpABNAioYu#Pmivsv+TALZ#OfFjyEsZ%@b1sVSS)|L7!N()La*IQm0i> zXTGr#@j5zS>lhnZeg^4m_#Az5*4Ji{9PE9qb-u%>|6Ogox3C2WB4R45u`ZxpWM-(@ z_yy4rj0ze>Y?Ik~C!O+3*k?motz~<5m95XeyAp)6#bSmw@L6VZ8W%?LPWU%?znBvC zSocj?n-nJH;G!p?{i?m4+tLQU&h34ZVP$1q23hp@)ud`~4?3FC72n(Pq?0=y>qXI% z<(Gdi@Wy!y@xSyG^`!(Z+vM@}9fZ-4eA+54|5ra*Z<4PZ(rt2V!$Tb0;W z%8AdDhyd_fSz~=h4f()d-(5m}J$IH|xNw2t%+KGFwJ(ly$W!;mT!%d8&i_g_ynLHO zo~^Isv(H7IU(WqR2`*98CvYd7aVJjy$DSWJ-*Aq6{ljPE(E|&}e9vbD>+_uL=6zXH zY-Y&k(UCL;rXygh8nYwt@rlz~d4ShGu6B@f;K*J@p0_`H zN>QJ{y-!_BzBzMDQ3g2s>ks7lgR98Gz1gT!9r<#dAT&&! z&^)`tRY5**^7JPP0zl^c`Y!f7U!FRw$n)OOrybLKs3|%&={{qelr!%WHlS>qPyrQt ztkr6rt{lx>0p}aelc(QX>N50o2lxY+qFEc#>L^#XTN!$6-U&^XSDQyS?(L|v`%*M_ z1pufY`Qn#|F2^JAkaX6DwEqsvjY$NzIXMQ`zJPn` zfWob&QF@vmz?lNxJ^D9x3Wo1-swF=-0dVpq@+{ojot=Wo^XRRBzkj$%A;%L)&CvLJ z5+{IL>RTPJ$x9RW)GcQ3Fh8v4=m(rB;D@tclTCl0%vPQV{m%aKz0*9^uh~w#YThVk z@8x;)R=~?2Y*fhc6qPiBq-!ZMX_;{oxOEu~**$cYp=H@_>jAwLaP;eU6&=+JdMf}2 zG;%o}gL61zP3k*_SOa@{$Eg_-(a-edCKD}iN?KSCy!Gi5ijL}qS3cUNs82wv0xl7jrk|lC0s;Pax zQo!80OtSm+QnuqL@3>Zccjh?R`D&4(4mq?c;C&rd!E!JzYb<2?O5@9vW9En*wk7zR z0Bd$+NLjVzU3(!X!n_4;wyk__BwK`?{Qd~LkpRahCqfS7IrH;(Y^|{W@D8%xQ{kz~Bj;N!e~?d3R8&fPa7ftfH)LkSO5#-1a@e zwNnL*EK9InY!g(pF`Z=J=w1Qv6Q$M4^6qd)eQ)yI{u=VnH=lQjf4un&dHC5|6=ijU z!~|uJyRw^01>88JeQNn0%SCmF3P6 zf!K8p$jY_}n>6|LWPv@1-$FGE3wD-><=odPfm zu_%<6_ZIwI_%~czA3ZMt7j|1aJ&@ra22QvuaSXqXqU(W-E9Bj&UIB&ceW!pPJ+oLq zATRGV_<>CE)--Zc{*{x#vD1TjeoE77z@#g)%Jx{kWrZ(7C_1(*Q$dC+OkJv-AhinU zPR-!tdwmd!@(^_9y3|uWhDP=U$4+Mew@LUSa`txf0Z~{XP-l!SRj&Z}3we2WP^*A$ z-LuH8l|G1sIERy$CBH|xZO=G%aKMr@@OI-B^d)@en$)#=-->XB;Zr*)$Z&<+JG2v| zRsq1U$(?og19*(JwAw<(E=Qm=qp%6+SVf!OZY zrIg_*#Ko+lEoo%nwAh*8Haj;lP|mwO@x?)qwQq!F&MwR6OpuRf!Ju2+Z$%4?DfO)a z`Va2n(OSbLxc#5DGUd22GYs3%K|I`dos32UW>VT|v!Ow7~Ew+Rp^E zDB$XTT|J7loPx&xl>GBBKeybG_!#9hnseK9vgWW5sqN^pgzWbvzEop~r!hE+X?sKg=asX^e z%geii76kx$ZORGGCNE9?DCAGA@UUKMx8_HrtRb{$vv`Y)7hOC!Td;#Po5&Z)Y@%}{8kzM(xzwwq7*DkQ~mxHl6hBqn;cnpm>W?PE*jTfPOc0^=Q-24iS1jl=5|QR@?sIFfqG{*uRMs z(9YBjKkXiO>!OZJ@!+xEvtZh?406@g+Q`8Xrd&=_(5e8K#QYW7>ac^4er&uAD~w+a z?l_0qM!!f_s{y_sA|$+3dcoS%4|zqptqZ0u)usR(UEcnPHgdEo0K1|S@7BKO1c-x| ziT5Xe9M-NS7G4pmV*e&mfT=grVEZBYvH7!in6bFfZJYYGJ2O~^3W&{OF>FjsQtR&dt5c7N zWwxRr8lhPM)L~Cs>U#y?s-#?wO9(FL>uvWZeHPUrs28}3w0aQvG?7{)WQ5@#8YErTJ4d=+oiI?{&zz}B$G6WlNuO;o4g^9bd*uat-OK(Nv zR00dDn~OV0V9s`%GMr(~PGx>iP`XnoTMD(a zi}$qwPd<*B+u=pZMJ71m+&DjO)G=KamSFP-bfR_6Tax^$UP|x>2=X!e$5ti2K{?0* z7n~E_?Y^_bo~Dd3hD6fW#Fn%Hxr>uu6XwVppU~`Kv-uY=@^7i?_$KXp4{*S_V9!}Q zFy+IXnj=)y>EGta*q}Z+l?jg(tTPdIpo8Dp=evc94mTIjwy}pPYHhRj_O+>Gi3tJ+2{lM!uu4=``GNrmnJMk0)wN1qpt|NBANbe2?`DBc~e3Bs>u%~ zpJ0(vEi+5M@fq>^_y}&}`Qt(o*A55Q0Mu$;_}TrGjmBID#pO`4ida zwKhj8l>vn?(&;*d*7X_cMBTIvcyb*h9>gc1jc6-xvtJW0Pq3eu%E7RyJX(TjvQiQ{ zwQC>JzfVq7@rXGIJ0{$n^wzW$DPI?ENM#4yAP&!1fkgXEaRy;#r~>f|Rj}!W8%-DT zSEYQFOJzbf>Oftn6Lq5vXbZRVARcY>8(Z&55{hVzcseK|D2~R~VpgPs7>e#4GsB1U z85uom@Qn6LN0lY)xn)t}OEeyTXUc=d|4dntay)-^%E_6Jn7#q5RoFi2#A1`ONS(&aBcUB zV#RXhw_o+jRj(q~M)M*qiSy4pJChvFNy3)1C1k(nd7qPV&YU^(z5BJ9Gpo_K#Vu}e zi(A~{mKrNWQ)l5qOex{-i{>Y#Bvj=1_`$a zRS_nyFww7|(9kx&IBsNqQQX4(V&g7~69vV_kLdBc{3FH-^mvV;grbasaNuYB?HWJl zJATax)&^@UZY0{qJ>>U=l=sU`CKr<`NP|y4tVVv3addvM;VG)%ck+sjKTAkafg&3q zYm43Ei18G?=Vsg+ePC8dB5pFdfC%>_{Tp*b#l{{KYYUDTKVTN4Qlc2j@`{Wl9Hk6} z^lw0s;m>MOejdM0v_t<^1+lL2zN|&xnER7Rxtnu~1Edmb0|m@KYP_E+XA{L4IbkG9 zHj#u8f&JYM-~^N0vl1(XK5n8`s;l$^;I>M37Wv?^V7DkPpSq?`r3#`h7no1N^Gr%mZ=B zw-eNFB(XtWVSM|7;<)FP#Bl}pq!!q-+(INgb3svD2Qdz8Ox*EN^&%xs?lZCB+*OG| z6|qev3b>5+Md^v_J+plcu<>pMn7pf_{7(Fe;a+%8K^RJgRP31tiVG|0CH!-&J4jI3kzEu|~9s3$9;sNFzFc|5>opY1N&fp}< zJ#09awK=AxR0lOqgbAe&WuO7`uRPeZiad`!doS7b#zeCJ?QF8;)sdv&NGruTa^x4q zku@(pME1R%#jxV}2iV`_=gINNkwo(J$+2YrshMQg>zQQ1(Imw==3v3mWU`g^747VK zYZ}?`@&MYlb!}M$VTleJew)4_x|LKHHAV#MbN1`QXn^;lwd8@qx1AhA{`2cco?E<-e^9sA?SLfa&n@*%D&aDn0&*^WE)4rZ(?OeU_JGu17&*bd+&&l6E z-9R?JJdj%Ac*S+9fs<(NKK;4Ol{L(ri8L~If4nz+h|jGh53u&7KIF#>Uy`!2vRhDE zT1r0o{%?x&1&*Iy!W1XJ4usfGt&#kE07^ge)4PgmfNy^}O_m(*q`0Okz~bF3xvpqO z;Nq44k{3T-LoI7N#kHydB%;ThJ+WVmdtApB`n!CJFA7;{3u&reY!KO&RpbF)`(lS; z@C9z%yh>ffD8)4kj<%+s_Ma5z3;b~YGsQJP(R+F1M#(kB`M}MR8|2vO#q_+}yFlRN zKc7~#Q5itYjV})}%e7hwHugDtV%~3`7Ti#(BiECPQhJ^ILSwP5e}G4)+a~hAx2VDRJI`#pq!mZb= zda(z&R*CzPLVgW!;jSD2NZ_YSUy~_dqZ&Z%-w#1!}W)$6grys5V-vJLics?kP5iB^frmE`i!aAmB#HRkHK- z3`Kit0lGmvl(jwT6xErDJkE@Vcx|ChUZLSJwaEk2HUa_v{`q~2N(Pn!I-0XzPbP1Q zS_$1btv)1z=Gg}g?bM8Wq^OMqx_ejSLs69kjpIN(Ox^nW^&GHD;b^?8Q z?hRtcN!D^emsvYv$cV+EeV}8zSfL8*WBe-!lv}9IbVYTj?F2e^@k_fF6$PM6SfNQ9 zqF&b6!wOZzKGBGM>{2wW47Hs=7p|ORs~6?%s}EBwl(jvE3||n|6}nc{3W>~X-eLVV zM_C`Bwi5^|D6uYE-oBksiYbotHClBuGEw)L^i*_Eo}KGHa@2MLp`dv1RF0y3yMWh) zrf!M8^zf8m9dw?S#wT2D5niFJJ9nQU$K<*kV|{?yP9WgO`|}m;+XaONgeIl4cE^&j zOSKE3bKBPpndV1;dem*PeS7lU1iCHMB+#+bOBC(f5v7J58x({qY1<^ z>{2X!9k)Dk3v_S28{_UPWr|cx$xHos^R&9RLIJ$-D!m2^*+2gNCB=DCIEz3I%ffC&Db@;&S*qOx4p>g0itMXC zjmqD0M*IU*PaxBhhm9u&F~#ET&SlS~uy@-*F8R4qV6DxP-%7su z&94kq^sg$YMIgj@E1&PCXx|Zl&JhP?td4wJgE$DBFeeTY@lVRIK;OK>#_w7D2_iFQpQ5cP0~1 zi$Hs63@C5gVE}<>_MNjQmJE9|d>J@l7J>Ll4RZGCpLAvbEl1&dd2FLf`5@a1KVSZa z?ba&qhdEG#K-e5~&5I8y+IBG5D#cMtBKCk2W)UbQMS~cq;hY18W3EIXSfWiQ29vLU zdWXF;B_D9LfUNJ+A5Ku^^)kEeBZnFU`tH{c9kQUlIAb2vk2cCoq;a@IPl35iP--eMSX2%)tZw63M)S%Jyu9 zEd)a0vgrMMMVromC?-zb5`Cpzns2hkTmto-862NqWVlvS2$a1&PC*WHkkTuW%ylsW z{c`ym#ez3yp}ZQsCF**Iv=;5bjcN(ROVthf%?V1Nj>28fh02bfU-SX zVGDuY`T8&^D7NFqQWiiEXxf&Tl1?L9bObkyY1Pz%1c4r&6_{9)2sCj`yn-C&;NJTv zf%YrgvlZ472*pFkybEt0B=cc6%_0ytPYoW<3T{aoxLQ*Pl)ltD0<~$I%;G0`d$z(F z0+p7Pvag7;WY*u_kioZE8DXb zTaIUwXJ0sE5uSNrA6fUDW#3C?cknC>OIzEdHg5xNm_?w@!~LRi4;y~1DFhlm*LniY z*kavuMRgcFBZ0JQ*VZC*=+uT}t~77WwZkClKpUgZCwBG1w;xtdAh8lLuCq@_?jeIy z7n50n!C9>o#4s0=*Q-yULDMYn58Uaf0@oza#Pw0P%lqVGV%*T!*5lygjFZPi17Rjyyv-aS!9hZQjnPs|jbjGWg8d zuYadT5Qq+V>evK@GR(!eMXp1j-Xjz2QrNT>`P2eoUG%sW+7Brw{5UtNo(rQ=C){+B z_sre;XI*JIM^(U7#6u&kC(y`z_1%WG1iC*p(Jon`wGewJj#w0dz3A zLI26WoVD5H4bl0ou%z{Nn7zkH9vqOUP=>h}I92_sueAiigmBhY*C7y=wBlQR`p*hk z1x{E(pr&J%gb!l`a`8gqEc%Is>6a_RT=dOw9RdLv%hiv`tO8$DKjY~b(kmliGB{xt zfpooe$ikZRo)XxBHsbOVsxx=QkuE*0ClKN%d7HMv76J{;QJ>J(KvbcpJQ;Ppecu*6 zz=`mbaMQMT)kqzl=X>6x_Xwi_)VHpliDhkzW0RtCMVO18LlRYARAvi-Qpc%JXsb|> z!k_hxXn-1!r^Zx7td>BKyEOjvWR1o@XSe=kG~&vE^z^6VNQW-g6X?N#i7XJ8w`&V* zArRs@dn|IZ6e0r~`B*q!ZN5hnsDb;cwgx1mt}(^0F+KWD-5j;f)ibeao8m~j4%QRs zfxd|(XSce6t1SfT(kqe7+^KGVEC$cOu;uTd*&#c@1>N<^j=&ao zp^OV=2GBz8@{RFrLXQCx*Xx$MGJ$Ma3j`nlpSsDl2xO1qrr8h$#9guE;jG|olmq@g zIv*GlvjS)#b^TlVBNOxOGbJz;LA0yZgKSv~1l-#(iA-LvuFJ5MKs|>#FO?VdpGljd zN;~%V9RUvb8O;x%h1AiQ;vg@I@SNQ-AG@j*O0!`t5YVPwGFwwEZ`T&sN}$dUI%!j> zqKX;#QH#UBq`Hq{958XgZ1)Ah$@N0xn|ae?(1i86wXRB_{*!D|R&wiPlBsggYAbUXUxmQmDO@ z)K&tat~GwKx_vSSus&@4&%$u*#u3SN5BFtJ{38S=>uzqsf<`RR=D{~QYtQJa(5=7q z6R842bJSIqtpvid4u?;w^1Y!^JnS(ca5;1y6x##@vM7C*%Uync8bshN!i}B@IlJ}$ zb+w`*UQd=Qz*4B4Hrq-dpl`Z;b`Gf$T&Lq!XnzTgYS<1s_x05vgt5wf31Mz5K-U<1 z!-S=;*6x56a+SF;OgbWCkq^G5P=H zYGK(JHk_iz!1n$9dd}Xbzu_wLV_1!7%Tf-3-lNp5yQo1RtZ|yP%Q1np2%a<6>TV`= zYd!$F_V(5^gYG#8Y!aBThMp}v{pnFVX}#{4t5_i{Cd1^TeDEzr_cYfd5DJ`F>m+Ym zbx3C3)8>R=@w}Gn8Y@$6a$iDNFx@nm1=GQu2Dj)(tx$<8r1Y4>>S)7SAOS3imA7pR z)F2Qrd7T3Y#J`zm!urV4q+Z^rJO?)kvORc~3K{_u8#VH6gqO>78LJ|XV)ssGTcIgW z#F6%$Z6{FY?&?;4)gsWS`RZne3UKR#h4TY)LSBMyxsJu+F;xfzK_FGXX;TfXP-tQ= z?_Sxv^nWOR~O$JmTD+Be|JXopG^9knxTT?-_DJ*E+m%iFgWY7q#Qanf3KCwe(~O7OuSoE!Q+ z)uEp25=(bfkA37)o{@R_c?Qsh(*k_nEXYiu9Nev>v+SI>bx^bZS@YBScsswvUj9hbDp^ z4P%!^oWM>Fj>^hFpg~jBQ4|Q2@wj>xi0znW#m@Pa0d2|Kw+fQ^u0gXy--OP%?ogV6 zE*)Zhgp>!=jT*6?t0G&3HEf^xgzhZXlQ@b*czeN?cfthP(;!;ST`@*4P$#%Y8>1gl zf!LfpbFtRvvG8+#As(HfGpf5l=#cBuVdN1a0_tYMm30{sIBxouDE1XWj);e{Y3i-U zY<#cb391$nQ4&z2z^U3RNBljnjMcibcD-9nfv%eQd%*f&!qo18b5$Z-87AGDH}z|v zqenyk?66hX62$aftM;!_k6(;pqV>wctHd)@v_1leUEmt!_cIr;ti$Ko9{=!}_CF_X z(2*YLfg7PCt}E2@#DuBCt&fm$S4PpIK?4ms7B{3IyfAA=j6+-*)&$|@!j#^L>{D;f zxAs6en1t;$EP+f%{YORXBY*-R$_$MD}BXV2XUPR=sYwAhM&ahemsEU!R&z9drfm=V;*hE3XIiZ+@h9j(Yn2N z!GK$YbsM`-*@vnymZt5h6g4?{;0*@M4ta~}1p7QkQJaIQD6BMen(LAa7;IXAuhDqX z$Ucl7v7b!Bm?hfNyi#4lAIJ?yfENpqaiz@*{eR5THw`H9R7 zBpS6e;wwkSM70j0P`ERO3@HdZ!^tBuF1!>0ov60T(4HcuI@s$#oc#uBN;{5tH2gHy zpWs0qk>b}DAXTTwQoPeOXinJ2-uHN5Palv6V~>mrx^UQ3g&HDAglONtiEo2wdNkgB z_uWZD^21)8wk4X?iP+qkJ1(*wykLtu&y>wkq~FZYH|Q9*##r+)$KrVCK#<4ft&b3q znVHgijV7Q$6ZSv``hW=oa)OJn76l8M@?eeJB(fabWno!y=6W4TofiBI9os~VF&}HJ zmXN0FHHJJwsewmfnXmIx1qS_CHYLMVw0^caV@sAopt z%JD0;SJ=Wd1lX9(cUvP9ad@DNGRWwq5m&m6@?VQF!`Sf;B9^3LOfj~19C@T7ijAm- zV)wH^>a(Nkd)AMK8IEb^)i*6S^sPx7q6qwZ9t#p{DUrClqCc0yGQ;W&$O%0a)y8W8 zb6IrEFm@P2K9(3$jO`ucwyF{_?SWNrSW1b2FPxiU5GnEgkpYj6TCDv6>3(^phPqL0 zAQF2*zoDcE<*zr@hnTYtZ+Vd7(<7zD0H?65{cj%<~NvrqGIaTXC!Ln z?dQ>nrlxzxti;p4(xHBzMMSxsFL6BICT^{-SLz#j5y?i~oFsnnyh9VYY#m7tp z%e9I;LZopK%r9n%QM-^@qlCz0uYP^AgN~=K)?F7w%EPBq5UD1UiT9s93!aG*2Qgs! zD&6&mvx1+CO!DeqiPgavVXQD_cXYgCt_W+yzNbuM$|Qp6Z_%FK^;^=PTVfF7J9`iB zmmU1V_!ZheVAgrY#!P*Ub8k{NMN@LP=^5K%2%fEPR>+HSoxDeK;988rGVpzmFV`!A)>a-Xm`ct$)co|!*8#sFhcYhxWtMOY)Gq%hS86tNHruMdSY zi&Wv-5-5_5?VAqknGv*gXhHbL>8m2IA$x;mf+D4a;&D?2scem*By9+?l*coiFtCmw zHb|4PMt2Q;L|+ZP%*eL>saT4z*0*;xdvvH>X%z3C@ATk2+!{K_VJt1YeL|*{}Jlg0sGGIB{MBDZ1)oai1#Rwy@ zG=emdyszjp`i|fj&w^)i6UPA}h=hO=8Hun;P=p{8=7u6PC`>l4AIFsESspjZNd>?{ zw1M)yd_B5StI;zks%h`g*e0m}e#YPMKTeRg6s;xKmy-qez`fW#>Aleh))!Nsq@)pU z3eSLNaTCYEg29$sB`KLusYUvWxgvbxL8vH}&2duER5=0ZGAx9q!eU7+mlWmyGo5Qn zwOJdSEG&C!z}|}kVQ^2}8+|}uq@0DZqn2sk&+3n0ZN0L zKyZpe9BL}C1c;vmv0x$KTA+Lcw6Gc=gB181{)cmLF0K(oL0f1OZQ~xe7w(CBOWl*3 zG&NA9gp!g<5DS(83Wt?|@&Q6-1Ge#Z8zyT!eDvXTXk literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..3c7846cfa744f44c6b104c5728e575e35a58b26d GIT binary patch literal 8544 zcmV-mA)nrfP)5RoFi2#A1`ONS(&aBcUB zV#RXhw_o+jRj(q~M)M*qiSy4pJChvFNy3)1C1k(nd7qPV&YU^(z5BJ9Gpo_K#Vu}e zi(A~{mKrNWQ)l5qOex{-i{>Y#Bvj=1_`$a zRS_nyFww7|(9kx&IBsNqQQX4(V&g7~69vV_kLdBc{3FH-^mvV;grbasaNuYB?HWJl zJATax)&^@UZY0{qJ>>U=l=sU`CKr<`NP|y4tVVv3addvM;VG)%ck+sjKTAkafg&3q zYm43Ei18G?=Vsg+ePC8dB5pFdfC%>_{Tp*b#l{{KYYUDTKVTN4Qlc2j@`{Wl9Hk6} z^lw0s;m>MOejdM0v_t<^1+lL2zN|&xnER7Rxtnu~1Edmb0|m@KYP_E+XA{L4IbkG9 zHj#u8f&JYM-~^N0vl1(XK5n8`s;l$^;I>M37Wv?^V7DkPpSq?`r3#`h7no1N^Gr%mZ=B zw-eNFB(XtWVSM|7;<)FP#Bl}pq!!q-+(INgb3svD2Qdz8Ox*EN^&%xs?lZCB+*OG| z6|qev3b>5+Md^v_J+plcu<>pMn7pf_{7(Fe;a+%8K^RJgRP31tiVG|0CH!-&J4jI3kzEu|~9s3$9;sNFzFc|5>opY1N&fp}< zJ#09awK=AxR0lOqgbAe&WuO7`uRPeZiad`!doS7b#zeCJ?QF8;)sdv&NGruTa^x4q zku@(pME1R%#jxV}2iV`_=gINNkwo(J$+2YrshMQg>zQQ1(Imw==3v3mWU`g^747VK zYZ}?`@&MYlb!}M$VTleJew)4_x|LKHHAV#MbN1`QXn^;lwd8@qx1AhA{`2cco?E<-e^9sA?SLfa&n@*%D&aDn0&*^WE)4rZ(?OeU_JGu17&*bd+&&l6E z-9R?JJdj%Ac*S+9fs<(NKK;4Ol{L(ri8L~If4nz+h|jGh53u&7KIF#>Uy`!2vRhDE zT1r0o{%?x&1&*Iy!W1XJ4usfGt&#kE07^ge)4PgmfNy^}O_m(*q`0Okz~bF3xvpqO z;Nq44k{3T-LoI7N#kHydB%;ThJ+WVmdtApB`n!CJFA7;{3u&reY!KO&RpbF)`(lS; z@C9z%yh>ffD8)4kj<%+s_Ma5z3;b~YGsQJP(R+F1M#(kB`M}MR8|2vO#q_+}yFlRN zKc7~#Q5itYjV})}%e7hwHugDtV%~3`7Ti#(BiECPQhJ^ILSwP5e}G4)+a~hAx2VDRJI`#pq!mZb= zda(z&R*CzPLVgW!;jSD2NZ_YSUy~_dqZ&Z%-w#1!}W)$6grys5V-vJLics?kP5iB^frmE`i!aAmB#HRkHK- z3`Kit0lGmvl(jwT6xErDJkE@Vcx|ChUZLSJwaEk2HUa_v{`q~2N(Pn!I-0XzPbP1Q zS_$1btv)1z=Gg}g?bM8Wq^OMqx_ejSLs69kjpIN(Ox^nW^&GHD;b^?8Q z?hRtcN!D^emsvYv$cV+EeV}8zSfL8*WBe-!lv}9IbVYTj?F2e^@k_fF6$PM6SfNQ9 zqF&b6!wOZzKGBGM>{2wW47Hs=7p|ORs~6?%s}EBwl(jvE3||n|6}nc{3W>~X-eLVV zM_C`Bwi5^|D6uYE-oBksiYbotHClBuGEw)L^i*_Eo}KGHa@2MLp`dv1RF0y3yMWh) zrf!M8^zf8m9dw?S#wT2D5niFJJ9nQU$K<*kV|{?yP9WgO`|}m;+XaONgeIl4cE^&j zOSKE3bKBPpndV1;dem*PeS7lU1iCHMB+#+bOBC(f5v7J58x({qY1<^ z>{2X!9k)Dk3v_S28{_UPWr|cx$xHos^R&9RLIJ$-D!m2^*+2gNCB=DCIEz3I%ffC&Db@;&S*qOx4p>g0itMXC zjmqD0M*IU*PaxBhhm9u&F~#ET&SlS~uy@-*F8R4qV6DxP-%7su z&94kq^sg$YMIgj@E1&PCXx|Zl&JhP?td4wJgE$DBFeeTY@lVRIK;OK>#_w7D2_iFQpQ5cP0~1 zi$Hs63@C5gVE}<>_MNjQmJE9|d>J@l7J>Ll4RZGCpLAvbEl1&dd2FLf`5@a1KVSZa z?ba&qhdEG#K-e5~&5I8y+IBG5D#cMtBKCk2W)UbQMS~cq;hY18W3EIXSfWiQ29vLU zdWXF;B_D9LfUNJ+A5Ku^^)kEeBZnFU`tH{c9kQUlIAb2vk2cCoq;a@IPl35iP--eMSX2%)tZw63M)S%Jyu9 zEd)a0vgrMMMVromC?-zb5`Cpzns2hkTmto-862NqWVlvS2$a1&PC*WHkkTuW%ylsW z{c`ym#ez3yp}ZQsCF**Iv=;5bjcN(ROVthf%?V1Nj>28fh02bfU-SX zVGDuY`T8&^D7NFqQWiiEXxf&Tl1?L9bObkyY1Pz%1c4r&6_{9)2sCj`yn-C&;NJTv zf%YrgvlZ472*pFkybEt0B=cc6%_0ytPYoW<3T{aoxLQ*Pl)ltD0<~$I%;G0`d$z(F z0+p7Pvag7;WY*u_kioZE8DXb zTaIUwXJ0sE5uSNrA6fUDW#3C?cknC>OIzEdHg5xNm_?w@!~LRi4;y~1DFhlm*LniY z*kavuMRgcFBZ0JQ*VZC*=+uT}t~77WwZkClKpUgZCwBG1w;xtdAh8lLuCq@_?jeIy z7n50n!C9>o#4s0=*Q-yULDMYn58Uaf0@oza#Pw0P%lqVGV%*T!*5lygjFZPi17Rjyyv-aS!9hZQjnPs|jbjGWg8d zuYadT5Qq+V>evK@GR(!eMXp1j-Xjz2QrNT>`P2eoUG%sW+7Brw{5UtNo(rQ=C){+B z_sre;XI*JIM^(U7#6u&kC(y`z_1%WG1iC*p(Jon`wGewJj#w0dz3A zLI26WoVD5H4bl0ou%z{Nn7zkH9vqOUP=>h}I92_sueAiigmBhY*C7y=wBlQR`p*hk z1x{E(pr&J%gb!l`a`8gqEc%Is>6a_RT=dOw9RdLv%hiv`tO8$DKjY~b(kmliGB{xt zfpooe$ikZRo)XxBHsbOVsxx=QkuE*0ClKN%d7HMv76J{;QJ>J(KvbcpJQ;Ppecu*6 zz=`mbaMQMT)kqzl=X>6x_Xwi_)VHpliDhkzW0RtCMVO18LlRYARAvi-Qpc%JXsb|> z!k_hxXn-1!r^Zx7td>BKyEOjvWR1o@XSe=kG~&vE^z^6VNQW-g6X?N#i7XJ8w`&V* zArRs@dn|IZ6e0r~`B*q!ZN5hnsDb;cwgx1mt}(^0F+KWD-5j;f)ibeao8m~j4%QRs zfxd|(XSce6t1SfT(kqe7+^KGVEC$cOu;uTd*&#c@1>N<^j=&ao zp^OV=2GBz8@{RFrLXQCx*Xx$MGJ$Ma3j`nlpSsDl2xO1qrr8h$#9guE;jG|olmq@g zIv*GlvjS)#b^TlVBNOxOGbJz;LA0yZgKSv~1l-#(iA-LvuFJ5MKs|>#FO?VdpGljd zN;~%V9RUvb8O;x%h1AiQ;vg@I@SNQ-AG@j*O0!`t5YVPwGFwwEZ`T&sN}$dUI%!j> zqKX;#QH#UBq`Hq{958XgZ1)Ah$@N0xn|ae?(1i86wXRB_{*!D|R&wiPlBsggYAbUXUxmQmDO@ z)K&tat~GwKx_vSSus&@4&%$u*#u3SN5BFtJ{38S=>uzqsf<`RR=D{~QYtQJa(5=7q z6R842bJSIqtpvid4u?;w^1Y!^JnS(ca5;1y6x##@vM7C*%Uync8bshN!i}B@IlJ}$ zb+w`*UQd=Qz*4B4Hrq-dpl`Z;b`Gf$T&Lq!XnzTgYS<1s_x05vgt5wf31Mz5K-U<1 z!-S=;*6x56a+SF;OgbWCkq^G5P=H zYGK(JHk_iz!1n$9dd}Xbzu_wLV_1!7%Tf-3-lNp5yQo1RtZ|yP%Q1np2%a<6>TV`= zYd!$F_V(5^gYG#8Y!aBThMp}v{pnFVX}#{4t5_i{Cd1^TeDEzr_cYfd5DJ`F>m+Ym zbx3C3)8>R=@w}Gn8Y@$6a$iDNFx@nm1=GQu2Dj)(tx$<8r1Y4>>S)7SAOS3imA7pR z)F2Qrd7T3Y#J`zm!urV4q+Z^rJO?)kvORc~3K{_u8#VH6gqO>78LJ|XV)ssGTcIgW z#F6%$Z6{FY?&?;4)gsWS`RZne3UKR#h4TY)LSBMyxsJu+F;xfzK_FGXX;TfXP-tQ= z?_Sxv^nWOR~O$JmTD+Be|JXopG^9knxTT?-_DJ*E+m%iFgWY7q#Qanf3KCwe(~O7OuSoE!Q+ z)uEp25=(bfkA37)o{@R_c?Qsh(*k_nEXYiu9Nev>v+SI>bx^bZS@YBScsswvUj9hbDp^ z4P%!^oWM>Fj>^hFpg~jBQ4|Q2@wj>xi0znW#m@Pa0d2|Kw+fQ^u0gXy--OP%?ogV6 zE*)Zhgp>!=jT*6?t0G&3HEf^xgzhZXlQ@b*czeN?cfthP(;!;ST`@*4P$#%Y8>1gl zf!LfpbFtRvvG8+#As(HfGpf5l=#cBuVdN1a0_tYMm30{sIBxouDE1XWj);e{Y3i-U zY<#cb391$nQ4&z2z^U3RNBljnjMcibcD-9nfv%eQd%*f&!qo18b5$Z-87AGDH}z|v zqenyk?66hX62$aftM;!_k6(;pqV>wctHd)@v_1leUEmt!_cIr;ti$Ko9{=!}_CF_X z(2*YLfg7PCt}E2@#DuBCt&fm$S4PpIK?4ms7B{3IyfAA=j6+-*)&$|@!j#^L>{D;f zxAs6en1t;$EP+f%{YORXBY*-R$_$MD}BXV2XUPR=sYwAhM&ahemsEU!R&z9drfm=V;*hE3XIiZ+@h9j(Yn2N z!GK$YbsM`-*@vnymZt5h6g4?{;0*@M4ta~}1p7QkQJaIQD6BMen(LAa7;IXAuhDqX z$Ucl7v7b!Bm?hfNyi#4lAIJ?yfENpqaiz@*{eR5THw`H9R7 zBpS6e;wwkSM70j0P`ERO3@HdZ!^tBuF1!>0ov60T(4HcuI@s$#oc#uBN;{5tH2gHy zpWs0qk>b}DAXTTwQoPeOXinJ2-uHN5Palv6V~>mrx^UQ3g&HDAglONtiEo2wdNkgB z_uWZD^21)8wk4X?iP+qkJ1(*wykLtu&y>wkq~FZYH|Q9*##r+)$KrVCK#<4ft&b3q znVHgijV7Q$6ZSv``hW=oa)OJn76l8M@?eeJB(fabWno!y=6W4TofiBI9os~VF&}HJ zmXN0FHHJJwsewmfnXmIx1qS_CHYLMVw0^caV@sAopt z%JD0;SJ=Wd1lX9(cUvP9ad@DNGRWwq5m&m6@?VQF!`Sf;B9^3LOfj~19C@T7ijAm- zV)wH^>a(Nkd)AMK8IEb^)i*6S^sPx7q6qwZ9t#p{DUrClqCc0yGQ;W&$O%0a)y8W8 zb6IrEFm@P2K9(3$jO`ucwyF{_?SWNrSW1b2FPxiU5GnEgkpYj6TCDv6>3(^phPqL0 zAQF2*zoDcE<*zr@hnTYtZ+Vd7(<7zD0H?65{cj%<~NvrqGIaTXC!Ln z?dQ>nrlxzxti;p4(xHBzMMSxsFL6BICT^{-SLz#j5y?i~oFsnnyh9VYY#m7tp z%e9I;LZopK%r9n%QM-^@qlCz0uYP^AgN~=K)?F7w%EPBq5UD1UiT9s93!aG*2Qgs! zD&6&mvx1+CO!DeqiPgavVXQD_cXYgCt_W+yzNbuM$|Qp6Z_%FK^;^=PTVfF7J9`iB zmmU1V_!ZheVAgrY#!P*Ub8k{NMN@LP=^5K%2%fEPR>+HSoxDeK;988rGVpzmFV`!A)>a-Xm`ct$)co|!*8#sFhcYhxWtMOY)Gq%hS86tNHruMdSY zi&Wv-5-5_5?VAqknGv*gXhHbL>8m2IA$x;mf+D4a;&D?2scem*By9+?l*coiFtCmw zHb|4PMt2Q;L|+ZP%*eL>saT4z*0*;xdvvH>X%z3C@ATk2+!{K_VJt1YeL|*{}Jlg0sGGIB{MBDZ1)oai1#Rwy@ zG=emdyszjp`i|fj&w^)i6UPA}h=hO=8Hun;P=p{8=7u6PC`>l4AIFsESspjZNd>?{ zw1M)yd_B5StI;zks%h`g*e0m}e#YPMKTeRg6s;xKmy-qez`fW#>Aleh))!Nsq@)pU z3eSLNaTCYEg29$sB`KLusYUvWxgvbxL8vH}&2duER5=0ZGAx9q!eU7+mlWmyGo5Qn zwOJdSEG&C!z}|}kVQ^2}8+|}uq@0DZqn2sk&+3n0ZN0L zKyZpe9BL}C1c;vmv0x$KTA+Lcw6Gc=gB181{)cmLF0K(oL0f1OZQ~xe7w(CBOWl*3 zG&NA9gp!g<5DS(83Wt?|@&Q6-1Ge#Z8zyT!eDvXTXk literal 0 HcmV?d00001 diff --git a/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 4d6372eebdb28e45604e46eeda8dd24651419bc0..90e9e036ae2041089bce3de47d423e76f4babd51 100644 GIT binary patch literal 12386 zcmY*gRa9I}ke(R^7~F!};K4OW2pTlOA^70#4nYTp;30UB;0f-7y9Br3?u6j(%fAnM z&OY3GU%I5fuCD&7Pu)lrC0Q(VGIRg{fF&;{rS|ge`0s(Byu8(x>`(y!E@pWt2@NlU zLl~OZE9nI)eS)vz&=4mQ7X_X~TgG)c!7Xf(#tiFl{ z&%O4yS*mdusm1?)X>x8V#3XaPfu(#(yJRxj|F}Qf9JFwpV7sQ<^$CktL6(tLNKW`N zfv;V6lEto+;#()!B%+(^P+=iYSi&W~&du{fl=S_g>^IGYQcsP-e75phcUX(qPvu*G zpZ3XLTR^~5%<8)14W{VF_V;a3wsIA$Z|40-bGa1I2(FJN+qqd+=G)}=pT5m4UNf10 z`?TnacEnauZ?C7H`M^hWMZW##DuBTE;|WHmPS81+7>xtZ33TH4fF00nd7|UHyNW<* zC2C|WkKs`v8kCx!yf^mfeQlg-JW2-X9-zBl^l*48wyaE|zJCSVDD%mR2HvdYG?2KsCsI};qGp?7ui z3VCaGljU%?>pV0UC>J0n9X4F&AIT&=N=p`In@Q2-gp}4B7BhGx9pdBM2@ukJql1Jl z2Y0ImFA3i+MqTI(@UGAIjQ!oa#X5I?&xMg~@ai?~I9rLrej8%YpO|JuFXUaoz<|a` zV$AV+9ki*Wm{1xPbb~Y&X4z9?fTu59OwjE+JU7nyL<#!cdw+Ny*?jT^Y4Tj8)B!Dk zJwgWU43mdD-mlaWO1O zX(yW*Aq&uHCEbDbRSVTUD?b>L4QnpO$;rj#ObW;w5~q@E0Wa~U69hH6s$~r<)U+)3 z31%$J|NQ19(pp?i%!ff+BO#{ChrP6~= z<%9j{ZS)rw^L7&xfBfjy)Dy_PyMwsD&T`@nGN&XvJk`mI&)!a|cc$;S50~)u?7)yH z=Jl6?y35OiF7~+F^`Lrl$yX@PPH8M=C*rpELED6Jm*}c+<(6|}b^OTl0#Owe9;8tT zx^Cx{?}hMgIibWHj@n4&k|cQAty%A3d83qdp~f~D`LmoQ3ZpbJX9cM@GI13sHZVHP zF~i&=9buBZKorI+;omA0Fa!G%+&2+i)(rN7NMAP2@G*a^*b`b|$l9Bw1~DF8M%pGS zvOFUYvjR($_B5nev3ZIVbxFpTZ&dq$pRFf1?T83gojl*6Me5ax-5T?II!RlMa$PID z`=0!X(Ss@Jp>5*e0LKK+2*1qC(F8}<-YOM?kxJ$x>!G^*_LJsE-@Msb>X2>1+L&Me z+LQiODY{N4hY1;%c5z)Q>3_eJsW=wFjfAuQlv{{TGS8)h(}0*_K54AyU8$%=Dt{SJ zo<)3QU4nCTT1F1E6CcXI6Lg39rXuXIO1ia?*EPTsYA70o2`d-;CmeW(wI5jQjz<@h zchl(&Xfc7A6D*LPI5X0tth6 zs?g`p-P@<6YY5_UGsDR^={N>#DLry)H4<-s6I+ol$CtxtX^w39aYeza@J#)A1`T4|-hRCm9C9D`T7Z=h(+gM7ROP z5ap8-!F7eB!P?4GOZwvT)#1|Mdp|iL=IXK8(8X)dE&d1h*Vjv^*Urxmf31&}IdPm( zS9H3{{B|r-%4-l!A`M6L5=)()T22)K)loh%YiZXr&e!p)>?^AhSD$AS6w1X?efC%N z8lNcwl6;QmYL+sxk2bTOc6E=|={6e)!@Z|`;sccRxbWB(65Twml6&+z15t=cc+XO_ z=I)cCm=F{isltc@BjqE<=jD;iW~bz4uhR{}o2S_^L}tZUvR^-5@Hf8TXJNM~f}q`xy;-yy zhe>SL;K7QQF!G{VH#joqCNh7jMqny6XFUR*5U^gJuOWGJoWv*=ak0Q8+=%gJ*PPzy zW`CNS=dIaL8!L+Ub$eTTps?pt^uK#d%hAk@`c^gQefcY&RkpSr_p5e4R4pZM=Y7x2 z&aYyg>!J@kmH(R5rl<9Dy8Lc8WgDBbN1dAAF~Irz$8s|x#a@**tU#1*S|B~2{ndck zXj@u*YkoHa;K`|{*R~I=s@;?f<%XhgZch0<&VO`u*JuQ&(+7g8m%yndnK6H-#wQ3 z>&(v900xlYeiH!DBVX}kDctbz`>6WB2HEL{Sw&1n1H(FW9n$VGU>R%F_ zZ0h{9fba)6{xP241vYoFq5Xo*cS?D9t0U3xwlSx*Okwlv3XBM${8dksqgVjuF6Z|= zVD+vE=&h-*^ZF6J!{`p;zLOBmvt?F${38{QAykYI-h%%v&;>L{d+!UIJMRO%sBDn{ zbWraG6w$@A{)9;f*1g}doG7Pv*kL?SaOykQ1j;<+GW~-=_5%n9;t~h82y<}X(EuE& zfh0oC53lDh-+*J&chMV|vcA`Hv=JjV>u8Mvhrh?nSZx2P_xy8l-|KZe$6Fm% zhVcQdW0Ou?#00?16&WVaYfY*f*oSm)x3pAtdTdE36Ouk>SEWVjE3gt^tD*a;Ib6&A z3~4Y1fW?G@i{y&T>3ah&#bgJTzYsB)FEBYb{`-49>CUBZs zEL&8mJdK%EmKSmga1KQa$6o=CZ~oFO5$^0KWEM zz-ZQejL%x=M0ItoR2l0`(*ncnTTeN4KUUp^N&hpp{yKfC&jPqyGN7Z>+g)VZL58c~>C8XtcwKyy-7 zOMUU}pxX!{u45D#QnU<(Blk=l#recK#dsi^AQ8x`&rP8|_>PwvYph{30Q11eD$AAT z{$Px4k{h_!N*>x$&!-)Ir_oK`p02NdWOPFD0`so}x~E#j*3f`AHbfCXpI;UAo|3F^ zJ#Fr{ex?s%-WFlV!9G+le)b#VS@tE{jQsEw>i@v~3{vv3A83JqrM^| z{uAUfPN4H+VdWX^@!|>21YSGfz=gBtPP&K$59NuEewn*n%h@5NnYl08O+)&?YVY{B z^V;%1AN;@or^&EdedWMjkos{AmEzv-eo%TWs7qnV9m8{$=f+-vS-N&F5oJ2&U1gLKYK z-}y;tI%n_Hf3M`DUVgpB>&At@y}4~TM#OM>xwThft{JRvM(RJMr&6xv|8)kOKc3I( z(GIa3_7@+=#SCLRjHRG(;YR03?N1hq5HRrJ zr*CX2KArh2|=+H!s%{{q5# z%S)mMp8v*bziK#ebTv2kA!8%SY8gGlU$=k?{u(@k%-oOpz2=Sqw=(@V^feP;w?^DY zBold39I(sz(0S-nqpewA1jIJ^c`=N-=|9B6ca1Fj$vHR#uzB|qd>=zs3_Z6{-_gPa z$b$f*ctEyqNoA<;sPlSbi@6puicg{>L`&HbM|gn+NmnUQz?r&MCugt=h)W;J?G$nJ zXGi!QW|OGZ+1rEF_hmmuIz9!~43fA8SU7w+&b7Qo{SO&jq<79q>Xjns#!5yg&cR_L zj-|QtB69yR4--~u9K5H|+ zBv>as0NF5jGxgicA^$b03}<|*fYUf|3BfQi)cI+fXaF|)bd&P0hH}#M$PiOY2GEi$ zcjvb@ie&9Z5OHztzCIv}GgRJr(pTj3U!r%w5D&%!9Lu}zxT|D=*3Gf1C2nm^s`!E| zf?qiYr{42gyZhSdFa%8MrNu$kqNrEakNoc#=ihj=@CUcGDBayQ!LJ?4{4C&GZ+|cO zjo~oOHeT5#lU%UD6WgEVR{5FP^<;}B_^lTFocnptXw^D&q?e#OPs8)+T)IvHUfCc0 zk(%JF9Jp7_8|T*|De~7XE6zCt4F{J(Zp%>@HCjlq_z%a=Pcnur;lcS1tE+)8Cp49a zD)5*t(Z7uY!4AOEheqf-exxafJ7w+t3*T&1#mK|xn7^yQ+E9p@oZ83seco7>x4roO zWF~m`hel^pD0s>;z~c^Ppw)dG@bKZ$eE&ETxU$dcM`f843aDg1%d$in9GoHOQ2b6S z;Q#Y8ui(cov+ch)e3XMD-mi~LER4G4V%S#nkdA=P&B)%BwP&Og z^#H-!&{E(fHhAJFtG^-?p0G*;l{7Q*RMFHzwcsCt{6VOsIUh@>zZFk zcD0K;IvGS+$5Cj7mz51jq4bfrkqW~5EEdpOAp1G#QJ_^BE+2@sB$oM>(N>{#{jnl@ zJDK_)M{|HyPCXzt(<@u=b8%d$K1?zdH=~~W=mO4cj~d*41n#;RXFaPqeF-Yg_9MAn zQYf=Wn`p-U_QkUq)-RlCr~ZTeJFa3%F!keKPB*`AwfcTRW8ewW^^IcdHK*%RNGX;J zGp+>)l6r0BOT*Bvgx%(Xy#0q+e_O9$iF#Lgi0seQe{8+HiDHqkSO_#dJ?%6WP^qd} z=+J2y#6d&A*Tjd<`W|TsAMRdR$9+pGNBj5E4LrVo_iPKa_2ude`RQxD=?#LuQqoaB zi>7>o43FVjG)QA(Rd2vm`=O1W&?ZH9^Z42T$4Sb*Yaya<3G6TvnaHyz8TF-ld zN7HEpZl~$BKmMR7X+N!R?Jh-8i{>N5$_fFvfM#KdUAz-eG4n!FkD@lwIq`p*FhFaw zfc1}xAFt*Na_Hd1$Q|xL4m|$OhsPT9h04i3O$-vhEWZFG&+94DY9BR_4LaasU%~c1 z<%y;hi<|ksRY-W1^rZ4yt!tkqyxz*##-Muaik}k5^0mbPfEiUHJZ^A!s#WA19$TKj zNWJ>72lc$^-EG{>uAq)>Iit9qvHY~Ojw3M9YmY-Ux{)k8AM-`ooBD&q2=t~A=O{6$ z$Wt5;b+v$4`c(C7voJkPrtiT_$O6f69{X2$^1|lY{S*PQT<-Shvt_a@Dapy2oEMGr zt=pKFfx2`6kHE%e@v%gBLr^39p)PsMZhxe7t(p~NMZcv;j+04*C(F@89k{m%N^M~Y zJ(&EYWTiFHs{HHUg3Wt&AcgQLRl|mgqt0hc@@+~QPDNSLP-mzuOK!KwqFob|`K9F;4d(QCK7y=+4eh*ax(qNaq_wz+~x z-i0d%S|rI7Bw##F;)3IA5UtU}N4aJd4|g__SU{uZy+2AL<8L5Z`*tUf&P-EGwTh@I z%lP6~lsgVvK}F8ohQ#6?2`(V*N|?Xjn;$Z`58H9afB%M(a7f^GT*NqA@-Wu_2Bg67 z;V0`k|9U4xlKHGc)!o-r$b!y}ZX|+k+)_e%QSLAZ~?uU+|2W zL%LHp)M-9xeodw;JeY9_Zz@Co#P>aJM?_F84{dW4>2*oUnH#tO8BV7%Bbv9QP`85% z=6Vc7U?90Ju7Eg-urP2y4Gj98Rd0K0m4`q;j^Oioy%?Qd8L>Y zL9Zc4V)d5*r}+XrI2yEutC|0IklLAqKM#v$pVkC?Wbtubyjt!){57ZM$X4=~ zwYhEf1PiY^26dYS=T%9xVB3}luoCEVeIOI5;U_+ z@74U_M`gVt`-fbVIjOmCSjpLwVRe6zIX0Jx*6KqJu*)<7kJVlHAFk%xF5H3NH0KC) zP=RQ0CMk`00itK_z*on$ zuCV5z-)kHWzxU;&8Q5jnUo*q;)N3hQrvYGbuc7UV*hjv%jn?=GJN}IEpTbOldeX3* zKV8f94ZbkIc>|*>%<)@dCk(T#jIO~du7#p5Cm(TvNR|W-fc=}YeL5P8yXGf-P zMwsWu7Y{30S_b5o@|{J%vc}`OTo4Rgb3;@`~WaFa7sjGKA8iDA*YPEnq;k zM_xq+W~NI;C{^@9OnuD`7}lN;{%4#@H@v* z@F}Y4$lNPWW>c3NJ14;(Oq4)<0`ZJ1`*>t@QhvHzPE|rbfl2|&m_7794k&1kjVY*k zZ$&sM<1zjk9bm0*O=^I&Zq*ZXIioHjQ%`}7-B3L>y2Jp~C&rU7d^6wT*IpSSegC!X zWLqKrs}B&6uVE>YlS%%g7I23><8RuoW<s@3mLKFbZh56lP;pNeMW5m!P3cw|Oq zW4@o-4@nY<(s0UNRe%g2b~yWRcx1@@ zO>+`(Gg&%vy2Q@gH4ASBg=c1gO&PN=Amnwgd1^c?Q{#}kNdBUlF*m$QmlzTOe!u{b zRyi{)EVrA81N0SBB(jfP^-A=e1g1Ys-71X%xi&3>Z})`w+*P8k7<}xr?@gfkwY+Jg zOCRJ_45MK=Fcwv7Ab_sOecVmLu$5*QV2bxrpjNHYd!QWwkDA`1^+gv-D*7_M4#`;# zqKpipH0V&RbA&$5{vK*^Hm?+Yg8~Qg=#ZY`c zA_G2;?&z3PTVHPMj<2CBq=OjZFMiuV06)h!U@HVK)Fxn*?o@xLC4zmev;$_w3;Phr zlb4oL{B4JQV2odfVPaDbNbJl0B8|-88O)7Idb|wUn-TfbtQk7fMn0^Yn6>OdBuH*t z1acBW>WQ`p3W4T+>c5DOjq^i@oMb?)!uAY?Bp`a0 z7EgJ*675M$aOnP-1GQuLMtsf;9TJCwO()iUuBl68l6qPY>dbqrwWDFhe1|PpP%}Jv zHVxMW4W#G6poU@)j5Vt0O%$acx6dc_Glg@IU{a$LBqIwp+$$XJa45la@{Q&`gU_MZ z-BQ@b7rJ|WoA zb6gylJA7WxD0=*HU!`CL1B&dSX-z2g>m?7x?THeypPri=iI}%hfwVhIX?veKWQ?`EVQiLLPs#zs>;7Sh%1s8GU`8b5 zZ||f1gM4dwQ}vwkeZ}OpAlVWJi9Gq%^YYbmugoSu{WXj%M&PJ-`19){TvmPW_bVs` z*B~w<>Fo=wg?L$Zpiws8N+u(_vH)(i*vmpW>0sm;@|+$i+a_kPiXZUopdi=9T2F>O zM3cd&-k?gLQX;qLgS#=N@x&Xb7T6;-KLX(8#Jy;VhA(#&E{}}$ZAeRY<_?oY`h
M}&;ZL%9rUN=9mJ+|UBFs&CncAXm9;yzx^K8@z2j28L;m;8_h(hH9bMQ0DvzjGSI5|VDlw{3BMCEk5SS)2ukH=Z;R3P}fbBqVqXGskAB zY0F#sb6%1B@n%GjydrW#LNGgNsJ_hRFh4aCCyWlVQRG?i7UY+0mq*(?cvvu%`6HYxFH(SQ#DnU zR6l#-xe(~0MAQX_dgasNWd3}gkje(UDxsmiI zBU$X15T&OJNn3Cb`9p_&^@nzfo?XlOMU{TDQwIM*`rnZ8`>|!oIFW#vWV#>j5gVuixvI zC&Q29%s#ICnsbHmb{7V_hWMO_$KPrA?p*6?X5bf~V{)baZYMa`?AJ84NpCGszQcXl zd}`)#(kUQ}4Wh&3loGyaA^_JV)mWh<4O|DO*R^|B`iW3K@B5}tfb(B_B(WK zHAi$xzJv7}>KqM6ZMu-D#eA$XdMF3GN2oOoe39t$YeE*?b6(@LTPc&Vc-6J^{A(?d zo1}C>h7YR@GcAhZY`1A#g=~LUx(*}Vx-my>5=7!-b7l+x+(hpxjpOg)QOyuL@4}}b^$1Q#T}x* zcq@?8SGA{R<&5}71VU6}perGR$B`VXu(9-~*p36(P4fUS zI;rs{osvEk8&6eJE6HZwA(6Fq41tsm=@4-zzsfoLlW!;XYw;ZnY@R3$8nDy-^4}m6V? z!5A&~8lG+SM{!LrB@)Sm;>Tp6Gw%xFm*dHIrY{?CR+^0}UKA0|We-EQ*S0ogMMh}r zvW2mhXc!L9y+!}=dr%V{bbA`qT(!M(oU5LW1@r`C8<)@t7G!iZa0K)8maBLWqh zre?%;!}g~H`Z5C@i&DG*ACsJ8+iRi#Yk>qy(}FvhG?rCg#%OOIQ-ue0 zsC?>YBW9%6qB|(<+6kLQn;^aiJZY>_xa-va;$XtR(_Hh8`c7nmO)`gZUWv*C86$Ae zsS;v)P4^NC%zvWupd0NHg)p^d-vJybRy^_+(isCEQh5j47rV^2>Xsqpknf!#DMv8s zpOG+{bi!PWl!yJ9x^Jb`1Isnku|*ZN6VfXBzuOtDtsMHjIb-@3sK4jNH|Mu9mWUU? zn?v)oadBCz25@{BPO4irGFke#yGU!)<;H!6E>4ivAlo0{%waC!yYc@Ss4B&UF&b|$ z4Xh0@;JG2}W!&9HkJZL#mY(@$I_1#`(x#o94VX0C zrOy#|ag;OmNT2)=oG9Yd#05bUQk%m!Z z6n1ZZz5Z+Xl52ee$w9Jf*TK9SXq*-Jfapkg8A-vZxYNhKi}RbpZ^i~-c;|-yj;Avr zm{x&gh^{R-?TYi%7RtuBVRG{;Fk)}y?=EOcEHw^PBl>j@-k4o~6LbRUl{{-ax z63`pha=v2TkcALt2~OK6=v#NCgD$A<}j&?e<79DeZYzaZM|3}j?~Q13tsBwvG^-@9j?xy_`q_UxeI&Ka3> z4@-@IR++OW(CVR>HCHeam!BTbYM*|1AO1umWaSh1U@^6wqxJpx`fepVP4KlvqM6qk z31iSSu&ht1W#|mcJNzqpggEs)4blgAJ;UW~&4;|Wnk+*rpM2p?Wd8Z>I*H@DVO0wV z#&?qtgB(A#>twn<&Zt|?Os2W;d6V~G11vVj;(Eg_8Z@u)*)ohgQRO`uzo)DGk;S}2 zWNa_LzJ487dQg6gNhrECb;jg7y8EkQ&jc#gd08CE$=>KHATiDGNiIQmVRAw!-f;#& zI+i540NQCcZG-lYL(;4jY7vv?o-+vIteE$qwCVIdvsGr4KCWkWMO(uFuplsi!{ ziW)xUqm)OIQJ8*hc$-ZAt`7EQ0pFg*&B@6pf9pEZt*B&24$Q}R;;73S6dPO^Rx#4N z=(2Mbl7>gThhICp8w@i$@8gUSS`;f9X|;M^TQFWYyz6+fWyweg#E2ewdSWEMWha!V z(9;`9uVmXHiNRp8<17C91`ZsP*gB@08)1pOD_pFsy#4l&k7ccQ_EU-IglvmAjgQNM zdD2SVR2>9jR7;>7jvPU;VE^KM_#tJ01nV8TYDHY#L*k4sV%?Q?B|^!DA8aqPfDP)H zBvx>#(qJ08B6-oF0k*4-&f@Cik;+#S#>+nYv6RH!Zu-XBI`lA?o6HL$SrI4j#>B<; zLGApQ^s|5+-h&^}KNC9P1zh_`etX=XN}h^rAb!(mcN#&Y$-k>1W4IFFOw@2CGChkY z-2QDe$D&4~XcRJuy-U^Iz00cDfa##(fFf+n%%FM+ay0u;h7l{_TAGaYHv<7!rnE-3 z;gzq^EyX8ix$z7qWAljD_3n`G+$nkJT>4*XNXxw*(iKhlM-|i$psYqkb3+DO#5*DR~3icFKP**0IZ-Ywq)~&Vely%hj`qSyPUR! zruD@{&?Qm%iSrZv+irF>k_YtBC}_aMs8sqQbtCkwRY@RRQI>rA?*4%pYDJg4FR}Oo z3+t@eN&&oqi3fUp^R?yUmg9$VO00*6w>T`2Rl%dt)Z?mV$a`LE1evK+F_TS=@DvU)@q-K<8 z($Z?WapIx3eEh3<6E`%AxIt6!l5UW`an0RJDX%~3a=x!u*dT1RPr@TPePAk&4fgL_73fJY2HimAQQl;)!`zKT$PGFMNPqGCPW{uDny z=L*Ms!8bgzdB6uHyL_1|U`?!3l-+>Bc;6;D_Zxd7tIkTDgz!F2?(Ed+&Ou{x#*f)& zl54B)S?QuV#i1g?UqaBR(|Fmms2X}60^VEJ`g}pWRS%)c#y52Gto#N%!v0I-9^E99 zN#5EPj!DQKixqEUnntV8Vr0di!9^hN`{^ex+fN!gR7IO>xt9nFI6JP_m+DYpn)a%O zaD0WLI1+_~T$p>++X#<*?HoySiz{F(p}$MaIPY%ARqE*>PUh|y>$l_hi|_uW$()qR z+ItGxj;ipEOm3OhuF~QZNC*l$&4Dg-Xg~rx14VqIty*8ZD)rQC9`RB4CoDlx{MvO0 zmpew9AuF^H)Yf-?VK(2d6MKxCnRh;(ly+N4zw_BV(PSt_lJV6QXesHA7<=%YE|^~C z_V%Mp^Wy7>kLIJWfa>_dYb=Ht+f=Qi;R16vr=2{6?=@Ub#xE8ZN(a6ZpVAT_fp%YR zJU$EjOwUGUcjc4xl6CZ#tH<46~4W?@l_ajrLmb+c|1Z`>IF0^Dh)(f-OTZO%&j20y&5w?zZ`vw?)R zDohpS_7k9EV`I~uah||(-1l;ea5sh9)o+Veo$Ic2*ufe?tn5XVY~(rwcGD6M@WX(h zMX*s7$+16|R!ATofBO2DvX|Y%Nstya4NZBpp zeUAIHf)lbt={{?Aj!3*EaECXEXXZFyZrn%wVF2G$4d~G&2BJ~yP)(=?_MwMBoiKtV zgXXr#2&8o7w8gzYc*{|F|B+Ddx{lVzJsnKtIcE~`?EOBVXDWomBS{j$0L4bA;6fmJ z)w43c51jbKag?7>XgUqU?}LsvM&6Qqc2bO%ytQSwbkXejPNL`V1UBEKa^ht6dw=Ha zxJ4yAi=U;Wi|oF!Eb_tI%vIYl?tfcXRfvlTreh?0sd<0mYF7?V7Hy~kZ|!R$BLi=G zh859msOV!!ZDb+190+M~M}#7!qk~Y1vPVWUkBg(1)xbrB%|%}xk#uX)C$dKx?_KAQ zCb8RWagY4F<#WfbTDty&dgek24GS)y=TR8my~yoj!)8h^#gGBT<9=%OUch>D_pWT4 z)6hi(i+;`696zfXYW;U)&q8ZCacaDI(skr)(bu20Ryrz(6RE~tA06tvO+;=$IrW?F zkrYSio+ySBMxO*o;PUerZJ-7k)84xA$o1-@y_M7-Wr>vT1Z0!bACloxj5oWRB?hj~ ztQl7wW~`D;Rejdl0uD z$-nEaU2ssIK=^8`?Rq+be^6ZKHNpR`J%UNbYl-!U^g7lu{_D!#Ay%Re%mg0QVS>U4 z;g>X`Az4Dl#o<8lhQ;HFa79@2pVCVghDL?}Me0d5+ShAtu-k0;PiMQ{INN^=UV^%EHUKy+C*dUxYQa_8v321aj`G0&=bbjral{GAK2}Kz_n92>y4V6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..90e9e036ae2041089bce3de47d423e76f4babd51 GIT binary patch literal 12386 zcmY*gRa9I}ke(R^7~F!};K4OW2pTlOA^70#4nYTp;30UB;0f-7y9Br3?u6j(%fAnM z&OY3GU%I5fuCD&7Pu)lrC0Q(VGIRg{fF&;{rS|ge`0s(Byu8(x>`(y!E@pWt2@NlU zLl~OZE9nI)eS)vz&=4mQ7X_X~TgG)c!7Xf(#tiFl{ z&%O4yS*mdusm1?)X>x8V#3XaPfu(#(yJRxj|F}Qf9JFwpV7sQ<^$CktL6(tLNKW`N zfv;V6lEto+;#()!B%+(^P+=iYSi&W~&du{fl=S_g>^IGYQcsP-e75phcUX(qPvu*G zpZ3XLTR^~5%<8)14W{VF_V;a3wsIA$Z|40-bGa1I2(FJN+qqd+=G)}=pT5m4UNf10 z`?TnacEnauZ?C7H`M^hWMZW##DuBTE;|WHmPS81+7>xtZ33TH4fF00nd7|UHyNW<* zC2C|WkKs`v8kCx!yf^mfeQlg-JW2-X9-zBl^l*48wyaE|zJCSVDD%mR2HvdYG?2KsCsI};qGp?7ui z3VCaGljU%?>pV0UC>J0n9X4F&AIT&=N=p`In@Q2-gp}4B7BhGx9pdBM2@ukJql1Jl z2Y0ImFA3i+MqTI(@UGAIjQ!oa#X5I?&xMg~@ai?~I9rLrej8%YpO|JuFXUaoz<|a` zV$AV+9ki*Wm{1xPbb~Y&X4z9?fTu59OwjE+JU7nyL<#!cdw+Ny*?jT^Y4Tj8)B!Dk zJwgWU43mdD-mlaWO1O zX(yW*Aq&uHCEbDbRSVTUD?b>L4QnpO$;rj#ObW;w5~q@E0Wa~U69hH6s$~r<)U+)3 z31%$J|NQ19(pp?i%!ff+BO#{ChrP6~= z<%9j{ZS)rw^L7&xfBfjy)Dy_PyMwsD&T`@nGN&XvJk`mI&)!a|cc$;S50~)u?7)yH z=Jl6?y35OiF7~+F^`Lrl$yX@PPH8M=C*rpELED6Jm*}c+<(6|}b^OTl0#Owe9;8tT zx^Cx{?}hMgIibWHj@n4&k|cQAty%A3d83qdp~f~D`LmoQ3ZpbJX9cM@GI13sHZVHP zF~i&=9buBZKorI+;omA0Fa!G%+&2+i)(rN7NMAP2@G*a^*b`b|$l9Bw1~DF8M%pGS zvOFUYvjR($_B5nev3ZIVbxFpTZ&dq$pRFf1?T83gojl*6Me5ax-5T?II!RlMa$PID z`=0!X(Ss@Jp>5*e0LKK+2*1qC(F8}<-YOM?kxJ$x>!G^*_LJsE-@Msb>X2>1+L&Me z+LQiODY{N4hY1;%c5z)Q>3_eJsW=wFjfAuQlv{{TGS8)h(}0*_K54AyU8$%=Dt{SJ zo<)3QU4nCTT1F1E6CcXI6Lg39rXuXIO1ia?*EPTsYA70o2`d-;CmeW(wI5jQjz<@h zchl(&Xfc7A6D*LPI5X0tth6 zs?g`p-P@<6YY5_UGsDR^={N>#DLry)H4<-s6I+ol$CtxtX^w39aYeza@J#)A1`T4|-hRCm9C9D`T7Z=h(+gM7ROP z5ap8-!F7eB!P?4GOZwvT)#1|Mdp|iL=IXK8(8X)dE&d1h*Vjv^*Urxmf31&}IdPm( zS9H3{{B|r-%4-l!A`M6L5=)()T22)K)loh%YiZXr&e!p)>?^AhSD$AS6w1X?efC%N z8lNcwl6;QmYL+sxk2bTOc6E=|={6e)!@Z|`;sccRxbWB(65Twml6&+z15t=cc+XO_ z=I)cCm=F{isltc@BjqE<=jD;iW~bz4uhR{}o2S_^L}tZUvR^-5@Hf8TXJNM~f}q`xy;-yy zhe>SL;K7QQF!G{VH#joqCNh7jMqny6XFUR*5U^gJuOWGJoWv*=ak0Q8+=%gJ*PPzy zW`CNS=dIaL8!L+Ub$eTTps?pt^uK#d%hAk@`c^gQefcY&RkpSr_p5e4R4pZM=Y7x2 z&aYyg>!J@kmH(R5rl<9Dy8Lc8WgDBbN1dAAF~Irz$8s|x#a@**tU#1*S|B~2{ndck zXj@u*YkoHa;K`|{*R~I=s@;?f<%XhgZch0<&VO`u*JuQ&(+7g8m%yndnK6H-#wQ3 z>&(v900xlYeiH!DBVX}kDctbz`>6WB2HEL{Sw&1n1H(FW9n$VGU>R%F_ zZ0h{9fba)6{xP241vYoFq5Xo*cS?D9t0U3xwlSx*Okwlv3XBM${8dksqgVjuF6Z|= zVD+vE=&h-*^ZF6J!{`p;zLOBmvt?F${38{QAykYI-h%%v&;>L{d+!UIJMRO%sBDn{ zbWraG6w$@A{)9;f*1g}doG7Pv*kL?SaOykQ1j;<+GW~-=_5%n9;t~h82y<}X(EuE& zfh0oC53lDh-+*J&chMV|vcA`Hv=JjV>u8Mvhrh?nSZx2P_xy8l-|KZe$6Fm% zhVcQdW0Ou?#00?16&WVaYfY*f*oSm)x3pAtdTdE36Ouk>SEWVjE3gt^tD*a;Ib6&A z3~4Y1fW?G@i{y&T>3ah&#bgJTzYsB)FEBYb{`-49>CUBZs zEL&8mJdK%EmKSmga1KQa$6o=CZ~oFO5$^0KWEM zz-ZQejL%x=M0ItoR2l0`(*ncnTTeN4KUUp^N&hpp{yKfC&jPqyGN7Z>+g)VZL58c~>C8XtcwKyy-7 zOMUU}pxX!{u45D#QnU<(Blk=l#recK#dsi^AQ8x`&rP8|_>PwvYph{30Q11eD$AAT z{$Px4k{h_!N*>x$&!-)Ir_oK`p02NdWOPFD0`so}x~E#j*3f`AHbfCXpI;UAo|3F^ zJ#Fr{ex?s%-WFlV!9G+le)b#VS@tE{jQsEw>i@v~3{vv3A83JqrM^| z{uAUfPN4H+VdWX^@!|>21YSGfz=gBtPP&K$59NuEewn*n%h@5NnYl08O+)&?YVY{B z^V;%1AN;@or^&EdedWMjkos{AmEzv-eo%TWs7qnV9m8{$=f+-vS-N&F5oJ2&U1gLKYK z-}y;tI%n_Hf3M`DUVgpB>&At@y}4~TM#OM>xwThft{JRvM(RJMr&6xv|8)kOKc3I( z(GIa3_7@+=#SCLRjHRG(;YR03?N1hq5HRrJ zr*CX2KArh2|=+H!s%{{q5# z%S)mMp8v*bziK#ebTv2kA!8%SY8gGlU$=k?{u(@k%-oOpz2=Sqw=(@V^feP;w?^DY zBold39I(sz(0S-nqpewA1jIJ^c`=N-=|9B6ca1Fj$vHR#uzB|qd>=zs3_Z6{-_gPa z$b$f*ctEyqNoA<;sPlSbi@6puicg{>L`&HbM|gn+NmnUQz?r&MCugt=h)W;J?G$nJ zXGi!QW|OGZ+1rEF_hmmuIz9!~43fA8SU7w+&b7Qo{SO&jq<79q>Xjns#!5yg&cR_L zj-|QtB69yR4--~u9K5H|+ zBv>as0NF5jGxgicA^$b03}<|*fYUf|3BfQi)cI+fXaF|)bd&P0hH}#M$PiOY2GEi$ zcjvb@ie&9Z5OHztzCIv}GgRJr(pTj3U!r%w5D&%!9Lu}zxT|D=*3Gf1C2nm^s`!E| zf?qiYr{42gyZhSdFa%8MrNu$kqNrEakNoc#=ihj=@CUcGDBayQ!LJ?4{4C&GZ+|cO zjo~oOHeT5#lU%UD6WgEVR{5FP^<;}B_^lTFocnptXw^D&q?e#OPs8)+T)IvHUfCc0 zk(%JF9Jp7_8|T*|De~7XE6zCt4F{J(Zp%>@HCjlq_z%a=Pcnur;lcS1tE+)8Cp49a zD)5*t(Z7uY!4AOEheqf-exxafJ7w+t3*T&1#mK|xn7^yQ+E9p@oZ83seco7>x4roO zWF~m`hel^pD0s>;z~c^Ppw)dG@bKZ$eE&ETxU$dcM`f843aDg1%d$in9GoHOQ2b6S z;Q#Y8ui(cov+ch)e3XMD-mi~LER4G4V%S#nkdA=P&B)%BwP&Og z^#H-!&{E(fHhAJFtG^-?p0G*;l{7Q*RMFHzwcsCt{6VOsIUh@>zZFk zcD0K;IvGS+$5Cj7mz51jq4bfrkqW~5EEdpOAp1G#QJ_^BE+2@sB$oM>(N>{#{jnl@ zJDK_)M{|HyPCXzt(<@u=b8%d$K1?zdH=~~W=mO4cj~d*41n#;RXFaPqeF-Yg_9MAn zQYf=Wn`p-U_QkUq)-RlCr~ZTeJFa3%F!keKPB*`AwfcTRW8ewW^^IcdHK*%RNGX;J zGp+>)l6r0BOT*Bvgx%(Xy#0q+e_O9$iF#Lgi0seQe{8+HiDHqkSO_#dJ?%6WP^qd} z=+J2y#6d&A*Tjd<`W|TsAMRdR$9+pGNBj5E4LrVo_iPKa_2ude`RQxD=?#LuQqoaB zi>7>o43FVjG)QA(Rd2vm`=O1W&?ZH9^Z42T$4Sb*Yaya<3G6TvnaHyz8TF-ld zN7HEpZl~$BKmMR7X+N!R?Jh-8i{>N5$_fFvfM#KdUAz-eG4n!FkD@lwIq`p*FhFaw zfc1}xAFt*Na_Hd1$Q|xL4m|$OhsPT9h04i3O$-vhEWZFG&+94DY9BR_4LaasU%~c1 z<%y;hi<|ksRY-W1^rZ4yt!tkqyxz*##-Muaik}k5^0mbPfEiUHJZ^A!s#WA19$TKj zNWJ>72lc$^-EG{>uAq)>Iit9qvHY~Ojw3M9YmY-Ux{)k8AM-`ooBD&q2=t~A=O{6$ z$Wt5;b+v$4`c(C7voJkPrtiT_$O6f69{X2$^1|lY{S*PQT<-Shvt_a@Dapy2oEMGr zt=pKFfx2`6kHE%e@v%gBLr^39p)PsMZhxe7t(p~NMZcv;j+04*C(F@89k{m%N^M~Y zJ(&EYWTiFHs{HHUg3Wt&AcgQLRl|mgqt0hc@@+~QPDNSLP-mzuOK!KwqFob|`K9F;4d(QCK7y=+4eh*ax(qNaq_wz+~x z-i0d%S|rI7Bw##F;)3IA5UtU}N4aJd4|g__SU{uZy+2AL<8L5Z`*tUf&P-EGwTh@I z%lP6~lsgVvK}F8ohQ#6?2`(V*N|?Xjn;$Z`58H9afB%M(a7f^GT*NqA@-Wu_2Bg67 z;V0`k|9U4xlKHGc)!o-r$b!y}ZX|+k+)_e%QSLAZ~?uU+|2W zL%LHp)M-9xeodw;JeY9_Zz@Co#P>aJM?_F84{dW4>2*oUnH#tO8BV7%Bbv9QP`85% z=6Vc7U?90Ju7Eg-urP2y4Gj98Rd0K0m4`q;j^Oioy%?Qd8L>Y zL9Zc4V)d5*r}+XrI2yEutC|0IklLAqKM#v$pVkC?Wbtubyjt!){57ZM$X4=~ zwYhEf1PiY^26dYS=T%9xVB3}luoCEVeIOI5;U_+ z@74U_M`gVt`-fbVIjOmCSjpLwVRe6zIX0Jx*6KqJu*)<7kJVlHAFk%xF5H3NH0KC) zP=RQ0CMk`00itK_z*on$ zuCV5z-)kHWzxU;&8Q5jnUo*q;)N3hQrvYGbuc7UV*hjv%jn?=GJN}IEpTbOldeX3* zKV8f94ZbkIc>|*>%<)@dCk(T#jIO~du7#p5Cm(TvNR|W-fc=}YeL5P8yXGf-P zMwsWu7Y{30S_b5o@|{J%vc}`OTo4Rgb3;@`~WaFa7sjGKA8iDA*YPEnq;k zM_xq+W~NI;C{^@9OnuD`7}lN;{%4#@H@v* z@F}Y4$lNPWW>c3NJ14;(Oq4)<0`ZJ1`*>t@QhvHzPE|rbfl2|&m_7794k&1kjVY*k zZ$&sM<1zjk9bm0*O=^I&Zq*ZXIioHjQ%`}7-B3L>y2Jp~C&rU7d^6wT*IpSSegC!X zWLqKrs}B&6uVE>YlS%%g7I23><8RuoW<s@3mLKFbZh56lP;pNeMW5m!P3cw|Oq zW4@o-4@nY<(s0UNRe%g2b~yWRcx1@@ zO>+`(Gg&%vy2Q@gH4ASBg=c1gO&PN=Amnwgd1^c?Q{#}kNdBUlF*m$QmlzTOe!u{b zRyi{)EVrA81N0SBB(jfP^-A=e1g1Ys-71X%xi&3>Z})`w+*P8k7<}xr?@gfkwY+Jg zOCRJ_45MK=Fcwv7Ab_sOecVmLu$5*QV2bxrpjNHYd!QWwkDA`1^+gv-D*7_M4#`;# zqKpipH0V&RbA&$5{vK*^Hm?+Yg8~Qg=#ZY`c zA_G2;?&z3PTVHPMj<2CBq=OjZFMiuV06)h!U@HVK)Fxn*?o@xLC4zmev;$_w3;Phr zlb4oL{B4JQV2odfVPaDbNbJl0B8|-88O)7Idb|wUn-TfbtQk7fMn0^Yn6>OdBuH*t z1acBW>WQ`p3W4T+>c5DOjq^i@oMb?)!uAY?Bp`a0 z7EgJ*675M$aOnP-1GQuLMtsf;9TJCwO()iUuBl68l6qPY>dbqrwWDFhe1|PpP%}Jv zHVxMW4W#G6poU@)j5Vt0O%$acx6dc_Glg@IU{a$LBqIwp+$$XJa45la@{Q&`gU_MZ z-BQ@b7rJ|WoA zb6gylJA7WxD0=*HU!`CL1B&dSX-z2g>m?7x?THeypPri=iI}%hfwVhIX?veKWQ?`EVQiLLPs#zs>;7Sh%1s8GU`8b5 zZ||f1gM4dwQ}vwkeZ}OpAlVWJi9Gq%^YYbmugoSu{WXj%M&PJ-`19){TvmPW_bVs` z*B~w<>Fo=wg?L$Zpiws8N+u(_vH)(i*vmpW>0sm;@|+$i+a_kPiXZUopdi=9T2F>O zM3cd&-k?gLQX;qLgS#=N@x&Xb7T6;-KLX(8#Jy;VhA(#&E{}}$ZAeRY<_?oY`h
M}&;ZL%9rUN=9mJ+|UBFs&CncAXm9;yzx^K8@z2j28L;m;8_h(hH9bMQ0DvzjGSI5|VDlw{3BMCEk5SS)2ukH=Z;R3P}fbBqVqXGskAB zY0F#sb6%1B@n%GjydrW#LNGgNsJ_hRFh4aCCyWlVQRG?i7UY+0mq*(?cvvu%`6HYxFH(SQ#DnU zR6l#-xe(~0MAQX_dgasNWd3}gkje(UDxsmiI zBU$X15T&OJNn3Cb`9p_&^@nzfo?XlOMU{TDQwIM*`rnZ8`>|!oIFW#vWV#>j5gVuixvI zC&Q29%s#ICnsbHmb{7V_hWMO_$KPrA?p*6?X5bf~V{)baZYMa`?AJ84NpCGszQcXl zd}`)#(kUQ}4Wh&3loGyaA^_JV)mWh<4O|DO*R^|B`iW3K@B5}tfb(B_B(WK zHAi$xzJv7}>KqM6ZMu-D#eA$XdMF3GN2oOoe39t$YeE*?b6(@LTPc&Vc-6J^{A(?d zo1}C>h7YR@GcAhZY`1A#g=~LUx(*}Vx-my>5=7!-b7l+x+(hpxjpOg)QOyuL@4}}b^$1Q#T}x* zcq@?8SGA{R<&5}71VU6}perGR$B`VXu(9-~*p36(P4fUS zI;rs{osvEk8&6eJE6HZwA(6Fq41tsm=@4-zzsfoLlW!;XYw;ZnY@R3$8nDy-^4}m6V? z!5A&~8lG+SM{!LrB@)Sm;>Tp6Gw%xFm*dHIrY{?CR+^0}UKA0|We-EQ*S0ogMMh}r zvW2mhXc!L9y+!}=dr%V{bbA`qT(!M(oU5LW1@r`C8<)@t7G!iZa0K)8maBLWqh zre?%;!}g~H`Z5C@i&DG*ACsJ8+iRi#Yk>qy(}FvhG?rCg#%OOIQ-ue0 zsC?>YBW9%6qB|(<+6kLQn;^aiJZY>_xa-va;$XtR(_Hh8`c7nmO)`gZUWv*C86$Ae zsS;v)P4^NC%zvWupd0NHg)p^d-vJybRy^_+(isCEQh5j47rV^2>Xsqpknf!#DMv8s zpOG+{bi!PWl!yJ9x^Jb`1Isnku|*ZN6VfXBzuOtDtsMHjIb-@3sK4jNH|Mu9mWUU? zn?v)oadBCz25@{BPO4irGFke#yGU!)<;H!6E>4ivAlo0{%waC!yYc@Ss4B&UF&b|$ z4Xh0@;JG2}W!&9HkJZL#mY(@$I_1#`(x#o94VX0C zrOy#|ag;OmNT2)=oG9Yd#05bUQk%m!Z z6n1ZZz5Z+Xl52ee$w9Jf*TK9SXq*-Jfapkg8A-vZxYNhKi}RbpZ^i~-c;|-yj;Avr zm{x&gh^{R-?TYi%7RtuBVRG{;Fk)}y?=EOcEHw^PBl>j@-k4o~6LbRUl{{-ax z63`pha=v2TkcALt2~OK6=v#NCgD$A<}j&?e<79DeZYzaZM|3}j?~Q13tsBwvG^-@9j?xy_`q_UxeI&Ka3> z4@-@IR++OW(CVR>HCHeam!BTbYM*|1AO1umWaSh1U@^6wqxJpx`fepVP4KlvqM6qk z31iSSu&ht1W#|mcJNzqpggEs)4blgAJ;UW~&4;|Wnk+*rpM2p?Wd8Z>I*H@DVO0wV z#&?qtgB(A#>twn<&Zt|?Os2W;d6V~G11vVj;(Eg_8Z@u)*)ohgQRO`uzo)DGk;S}2 zWNa_LzJ487dQg6gNhrECb;jg7y8EkQ&jc#gd08CE$=>KHATiDGNiIQmVRAw!-f;#& zI+i540NQCcZG-lYL(;4jY7vv?o-+vIteE$qwCVIdvsGr4KCWkWMO(uFuplsi!{ ziW)xUqm)OIQJ8*hc$-ZAt`7EQ0pFg*&B@6pf9pEZt*B&24$Q}R;;73S6dPO^Rx#4N z=(2Mbl7>gThhICp8w@i$@8gUSS`;f9X|;M^TQFWYyz6+fWyweg#E2ewdSWEMWha!V z(9;`9uVmXHiNRp8<17C91`ZsP*gB@08)1pOD_pFsy#4l&k7ccQ_EU-IglvmAjgQNM zdD2SVR2>9jR7;>7jvPU;VE^KM_#tJ01nV8TYDHY#L*k4sV%?Q?B|^!DA8aqPfDP)H zBvx>#(qJ08B6-oF0k*4-&f@Cik;+#S#>+nYv6RH!Zu-XBI`lA?o6HL$SrI4j#>B<; zLGApQ^s|5+-h&^}KNC9P1zh_`etX=XN}h^rAb!(mcN#&Y$-k>1W4IFFOw@2CGChkY z-2QDe$D&4~XcRJuy-U^Iz00cDfa##(fFf+n%%FM+ay0u;h7l{_TAGaYHv<7!rnE-3 z;gzq^EyX8ix$z7qWAljD_3n`G+$nkJT>4*XNXxw*(iKhlM-|i$psYqkb3+DO#5*DR~3icFKP**0IZ-Ywq)~&Vely%hj`qSyPUR! zruD@{&?Qm%iSrZv+irF>k_YtBC}_aMs8sqQbtCkwRY@RRQI>rA?*4%pYDJg4FR}Oo z3+t@eN&&oqi3fUp^R?yUmg9$VO00*6w>T`2Rl%dt)Z?mV$a`LE1evK+F_TS=@DvU)@q-K<8 z($Z?WapIx3eEh3<6E`%AxIt6!l5UW`an0RJDX%~3a=x!u*dT1RPr@TPePAk&4fgL_73fJY2HimAQQl;)!`zKT$PGFMNPqGCPW{uDny z=L*Ms!8bgzdB6uHyL_1|U`?!3l-+>Bc;6;D_Zxd7tIkTDgz!F2?(Ed+&Ou{x#*f)& zl54B)S?QuV#i1g?UqaBR(|Fmms2X}60^VEJ`g}pWRS%)c#y52Gto#N%!v0I-9^E99 zN#5EPj!DQKixqEUnntV8Vr0di!9^hN`{^ex+fN!gR7IO>xt9nFI6JP_m+DYpn)a%O zaD0WLI1+_~T$p>++X#<*?HoySiz{F(p}$MaIPY%ARqE*>PUh|y>$l_hi|_uW$()qR z+ItGxj;ipEOm3OhuF~QZNC*l$&4Dg-Xg~rx14VqIty*8ZD)rQC9`RB4CoDlx{MvO0 zmpew9AuF^H)Yf-?VK(2d6MKxCnRh;(ly+N4zw_BV(PSt_lJV6QXesHA7<=%YE|^~C z_V%Mp^Wy7>kLIJWfa>_dYb=Ht+f=Qi;R16vr=2{6?=@Ub#xE8ZN(a6ZpVAT_fp%YR zJU$EjOwUGUcjc4xl6CZ#tH<46~4W?@l_ajrLmb+c|1Z`>IF0^Dh)(f-OTZO%&j20y&5w?zZ`vw?)R zDohpS_7k9EV`I~uah||(-1l;ea5sh9)o+Veo$Ic2*ufe?tn5XVY~(rwcGD6M@WX(h zMX*s7$+16|R!ATofBO2DvX|Y%Nstya4NZBpp zeUAIHf)lbt={{?Aj!3*EaECXEXXZFyZrn%wVF2G$4d~G&2BJ~yP)(=?_MwMBoiKtV zgXXr#2&8o7w8gzYc*{|F|B+Ddx{lVzJsnKtIcE~`?EOBVXDWomBS{j$0L4bA;6fmJ z)w43c51jbKag?7>XgUqU?}LsvM&6Qqc2bO%ytQSwbkXejPNL`V1UBEKa^ht6dw=Ha zxJ4yAi=U;Wi|oF!Eb_tI%vIYl?tfcXRfvlTreh?0sd<0mYF7?V7Hy~kZ|!R$BLi=G zh859msOV!!ZDb+190+M~M}#7!qk~Y1vPVWUkBg(1)xbrB%|%}xk#uX)C$dKx?_KAQ zCb8RWagY4F<#WfbTDty&dgek24GS)y=TR8my~yoj!)8h^#gGBT<9=%OUch>D_pWT4 z)6hi(i+;`696zfXYW;U)&q8ZCacaDI(skr)(bu20Ryrz(6RE~tA06tvO+;=$IrW?F zkrYSio+ySBMxO*o;PUerZJ-7k)84xA$o1-@y_M7-Wr>vT1Z0!bACloxj5oWRB?hj~ ztQl7wW~`D;Rejdl0uD z$-nEaU2ssIK=^8`?Rq+be^6ZKHNpR`J%UNbYl-!U^g7lu{_D!#Ay%Re%mg0QVS>U4 z;g>X`Az4Dl#o<8lhQ;HFa79@2pVCVghDL?}Me0d5+ShAtu-k0;PiMQ{INN^=UV^%EHUKy+C*dUxYQa_8v321aj`G0&=bbjral{GAK2}Kz_n92>y4 + + #7AAC2B + \ No newline at end of file diff --git a/app/android/app/src/profile/AndroidManifest.xml b/app/android/app/src/profile/AndroidManifest.xml index e7e8338b..0db8f45c 100644 --- a/app/android/app/src/profile/AndroidManifest.xml +++ b/app/android/app/src/profile/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="de.mensa_ka.app"> + android:name="io.flutter.embedding.android.NormalTheme" + android:resource="@style/NormalTheme" /> - - + +