From 0eb1c0722958e4e050888902fac0be10cacd2d8b Mon Sep 17 00:00:00 2001 From: susch19 Date: Thu, 6 Jan 2022 21:15:38 +0100 Subject: [PATCH] Added possibility to add products from brought list (#11) * copied the iterable extensions from smarthome (maybe move this into a package in the future) * added click on already brought items, that currently adds a copy of the selected entry to the list, from which the history is * removed the nullable int amount, because it should never be null, as the amount always has a value * removed unused imports --- .gitignore | 1 + lib/helper/iterable_extensions.dart | 109 ++++++ lib/localization/nssl_messages_de.dart | 1 + lib/localization/nssl_messages_en.dart | 1 + lib/localization/nssl_strings.dart | 389 ++++++++++++------- lib/main.dart | 16 +- lib/models/shopping_item.dart | 31 +- lib/models/shopping_list.dart | 76 +++- lib/models_json.dart | 33 +- lib/pages/barcode_scanner_page.dart | 23 +- lib/pages/bought_items.dart | 103 +++-- lib/pages/change_password.dart | 39 +- lib/pages/contributors.dart | 60 ++- lib/pages/login.dart | 46 ++- lib/pages/main_page.dart | 268 ++++++++----- lib/pages/product_add_to_database.dart | 59 +-- lib/pages/shopping_item_search.dart | 39 +- lib/server_communication/return_classes.dart | 84 ++-- tool/env.dart | 12 +- 19 files changed, 933 insertions(+), 457 deletions(-) create mode 100644 lib/helper/iterable_extensions.dart diff --git a/.gitignore b/.gitignore index d3eea47..0652200 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ android/app/google-services.json ios/Flutter/flutter_export_environment.sh .dart_tool/* android/app/src/main/java/io/flutter/plugins/* +lib/.license.dart diff --git a/lib/helper/iterable_extensions.dart b/lib/helper/iterable_extensions.dart new file mode 100644 index 0000000..ea5b6b9 --- /dev/null +++ b/lib/helper/iterable_extensions.dart @@ -0,0 +1,109 @@ +extension Linq on List { + bool removeElements(Iterable elements) { + bool b = true; + for (var e in elements) { + if (!b) return b; + b = this.remove(e); + } + return b; + } + + List copy() { + List items = []; + this.forEach((element) { + items.add(element); + }); + return items; + } +} + +extension MoreMath on double { + double clamp(double lower, double maximum) { + if (this <= lower) return lower; + if (this >= maximum) return maximum; + return this; + } +} + +extension Maps on Map { + List select(T Function(K, E) keyFunction) { + var retList = []; + for (var entry in this.entries) { + retList.add(keyFunction(entry.key, entry.value)); + } + return retList; + } + + MapEntry? elementAt(int index, {MapEntry? orElse}) { + int i = 0; + for (var el in this.entries) { + if (index == i) return el; + i++; + } + return orElse; + } +} + +extension Iterables on Iterable { + Map> groupBy(K Function(E) keyFunction) => fold( + >{}, + (Map> map, E element) => + map..putIfAbsent(keyFunction(element), () => []).add(element)); + + Map> groupManyBy(List Function(E) keyFunction) => + fold(>{}, (Map> map, E element) { + for (var r in keyFunction(element)) { + map..putIfAbsent(r, () => []).add(element); + } + return map; + }); + + E? firstOrNull(bool Function(E element) keyFunction) { + for (var item in [...this]) { + if (keyFunction(item)) return item; + } + return null; + } + + Iterable injectForIndex(E? Function(int index) indexFunc) sync* { + int index = 0; + for (var item in [...this]) { + var res = indexFunc(index); + if (res != null) yield res; + yield item; + index++; + } + } + + int sum(int Function(E element) keyFunction) { + int sum = 0; + for (var item in [...this]) { + sum += keyFunction(item); + } + return sum; + } + + int bitOr(int Function(E element) keyFunction) { + int sum = 0; + for (var item in [...this]) { + sum |= keyFunction(item); + } + return sum; + } + + int bitAnd(int Function(E element) keyFunction) { + int sum = 0; + for (var item in [...this]) { + sum &= keyFunction(item); + } + return sum; + } + + int bitXor(int Function(E element) keyFunction) { + int sum = 0; + for (var item in [...this]) { + sum ^= keyFunction(item); + } + return sum; + } +} diff --git a/lib/localization/nssl_messages_de.dart b/lib/localization/nssl_messages_de.dart index 82837b6..8a70354 100644 --- a/lib/localization/nssl_messages_de.dart +++ b/lib/localization/nssl_messages_de.dart @@ -91,6 +91,7 @@ class MessageLookup extends MessageLookupByLibrary { "newProductWeight": MessageLookupByLibrary.simpleMessage("Menge mit Einheit"), "newProductWeightHint": MessageLookupByLibrary.simpleMessage("Zum Beispiel: 1,5l oder 100g"), "newProductAddToList": MessageLookupByLibrary.simpleMessage("Füge es der aktuellen Liste hinzu"), + "newProductAddedToList": MessageLookupByLibrary.simpleMessage(" wurde hinzugefügt zur Liste "), "newProductStarExplanation": MessageLookupByLibrary.simpleMessage("* kennzeichnet die benötigten Felder"), "fieldRequiredError": MessageLookupByLibrary.simpleMessage("Dieses Feld wird benötigt!"), "newProductNameToShort": MessageLookupByLibrary.simpleMessage("Dieser Name scheint zu kurz zu sein"), diff --git a/lib/localization/nssl_messages_en.dart b/lib/localization/nssl_messages_en.dart index ea81804..336c2d5 100644 --- a/lib/localization/nssl_messages_en.dart +++ b/lib/localization/nssl_messages_en.dart @@ -90,6 +90,7 @@ class MessageLookup extends MessageLookupByLibrary { "newProductWeight": MessageLookupByLibrary.simpleMessage("Amount with Unit"), "newProductWeightHint": MessageLookupByLibrary.simpleMessage("Example: 1.5l or 100g"), "newProductAddToList": MessageLookupByLibrary.simpleMessage("Add to current list"), + "newProductAddedToList": MessageLookupByLibrary.simpleMessage(" was added to list "), "newProductStarExplanation": MessageLookupByLibrary.simpleMessage('* indicates required field'), "fieldRequiredError": MessageLookupByLibrary.simpleMessage("This field is required!"), "newProductNameToShort": MessageLookupByLibrary.simpleMessage("This name seems to be to short"), diff --git a/lib/localization/nssl_strings.dart b/lib/localization/nssl_strings.dart index 7c5b814..b6af7bf 100644 --- a/lib/localization/nssl_strings.dart +++ b/lib/localization/nssl_strings.dart @@ -23,177 +23,286 @@ class NSSLStrings { } // static final NSSLStrings instance = NSSLStrings(); - String options() => Intl.message('Options', name: 'options', locale: _localeName); - String changeTheme() => Intl.message('Change Theme', name: 'changeTheme', locale: _localeName); + String options() => + Intl.message('Options', name: 'options', locale: _localeName); + String changeTheme() => + Intl.message('Change Theme', name: 'changeTheme', locale: _localeName); String scanPB() => Intl.message('SCAN', name: 'scanPB', locale: _localeName); String addPB() => Intl.message('ADD', name: 'addPB', locale: _localeName); - String searchPB() => Intl.message('SEARCH', name: 'searchPB', locale: _localeName); - String deleteCrossedOutPB() => Intl.message('DELETE CROSSED OUT', name: 'deleteCrossedOutPB', locale: _localeName); - String addListPB() => Intl.message('ADD LIST', name: 'addListPB', locale: _localeName); - String contributors() => Intl.message('Contributors', name: 'contributors', locale: _localeName); - String rename() => Intl.message('Rename', name: 'rename', locale: _localeName); - String remove() => Intl.message('Remove', name: 'remove', locale: _localeName); - String addProduct() => Intl.message('Add Product', name: 'addProduct', locale: _localeName); - String addProductWithoutSearch() => Intl.message('Insert the name of the product, without searching in the database', - name: 'addProductWithoutSearch', locale: _localeName); - String productName() => Intl.message('Product name', name: 'productName', locale: _localeName); + String searchPB() => + Intl.message('SEARCH', name: 'searchPB', locale: _localeName); + String deleteCrossedOutPB() => Intl.message('DELETE CROSSED OUT', + name: 'deleteCrossedOutPB', locale: _localeName); + String addListPB() => + Intl.message('ADD LIST', name: 'addListPB', locale: _localeName); + String contributors() => + Intl.message('Contributors', name: 'contributors', locale: _localeName); + String rename() => + Intl.message('Rename', name: 'rename', locale: _localeName); + String remove() => + Intl.message('Remove', name: 'remove', locale: _localeName); + String addProduct() => + Intl.message('Add Product', name: 'addProduct', locale: _localeName); + String addProductWithoutSearch() => Intl.message( + 'Insert the name of the product, without searching in the database', + name: 'addProductWithoutSearch', + locale: _localeName); + String productName() => + Intl.message('Product name', name: 'productName', locale: _localeName); String messageDeleteAllCrossedOut() => - Intl.message('You have deleted all crossed out items', name: 'messageDeleteAllCrossedOut', locale: _localeName); + Intl.message('You have deleted all crossed out items', + name: 'messageDeleteAllCrossedOut', locale: _localeName); String undo() => Intl.message('UNDO', name: 'undo', locale: _localeName); String noListsInDrawerMessage() => - Intl.message('Here is the place for your lists', name: 'noListsInDrawerMessage', locale: _localeName); - String notLoggedInYet() => Intl.message('Not logged in yet', name: 'notLoggedInYet', locale: _localeName); - String newNameOfListHint() => - Intl.message('The new name of the new list', name: 'newNameOfListHint', locale: _localeName); - String listName() => Intl.message('Listname', name: 'listName', locale: _localeName); - String renameListTitle() => Intl.message('Rename List', name: 'renameListTitle', locale: _localeName); - String renameListHint() => Intl.message('The name of the new list', name: 'renameListHint', locale: _localeName); - String addNewListTitle() => Intl.message('Add new List', name: 'addNewListTitle', locale: _localeName); - String youHaveActionItemMessage() => Intl.message('You have ', name: 'youHaveActionItemMessage', locale: _localeName); - String archived() => Intl.message('archived', name: 'archived', locale: _localeName); - String deleted() => Intl.message('deleted', name: 'deleted', locale: _localeName); - String youHaveActionNameMessage() => Intl.message('You have ', name: 'youHaveActionNameMessage', locale: _localeName); - String demoteMenu() => Intl.message('Demote', name: 'demoteMenu', locale: _localeName); - String promoteMenu() => Intl.message('Promote', name: 'promoteMenu', locale: _localeName); - String contributorUser() => Intl.message(" - User", name: 'contributorUser', locale: _localeName); - String contributorAdmin() => Intl.message(" - Admin", name: 'contributorAdmin', locale: _localeName); + Intl.message('Here is the place for your lists', + name: 'noListsInDrawerMessage', locale: _localeName); + String notLoggedInYet() => Intl.message('Not logged in yet', + name: 'notLoggedInYet', locale: _localeName); + String newNameOfListHint() => Intl.message('The new name of the new list', + name: 'newNameOfListHint', locale: _localeName); + String listName() => + Intl.message('Listname', name: 'listName', locale: _localeName); + String renameListTitle() => + Intl.message('Rename List', name: 'renameListTitle', locale: _localeName); + String renameListHint() => Intl.message('The name of the new list', + name: 'renameListHint', locale: _localeName); + String addNewListTitle() => Intl.message('Add new List', + name: 'addNewListTitle', locale: _localeName); + String youHaveActionItemMessage() => Intl.message('You have ', + name: 'youHaveActionItemMessage', locale: _localeName); + String archived() => + Intl.message('archived', name: 'archived', locale: _localeName); + String deleted() => + Intl.message('deleted', name: 'deleted', locale: _localeName); + String youHaveActionNameMessage() => Intl.message('You have ', + name: 'youHaveActionNameMessage', locale: _localeName); + String demoteMenu() => + Intl.message('Demote', name: 'demoteMenu', locale: _localeName); + String promoteMenu() => + Intl.message('Promote', name: 'promoteMenu', locale: _localeName); + String contributorUser() => + Intl.message(" - User", name: 'contributorUser', locale: _localeName); + String contributorAdmin() => + Intl.message(" - Admin", name: 'contributorAdmin', locale: _localeName); String genericErrorMessageSnackbar() => - Intl.message('Something went wrong!\n', name: 'genericErrorMessageSnackbar', locale: _localeName); - String nameOfNewContributorHint() => - Intl.message('Name of new Contributor', name: 'nameOfNewContributorHint', locale: _localeName); + Intl.message('Something went wrong!\n', + name: 'genericErrorMessageSnackbar', locale: _localeName); + String nameOfNewContributorHint() => Intl.message('Name of new Contributor', + name: 'nameOfNewContributorHint', locale: _localeName); String wasRemovedSuccessfullyMessage() => - Intl.message(' was removed successfully', name: 'wasRemovedSuccessfullyMessage', locale: _localeName); - String loginSuccessfulMessage() => - Intl.message('Login successfull.', name: 'loginSuccessfullMessage', locale: _localeName); - String nameEmailRequiredError() => - Intl.message('Name or Email is required.', name: 'nameEmailRequiredError', locale: _localeName); - String usernameToShortError() => Intl.message('Your username has to be at least 4 characters long', - name: 'usernameToShortError', locale: _localeName); - String emailRequiredError() => Intl.message('EMail is required.', name: 'emailRequiredError', locale: _localeName); - String emailIncorrectFormatError() => Intl.message('The email seems to be in the incorrect format.', - name: 'emailIncorrectFormatError', locale: _localeName); - String chooseAPassword() => Intl.message('Please choose a password.', name: 'chooseAPassword', locale: _localeName); + Intl.message(' was removed successfully', + name: 'wasRemovedSuccessfullyMessage', locale: _localeName); + String loginSuccessfulMessage() => Intl.message('Login successfull.', + name: 'loginSuccessfullMessage', locale: _localeName); + String nameEmailRequiredError() => Intl.message('Name or Email is required.', + name: 'nameEmailRequiredError', locale: _localeName); + String usernameToShortError() => + Intl.message('Your username has to be at least 4 characters long', + name: 'usernameToShortError', locale: _localeName); + String emailRequiredError() => Intl.message('EMail is required.', + name: 'emailRequiredError', locale: _localeName); + String emailIncorrectFormatError() => + Intl.message('The email seems to be in the incorrect format.', + name: 'emailIncorrectFormatError', locale: _localeName); + String chooseAPassword() => Intl.message('Please choose a password.', + name: 'chooseAPassword', locale: _localeName); String login() => Intl.message('Login', name: 'login', locale: _localeName); String usernameOrEmailForLoginHint() => - Intl.message('Username or email can be used to login', name: 'usernameOrEmailForLoginHint', locale: _localeName); - String usernameOrEmailTitle() => Intl.message('Username or Email', name: 'usernameOrEmailTitle', locale: _localeName); - String emailTitle() => Intl.message('Email', name: 'emailTitle', locale: _localeName); - String choosenPasswordHint() => - Intl.message('The password you have choosen', name: 'choosenPasswordHint', locale: _localeName); - String password() => Intl.message('Password', name: 'password', locale: _localeName); - String loginButton() => Intl.message('LOGIN', name: 'loginButton', locale: _localeName); + Intl.message('Username or email can be used to login', + name: 'usernameOrEmailForLoginHint', locale: _localeName); + String usernameOrEmailTitle() => Intl.message('Username or Email', + name: 'usernameOrEmailTitle', locale: _localeName); + String emailTitle() => + Intl.message('Email', name: 'emailTitle', locale: _localeName); + String choosenPasswordHint() => Intl.message('The password you have choosen', + name: 'choosenPasswordHint', locale: _localeName); + String password() => + Intl.message('Password', name: 'password', locale: _localeName); + String loginButton() => + Intl.message('LOGIN', name: 'loginButton', locale: _localeName); String registerTextOnLogin() => - Intl.message('Don\'t have an account? Create one now.', name: 'registerTextOnLogin', locale: _localeName); - String usernameEmptyError() => - Intl.message('Username has to be filled in', name: 'usernameEmptyError', locale: _localeName); - String passwordEmptyError() => - Intl.message('Password has to be filled in', name: 'passwordEmptyError', locale: _localeName); - String emailEmptyError() => Intl.message('Email has to be filled in', name: 'emailEmptyError', locale: _localeName); + Intl.message('Don\'t have an account? Create one now.', + name: 'registerTextOnLogin', locale: _localeName); + String usernameEmptyError() => Intl.message('Username has to be filled in', + name: 'usernameEmptyError', locale: _localeName); + String passwordEmptyError() => Intl.message('Password has to be filled in', + name: 'passwordEmptyError', locale: _localeName); + String emailEmptyError() => Intl.message('Email has to be filled in', + name: 'emailEmptyError', locale: _localeName); String reenterPasswordError() => - Intl.message('Passwords doesn\'t match or are empty', name: 'reenterPasswordError', locale: _localeName); + Intl.message('Passwords doesn\'t match or are empty', + name: 'reenterPasswordError', locale: _localeName); String unknownUsernameError() => - Intl.message('There is something wrong with your username', name: 'unknownUsernameError', locale: _localeName); + Intl.message('There is something wrong with your username', + name: 'unknownUsernameError', locale: _localeName); String unknownEmailError() => - Intl.message('There is something wrong with your email', name: 'unknownEmailError', locale: _localeName); + Intl.message('There is something wrong with your email', + name: 'unknownEmailError', locale: _localeName); String unknownPasswordError() => - Intl.message('There is something wrong with your password', name: 'unknownPasswordError', locale: _localeName); - String unknownReenterPasswordError() => Intl.message('There is something wrong with your password validation', - name: 'unknownReenterPasswordError', locale: _localeName); + Intl.message('There is something wrong with your password', + name: 'unknownPasswordError', locale: _localeName); + String unknownReenterPasswordError() => + Intl.message('There is something wrong with your password validation', + name: 'unknownReenterPasswordError', locale: _localeName); String registrationSuccessfulMessage() => - Intl.message('Registration successfull.', name: 'registrationSuccessfullMessage', locale: _localeName); - String registrationTitle() => Intl.message('Registration', name: 'registrationTitle', locale: _localeName); - String nameEmptyError() => Intl.message('Name is required.', name: 'nameEmptyError', locale: _localeName); - String chooseAPasswordPrompt() => - Intl.message('Please choose a password.', name: 'chooseAPasswordPrompt', locale: _localeName); + Intl.message('Registration successfull.', + name: 'registrationSuccessfullMessage', locale: _localeName); + String registrationTitle() => Intl.message('Registration', + name: 'registrationTitle', locale: _localeName); + String nameEmptyError() => Intl.message('Name is required.', + name: 'nameEmptyError', locale: _localeName); + String chooseAPasswordPrompt() => Intl.message('Please choose a password.', + name: 'chooseAPasswordPrompt', locale: _localeName); String reenterPasswordPrompt() => - Intl.message('Please reenter your password.', name: 'reenterPasswordPromt', locale: _localeName); - String passwordsDontMatchError() => - Intl.message('Passwords don\'t match', name: 'passwordsDontMatchError', locale: _localeName); + Intl.message('Please reenter your password.', + name: 'reenterPasswordPromt', locale: _localeName); + String passwordsDontMatchError() => Intl.message('Passwords don\'t match', + name: 'passwordsDontMatchError', locale: _localeName); String usernameRegisterHint() => - Intl.message('The name to login and to be found by others', name: 'usernameRegisterHint', locale: _localeName); - String username() => Intl.message('Username', name: 'username', locale: _localeName); + Intl.message('The name to login and to be found by others', + name: 'usernameRegisterHint', locale: _localeName); + String username() => + Intl.message('Username', name: 'username', locale: _localeName); String emailRegisterHint() => - Intl.message('The email to login and to be found by others', name: 'emailRegisterHint', locale: _localeName); + Intl.message('The email to login and to be found by others', + name: 'emailRegisterHint', locale: _localeName); String passwordRegisterHint() => - Intl.message('The password to secure your account', name: 'passwordRegisterHint', locale: _localeName); + Intl.message('The password to secure your account', + name: 'passwordRegisterHint', locale: _localeName); String retypePasswordHint() => - Intl.message('Re-type your password for validation', name: 'retypePasswordHint', locale: _localeName); - String retypePasswordTitle() => Intl.message('Re-type Password', name: 'retypePasswordTitle', locale: _localeName); - String registerButton() => Intl.message('REGISTER', name: 'registerButton', locale: _localeName); - String discardNewProduct() => Intl.message('Discard new product?', name: 'discardNewProduct', locale: _localeName); - String cancelButton() => Intl.message('CANCEL', name: 'cancelButton', locale: _localeName); - String acceptButton() => Intl.message('ACCEPT', name: 'acceptButton', locale: _localeName); - String discardButton() => Intl.message('DISCARD', name: 'discardButton', locale: _localeName); - String fixErrorsBeforeSubmittingPrompt() => Intl.message('Please fix the errors in red before submitting.', - name: 'fixErrorsBeforeSubmittingPrompt', locale: _localeName); - String newProductTitle() => Intl.message('New Product', name: 'newProductTitle', locale: _localeName); - String saveButton() => Intl.message('SAVE', name: 'saveButton', locale: _localeName); - String newProductName() => Intl.message('Product Name *', name: 'newProductName', locale: _localeName); - String newProductNameHint() => - Intl.message('How is this product called?', name: 'newProductNameHint', locale: _localeName); - String newProductBrandName() => Intl.message('Brand Name *', name: 'newProductBrandName', locale: _localeName); + Intl.message('Re-type your password for validation', + name: 'retypePasswordHint', locale: _localeName); + String retypePasswordTitle() => Intl.message('Re-type Password', + name: 'retypePasswordTitle', locale: _localeName); + String registerButton() => + Intl.message('REGISTER', name: 'registerButton', locale: _localeName); + String discardNewProduct() => Intl.message('Discard new product?', + name: 'discardNewProduct', locale: _localeName); + String cancelButton() => + Intl.message('CANCEL', name: 'cancelButton', locale: _localeName); + String acceptButton() => + Intl.message('ACCEPT', name: 'acceptButton', locale: _localeName); + String discardButton() => + Intl.message('DISCARD', name: 'discardButton', locale: _localeName); + String fixErrorsBeforeSubmittingPrompt() => + Intl.message('Please fix the errors in red before submitting.', + name: 'fixErrorsBeforeSubmittingPrompt', locale: _localeName); + String newProductTitle() => + Intl.message('New Product', name: 'newProductTitle', locale: _localeName); + String saveButton() => + Intl.message('SAVE', name: 'saveButton', locale: _localeName); + String newProductName() => Intl.message('Product Name *', + name: 'newProductName', locale: _localeName); + String newProductNameHint() => Intl.message('How is this product called?', + name: 'newProductNameHint', locale: _localeName); + String newProductBrandName() => Intl.message('Brand Name *', + name: 'newProductBrandName', locale: _localeName); String newProductBrandNameHint() => - Intl.message('Which company sells this product?', name: 'newProductBrandNameHint', locale: _localeName); - String newProductWeight() => Intl.message('Weight', name: 'newProductWeight', locale: _localeName); + Intl.message('Which company sells this product?', + name: 'newProductBrandNameHint', locale: _localeName); + String newProductWeight() => + Intl.message('Weight', name: 'newProductWeight', locale: _localeName); String newProductWeightHint() => - Intl.message('What is the normal packaging size?', name: 'newProductWeightHint', locale: _localeName); - String newProductAddToList() => Intl.message('Add to current list', name: 'newProductAddToList', locale: _localeName); + Intl.message('What is the normal packaging size?', + name: 'newProductWeightHint', locale: _localeName); + String newProductAddToList() => Intl.message('Add to current list', + name: 'newProductAddToList', locale: _localeName); + String newProductAddedToList() => Intl.message(' added to list ', + name: 'newProductAddedToList', locale: _localeName); String newProductStarExplanation() => - Intl.message('* indicates required field', name: 'newProductStarExplanation', locale: _localeName); - String fieldRequiredError() => - Intl.message('This field is required!', name: 'fieldRequiredError', locale: _localeName); + Intl.message('* indicates required field', + name: 'newProductStarExplanation', locale: _localeName); + String fieldRequiredError() => Intl.message('This field is required!', + name: 'fieldRequiredError', locale: _localeName); String newProductNameToShort() => - Intl.message('This name seems to be to short', name: 'newProductNameToShort', locale: _localeName); - String addedProduct() => Intl.message(' added', name: 'addedProduct', locale: _localeName); - String productWasAlreadyInList() => Intl.message(' was already in list. The amount was increased by 1', - name: 'productWasAlreadyInList', locale: _localeName); - String searchProductHint() => Intl.message('Search Product', name: 'searchProductHint', locale: _localeName); - String noMoreProductsMessage() => - Intl.message('No more products found!', name: 'noMoreProductsMessage', locale: _localeName); - String codeText() => Intl.message('Code: ', name: 'codeText', locale: _localeName); - String removed() => Intl.message('removed', name: 'removed', locale: _localeName); - String changePrimaryColor() => Intl.message('Primary Color', name: 'changePrimaryColor', locale: _localeName); - String changeAccentColor() => Intl.message('Accent Color', name: 'changeAccentColor', locale: _localeName); - String changeDarkTheme() => Intl.message('Dark Theme', name: 'changeDarkTheme', locale: _localeName); - String changeAccentTextColor() => Intl.message('Dark Icons', name: 'changeAccentTextColor', locale: _localeName); - String autoSync() => Intl.message('Auto-Sync', name: 'autoSync', locale: _localeName); - String changePasswordButton() => Intl.message('CHANGE PASSWORD', name: 'changePasswordButton', locale: _localeName); - String oldPassword() => Intl.message('Current password', name: 'currentPassword', locale: _localeName); + Intl.message('This name seems to be to short', + name: 'newProductNameToShort', locale: _localeName); + String addedProduct() => + Intl.message(' added', name: 'addedProduct', locale: _localeName); + String productWasAlreadyInList() => + Intl.message(' was already in list. The amount was increased by 1', + name: 'productWasAlreadyInList', locale: _localeName); + String searchProductHint() => Intl.message('Search Product', + name: 'searchProductHint', locale: _localeName); + String noMoreProductsMessage() => Intl.message('No more products found!', + name: 'noMoreProductsMessage', locale: _localeName); + String codeText() => + Intl.message('Code: ', name: 'codeText', locale: _localeName); + String removed() => + Intl.message('removed', name: 'removed', locale: _localeName); + String changePrimaryColor() => Intl.message('Primary Color', + name: 'changePrimaryColor', locale: _localeName); + String changeAccentColor() => Intl.message('Accent Color', + name: 'changeAccentColor', locale: _localeName); + String changeDarkTheme() => + Intl.message('Dark Theme', name: 'changeDarkTheme', locale: _localeName); + String changeAccentTextColor() => Intl.message('Dark Icons', + name: 'changeAccentTextColor', locale: _localeName); + String autoSync() => + Intl.message('Auto-Sync', name: 'autoSync', locale: _localeName); + String changePasswordButton() => Intl.message('CHANGE PASSWORD', + name: 'changePasswordButton', locale: _localeName); + String oldPassword() => Intl.message('Current password', + name: 'currentPassword', locale: _localeName); String oldPasswordHint() => - Intl.message('Current password that should be changed', name: 'currentPasswordHint', locale: _localeName); - String newPassword() => Intl.message('New password', name: 'newPassword', locale: _localeName); - String newPasswordHint() => - Intl.message('The new password you have chosen', name: 'newPasswordHint', locale: _localeName); - String new2Password() => Intl.message('Repeat new password', name: 'repeatNewPassword', locale: _localeName); + Intl.message('Current password that should be changed', + name: 'currentPasswordHint', locale: _localeName); + String newPassword() => + Intl.message('New password', name: 'newPassword', locale: _localeName); + String newPasswordHint() => Intl.message('The new password you have chosen', + name: 'newPasswordHint', locale: _localeName); + String new2Password() => Intl.message('Repeat new password', + name: 'repeatNewPassword', locale: _localeName); String new2PasswordHint() => - Intl.message('repeat the new password you have chosen', name: 'repeatNewPasswordHint', locale: _localeName); - String changePasswordPD() => Intl.message('Change Password', name: 'changePasswordPD', locale: _localeName); - String successful() => Intl.message('Successful', name: 'successful', locale: _localeName); - String passwordSet() => Intl.message('Your password has been set', name: 'passwordSet', locale: _localeName); - String tokenExpired() => Intl.message('Token expired', name: 'tokenExpired', locale: _localeName); + Intl.message('repeat the new password you have chosen', + name: 'repeatNewPasswordHint', locale: _localeName); + String changePasswordPD() => Intl.message('Change Password', + name: 'changePasswordPD', locale: _localeName); + String successful() => + Intl.message('Successful', name: 'successful', locale: _localeName); + String passwordSet() => Intl.message('Your password has been set', + name: 'passwordSet', locale: _localeName); + String tokenExpired() => + Intl.message('Token expired', name: 'tokenExpired', locale: _localeName); String tokenExpiredExplanation() => Intl.message( 'Your token has expired. Login is required. If this happends multiple times per month, please contact us.', name: 'tokenExpiredExplanation', locale: _localeName); - String noListLoaded() => Intl.message('No List Loaded', name: 'noListLoaded', locale: _localeName); - String renameListItem() => Intl.message('Rename Product', name: 'renameListItem', locale: _localeName); - String renameListItemHint() => - Intl.message('The new name of the product', name: 'renameListItemHint', locale: _localeName); - String renameListItemLabel() => Intl.message('new product name', name: 'renameListItemLabel', locale: _localeName); - String discardNewTheme() => Intl.message('Discard new theme?', name: 'discardNewTheme', locale: _localeName); - String forgotPassword() => Intl.message('Forgot password?', name: 'forgotPassword', locale: _localeName); - String bePatient() => Intl.message('Please be patient, the server is processing your request already', - name: 'bePatient', locale: _localeName); - String logout() => Intl.message('Logout', name: 'logout', locale: _localeName); - String deleteListTitle() => Intl.message('Delete List', name: 'deleteListTitle', locale: _localeName); - String deleteListText() => Intl.message('Do you really want to delete the list? This CAN\'T be undone!', - name: 'deleteListText', locale: _localeName); - String exportAsPdf() => Intl.message('Export as PDF', name: 'exportAsPdf', locale: _localeName); - String boughtProducts() => Intl.message('Bought Products', name: 'boughtProducts', locale: _localeName); - String nothingBoughtYet() => Intl.message('Nothing bought yet', name: 'nothingBoughtYet', locale: _localeName); - String reorderItems() => Intl.message('Reorder', name: 'reorderItems', locale: _localeName); + String noListLoaded() => + Intl.message('No List Loaded', name: 'noListLoaded', locale: _localeName); + String renameListItem() => Intl.message('Rename Product', + name: 'renameListItem', locale: _localeName); + String renameListItemHint() => Intl.message('The new name of the product', + name: 'renameListItemHint', locale: _localeName); + String renameListItemLabel() => Intl.message('new product name', + name: 'renameListItemLabel', locale: _localeName); + String discardNewTheme() => Intl.message('Discard new theme?', + name: 'discardNewTheme', locale: _localeName); + String forgotPassword() => Intl.message('Forgot password?', + name: 'forgotPassword', locale: _localeName); + String bePatient() => Intl.message( + 'Please be patient, the server is processing your request already', + name: 'bePatient', + locale: _localeName); + String logout() => + Intl.message('Logout', name: 'logout', locale: _localeName); + String deleteListTitle() => + Intl.message('Delete List', name: 'deleteListTitle', locale: _localeName); + String deleteListText() => Intl.message( + 'Do you really want to delete the list? This CAN\'T be undone!', + name: 'deleteListText', + locale: _localeName); + String exportAsPdf() => + Intl.message('Export as PDF', name: 'exportAsPdf', locale: _localeName); + String boughtProducts() => Intl.message('Bought Products', + name: 'boughtProducts', locale: _localeName); + String nothingBoughtYet() => Intl.message('Nothing bought yet', + name: 'nothingBoughtYet', locale: _localeName); + String reorderItems() => + Intl.message('Reorder', name: 'reorderItems', locale: _localeName); - String refresh() => Intl.message('Refresh', name: "refresh", locale: _localeName); + String refresh() => + Intl.message('Refresh', name: "refresh", locale: _localeName); //String openAppDrawerTooltip() => Intl.message('Open navigation menu', name: 'openNavigationMenu', locale: _localeName); } diff --git a/lib/main.dart b/lib/main.dart index 2ea0d4f..a4130b9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,7 +4,6 @@ import 'dart:ui'; import 'package:adaptive_theme/adaptive_theme.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:nssl/options/themes.dart'; import 'package:nssl/pages/pages.dart'; import 'package:nssl/manager/manager_export.dart'; @@ -30,7 +29,8 @@ Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { // make sure you call `initializeApp` before using other Firebase services. await Startup.initializeMinFunction(); //Startup.remoteMessages.add(message); - var dir = await Startup.fs.systemTempDirectory.childDirectory("message").create(); + var dir = + await Startup.fs.systemTempDirectory.childDirectory("message").create(); var file = dir.childFile(DateTime.now().microsecondsSinceEpoch.toString()); await file.writeAsString(jsonEncode(message.data)); } @@ -45,8 +45,9 @@ Future main() async { else return Container(color: Colors.green); }, - future: Startup.initialize() - .then((value) => FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler)), + future: Startup.initialize().then((value) => + FirebaseMessaging.onBackgroundMessage( + _firebaseMessagingBackgroundHandler)), )); } @@ -95,7 +96,8 @@ class _NSSLState extends State { // FirebaseMessaging.onBackgroundMessage((message) => CloudMessaging.onMessage(message, setState)); // firebaseMessaging.configure( // onMessage: (x) => CloudMessaging.onMessage(x, setState), onLaunch: (x) => Startup.initialize()); - for (var list in User.shoppingLists) if (list.messagingEnabled) list.subscribeForFirebaseMessaging(); + for (var list in User.shoppingLists) + if (list.messagingEnabled) list.subscribeForFirebaseMessaging(); } Future subscribeFirebase(BuildContext context) async { @@ -145,7 +147,9 @@ class _NSSLState extends State { } Scaffold mainAppHome() => Scaffold( - key: _mainScaffoldKey, resizeToAvoidBottomInset: false, body: MainPage() //CustomThemePage()//LoginPage(), + key: _mainScaffoldKey, + resizeToAvoidBottomInset: false, + body: MainPage() //CustomThemePage()//LoginPage(), ); Scaffold mainAppLoginRegister() => Scaffold( diff --git a/lib/models/shopping_item.dart b/lib/models/shopping_item.dart index fc01939..f77c8da 100644 --- a/lib/models/shopping_item.dart +++ b/lib/models/shopping_item.dart @@ -1,4 +1,4 @@ -class TestClass{ +class TestClass { int test; String? o; @@ -9,13 +9,13 @@ class TestClass{ class ShoppingItem { //extends JsonDecoder{ - int? amount; + int amount = 1; String? name; int? id; - DateTime? created; - DateTime? changed; + DateTime? created; + DateTime? changed; bool crossedOut = false; - int? sortOrder; + int? sortOrder; ShoppingItem(this.name); @@ -24,14 +24,21 @@ class ShoppingItem { return name! + "\u{1F}" + amount.toString() + "\u{1F}" + id.toString(); } - ShoppingItem clone(){ + /// Creates a copy, with only including [amount] and [name] + ShoppingItem copy() { + return ShoppingItem(name)..amount = amount; + } + + /// Creates an identical clone, where all fields are the same as + /// the parent item + ShoppingItem clone() { return ShoppingItem(name) - ..amount=amount - ..id=id - ..created=created - ..changed=changed - ..crossedOut=crossedOut - ..sortOrder=sortOrder; + ..amount = amount + ..id = id + ..created = created + ..changed = changed + ..crossedOut = crossedOut + ..sortOrder = sortOrder; } ShoppingItem.fromJson(String s) : name = s; diff --git a/lib/models/shopping_list.dart b/lib/models/shopping_list.dart index 5ee096f..e85d644 100644 --- a/lib/models/shopping_list.dart +++ b/lib/models/shopping_list.dart @@ -22,14 +22,24 @@ class ShoppingList { Future save() async { await DatabaseManager.database.transaction((z) async { - await z.execute('INSERT OR REPLACE INTO ShoppingLists(id, name, messaging, user_id) VALUES(?, ?, ?, ?)', + await z.execute( + 'INSERT OR REPLACE INTO ShoppingLists(id, name, messaging, user_id) VALUES(?, ?, ?, ?)', [id, name, messagingEnabled ? 1 : 0, User.ownId]); - await z.rawDelete("DELETE FROM ShoppingItems WHERE res_list_id = ? and id not in (?)", [id, shoppingItems!.map((e) => e!.id).join(",")]); + await z.rawDelete( + "DELETE FROM ShoppingItems WHERE res_list_id = ? and id not in (?)", + [id, shoppingItems!.map((e) => e!.id).join(",")]); for (var item in shoppingItems!) { await z.execute( "INSERT OR REPLACE INTO ShoppingItems(id, name, amount, crossed, res_list_id, sortorder) VALUES (?, ?, ?, ?, ?, ?)", - [item!.id, item.name, item.amount, item.crossedOut ? 1 : 0, id, item.sortOrder]); + [ + item!.id, + item.name, + item.amount, + item.crossedOut ? 1 : 0, + id, + item.sortOrder + ]); } }); } @@ -39,18 +49,28 @@ class ShoppingList { shoppingItems!.insert(index, item); await DatabaseManager.database.execute( "INSERT OR REPLACE INTO ShoppingItems(id, name, amount, crossed, res_list_id, sortorder) VALUES (?, ?, ?, ?, ?, ?)", - [item.id, item.name, item.amount, item.crossedOut ? 1 : 0, id, item.sortOrder]); + [ + item.id, + item.name, + item.amount, + item.crossedOut ? 1 : 0, + id, + item.sortOrder + ]); } Future deleteSingleItem(ShoppingItem item) async { shoppingItems!.remove(item); - await DatabaseManager.database.rawDelete("DELETE FROM ShoppingItems WHERE id = ?", [item.id]); + await DatabaseManager.database + .rawDelete("DELETE FROM ShoppingItems WHERE id = ?", [item.id]); } static Future> load() async { - var lists = await DatabaseManager.database.rawQuery("SELECT * FROM ShoppingLists WHERE user_id = ?", [User.ownId]); + var lists = await DatabaseManager.database.rawQuery( + "SELECT * FROM ShoppingLists WHERE user_id = ?", [User.ownId]); - var items = await DatabaseManager.database.rawQuery("SELECT * FROM ShoppingItems ORDER BY res_list_id, sortorder"); + var items = await DatabaseManager.database.rawQuery( + "SELECT * FROM ShoppingItems ORDER BY res_list_id, sortorder"); // TODO: if db ordering enough for us, or do we want to order by ourself in code? // return lists @@ -75,7 +95,7 @@ class ShoppingList { ..shoppingItems = items .where((y) => y["res_list_id"] == x["id"]) .map((y) => ShoppingItem(y["name"] as String?) - ..amount = y["amount"] as int? + ..amount = y["amount"] as int ..crossedOut = y["crossed"] == 0 ? false : true ..id = y["id"] as int? ..sortOrder = y["sortorder"] as int?) @@ -95,8 +115,9 @@ class ShoppingList { // } var newList = GetListResult.fromJson(res.body); List> items; - items = (await DatabaseManager.database - .rawQuery("SELECT id, crossed, sortorder FROM ShoppingItems WHERE res_list_id = ?", [id])); + items = (await DatabaseManager.database.rawQuery( + "SELECT id, crossed, sortorder FROM ShoppingItems WHERE res_list_id = ?", + [id])); shoppingItems!.clear(); for (var item in newList.products!) @@ -105,8 +126,11 @@ class ShoppingList { ..amount = item.amount ..changed = item.changed ..created = item.created - ..crossedOut = - (items.firstWhere((x) => x["id"] == item.id, orElse: () => {"crossed": 0})["crossed"] == 0 ? false : true) + ..crossedOut = (items.firstWhere((x) => x["id"] == item.id, + orElse: () => {"crossed": 0})["crossed"] == + 0 + ? false + : true) ..sortOrder = item.sortOrder); shoppingItems!.sort((a, b) => a!.sortOrder!.compareTo(b!.sortOrder!)); @@ -114,14 +138,17 @@ class ShoppingList { } static Future reloadAllLists([BuildContext? cont]) async { - var result = GetListsResult.fromJson((await ShoppingListSync.getLists(cont)).body); + var result = + GetListsResult.fromJson((await ShoppingListSync.getLists(cont)).body); User.shoppingLists.clear(); - await DatabaseManager.database.delete("ShoppingLists", where: "user_id = ?", whereArgs: [User.ownId]); + await DatabaseManager.database + .delete("ShoppingLists", where: "user_id = ?", whereArgs: [User.ownId]); //await DatabaseManager.database.rawDelete("DELETE FROM ShoppingLists where user_id = ?", [User.ownId]); List> items; - items = (await DatabaseManager.database.rawQuery("SELECT id, crossed, sortorder FROM ShoppingItems")); + items = (await DatabaseManager.database + .rawQuery("SELECT id, crossed, sortorder FROM ShoppingItems")); for (var res in result.shoppingLists) { var list = ShoppingList(res.id, res.name); @@ -130,14 +157,20 @@ class ShoppingList { list.shoppingItems!.add(ShoppingItem(item.name) ..id = item.id ..amount = item.amount - ..crossedOut = - (items.firstWhere((x) => x["id"] == item.id, orElse: () => {"crossed": 0})["crossed"] == 0 ? false : true) - ..sortOrder = (items.firstWhere((x) => x["id"] == item.id, orElse: () => {"sortorder": 0})["sortorder"])); + ..crossedOut = (items.firstWhere((x) => x["id"] == item.id, + orElse: () => {"crossed": 0})["crossed"] == + 0 + ? false + : true) + ..sortOrder = (items.firstWhere((x) => x["id"] == item.id, + orElse: () => {"sortorder": 0})["sortorder"])); if (list.shoppingItems!.any((element) => element!.sortOrder == null)) - for (int i = 0; i < list.shoppingItems!.length; i++) list.shoppingItems![i]!.sortOrder = i; + for (int i = 0; i < list.shoppingItems!.length; i++) + list.shoppingItems![i]!.sortOrder = i; - list.shoppingItems!.sort((a, b) => a!.sortOrder!.compareTo(b!.sortOrder!)); + list.shoppingItems! + .sort((a, b) => a!.sortOrder!.compareTo(b!.sortOrder!)); User.shoppingLists.add(list); list.subscribeForFirebaseMessaging(); @@ -150,6 +183,7 @@ class ShoppingList { } void unsubscribeFromFirebaseMessaging() { - firebaseMessaging?.unsubscribeFromTopic(id.toString() + "shoppingListTopic"); + firebaseMessaging + ?.unsubscribeFromTopic(id.toString() + "shoppingListTopic"); } } diff --git a/lib/models_json.dart b/lib/models_json.dart index 5c13a8d..4e9f8f3 100644 --- a/lib/models_json.dart +++ b/lib/models_json.dart @@ -13,7 +13,7 @@ class User { class Product { String? gtin; String? name; - int? quantity; + int? quantity; String? unit; toJson() => {"gtin": gtin, "name": name, "quantity": quantity, "unit": unit}; @@ -26,27 +26,40 @@ class Product { } class ShoppingItem { - ShoppingItem(this.id, this.amount, this.name, this.changed, this.created, this.sortOrder) : super(); + ShoppingItem(this.id, this.amount, this.name, this.changed, this.created, + this.sortOrder) + : super(); int? id; - int? amount; + int amount; String? name; - DateTime? changed; - DateTime? created; + DateTime? changed; + DateTime? created; int? sortOrder; - toJson() => {"id": id, "amount": amount, "name": name, "changed" :changed, "created" :created, "sortOrder" : sortOrder}; + toJson() => { + "id": id, + "amount": amount, + "name": name, + "changed": changed, + "created": created, + "sortOrder": sortOrder + }; - static ShoppingItem fromJson(Map data) => - ShoppingItem(data["id"], data["amount"], data["name"], data["changed"], data["created"], data["sortOrder"]); + static ShoppingItem fromJson(Map data) => ShoppingItem( + data["id"], + data["amount"], + data["name"], + data["changed"], + data["created"], + data["sortOrder"]); } class ShoppingList { List? products; - int? id; + int? id; String? name; toJson() => { - "products": products?.map((p) => p.toJson()).toList(growable: false), "id": id, "name": name diff --git a/lib/pages/barcode_scanner_page.dart b/lib/pages/barcode_scanner_page.dart index 14f6393..d43d909 100644 --- a/lib/pages/barcode_scanner_page.dart +++ b/lib/pages/barcode_scanner_page.dart @@ -4,7 +4,6 @@ * Copyright (C) 2020- Scandit AG. All rights reserved. */ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -93,13 +92,16 @@ class _BarcodeScannerScreenState extends State // Add a barcode capture overlay to the data capture view to render the location of captured barcodes on top of // the video preview. This is optional, but recommended for better visual feedback. - var overlay = BarcodeCaptureOverlay.withBarcodeCaptureForView(_barcodeCapture, _captureView) + var overlay = BarcodeCaptureOverlay.withBarcodeCaptureForView( + _barcodeCapture, _captureView) ..viewfinder = RectangularViewfinder.withStyleAndLineStyle( - RectangularViewfinderStyle.square, RectangularViewfinderLineStyle.light); + RectangularViewfinderStyle.square, + RectangularViewfinderLineStyle.light); // Adjust the overlay's barcode highlighting to match the new viewfinder styles and improve the visibility of feedback. // With 6.10 we will introduce this visual treatment as a new style for the overlay. - overlay.brush = Brush(Color.fromARGB(0, 0, 0, 0), Color.fromARGB(255, 255, 255, 255), 3); + overlay.brush = Brush( + Color.fromARGB(0, 0, 0, 0), Color.fromARGB(255, 255, 255, 255), 3); _captureView.addOverlay(overlay); @@ -117,7 +119,8 @@ class _BarcodeScannerScreenState extends State Widget child; if (_isPermissionMessageVisible) { child = PlatformText('No permission to access the camera!', - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black)); + style: TextStyle( + fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black)); } else { child = _captureView; } @@ -134,15 +137,19 @@ class _BarcodeScannerScreenState extends State } @override - void didScan(BarcodeCapture barcodeCapture, BarcodeCaptureSession session) async { + void didScan( + BarcodeCapture barcodeCapture, BarcodeCaptureSession session) async { _barcodeCapture.isEnabled = false; var code = session.newlyRecognizedBarcodes.first; - var data = (code.data == null || code.data?.isEmpty == true) ? code.rawData : code.data; + var data = (code.data == null || code.data?.isEmpty == true) + ? code.rawData + : code.data; Navigator.pop(context, data); } @override - void didUpdateSession(BarcodeCapture barcodeCapture, BarcodeCaptureSession session) {} + void didUpdateSession( + BarcodeCapture barcodeCapture, BarcodeCaptureSession session) {} @override void dispose() { diff --git a/lib/pages/bought_items.dart b/lib/pages/bought_items.dart index dd73b19..ecdde90 100644 --- a/lib/pages/bought_items.dart +++ b/lib/pages/bought_items.dart @@ -1,26 +1,29 @@ import 'package:flutter/material.dart'; import 'package:nssl/localization/nssl_strings.dart'; import 'package:nssl/models/model_export.dart'; -import 'package:flutter/widgets.dart'; import 'package:nssl/server_communication//s_c.dart'; import 'package:nssl/server_communication/return_classes.dart'; +import 'package:nssl/helper/iterable_extensions.dart'; class BoughtItemsPage extends StatefulWidget { BoughtItemsPage(this.listId, {Key? key, this.title}) : super(key: key); final String? title; final int listId; @override - _BoughtItemsPagePageState createState() => new _BoughtItemsPagePageState(listId); + _BoughtItemsPagePageState createState() => + new _BoughtItemsPagePageState(listId); } -class _BoughtItemsPagePageState extends State with SingleTickerProviderStateMixin { +class _BoughtItemsPagePageState extends State + with SingleTickerProviderStateMixin { final GlobalKey _mainScaffoldKey = GlobalKey(); var tec = TextEditingController(); var shoppingItems = []; + var currentList = ShoppingList(0, "empty"); var shoppingItemsGrouped = new Map>(); int k = 1; - int? listId; + int listId; TabController? _controller; @override @@ -34,8 +37,10 @@ class _BoughtItemsPagePageState extends State with SingleTicker super.dispose(); } - _BoughtItemsPagePageState(int listId) { - this.listId = listId; + _BoughtItemsPagePageState(this.listId) { + currentList = + User.shoppingLists.firstWhere((element) => element.id == listId); + ShoppingListSync.getList(listId, null, bought: true).then((o) { if (o.statusCode == 500) { showInSnackBar("Internal Server Error"); @@ -43,7 +48,8 @@ class _BoughtItemsPagePageState extends State with SingleTicker } var z = GetBoughtListResult.fromJson(o.body); if (z.products.length <= 0) - showInSnackBar(NSSLStrings.of(context)!.nothingBoughtYet(), duration: Duration(seconds: 10)); + showInSnackBar(NSSLStrings.of(context)!.nothingBoughtYet(), + duration: Duration(seconds: 10)); else { shoppingItems.addAll(z.products.map((f) => ShoppingItem(f.name) ..id = f.id @@ -62,7 +68,8 @@ class _BoughtItemsPagePageState extends State with SingleTicker } setState(() { - _controller = TabController(vsync: this, length: shoppingItemsGrouped.keys.length); + _controller = TabController( + vsync: this, length: shoppingItemsGrouped.keys.length); }); }); } @@ -75,28 +82,27 @@ class _BoughtItemsPagePageState extends State with SingleTicker Widget build(BuildContext context) { if (_controller == null) return Scaffold( - appBar: AppBar( - title: Text(NSSLStrings.of(context)!.boughtProducts()), - actions: [], - ), - body: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - child: SizedBox( - width: 40.0, - height: 40.0, - child: CircularProgressIndicator()), - padding: const EdgeInsets.only(top: 16.0), - ) - ], - )); + appBar: AppBar( + title: Text(NSSLStrings.of(context)!.boughtProducts()), + actions: [], + ), + body: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + child: SizedBox( + width: 40.0, + height: 40.0, + child: CircularProgressIndicator()), + padding: const EdgeInsets.only(top: 16.0), + ) + ], + )); return Scaffold( key: _mainScaffoldKey, appBar: AppBar( title: Text(NSSLStrings.of(context)!.boughtProducts()), - actions: [], bottom: TabBar( controller: _controller, isScrollable: true, @@ -128,10 +134,13 @@ class _BoughtItemsPagePageState extends State with SingleTicker ); } - void showInSnackBar(String value, {Duration? duration, SnackBarAction? action}) { + void showInSnackBar(String value, + {Duration? duration, SnackBarAction? action}) { ScaffoldMessenger.of(context).removeCurrentSnackBar(); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(value), duration: duration ?? Duration(seconds: 3), action: action)); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(value), + duration: duration ?? Duration(seconds: 3), + action: action)); } List createTabs() { @@ -155,10 +164,40 @@ class _BoughtItemsPagePageState extends State with SingleTicker child: Center( child: ListView( children: shoppingItemsGrouped[item]! - .map((i) => ListTile( - title: Text(i.name!), - leading: Text(i.amount.toString() + "x"), - )) + .map( + (i) => ListTile( + title: Text(i.name!), + leading: Text(i.amount.toString() + "x"), + onTap: () async { + var existingItem = currentList.shoppingItems + ?.firstOrNull((item) => item?.name == i.name); + if (existingItem != null) { + var answer = + await ShoppingListSync.changeProductAmount( + currentList.id!, + existingItem.id, + i.amount, + context); + var p = + ChangeListItemResult.fromJson((answer).body); + existingItem.amount = p.amount; + existingItem.changed = p.changed; + } else { + var p = AddListItemResult.fromJson( + (await ShoppingListSync.addProduct(listId, + i.name, null, i.amount, context)) + .body); + var newItem = ShoppingItem(p.name) + ..amount = i.amount + ..id = p.productId; + + currentList.addSingleItem(newItem); + } + showInSnackBar( + "${i.amount}x ${i.name}${NSSLStrings.of(context)?.newProductAddedToList()}${currentList.name}"); + }, + ), + ) .toList(growable: false), ), ), diff --git a/lib/pages/change_password.dart b/lib/pages/change_password.dart index e9f09a0..4226a0e 100644 --- a/lib/pages/change_password.dart +++ b/lib/pages/change_password.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:nssl/localization/nssl_strings.dart'; import 'package:nssl/models/user.dart'; import 'package:nssl/pages/login.dart'; @@ -22,8 +21,8 @@ class ChangePasswordPageState extends State { var newPw2Input = ForInput(); void showInSnackBar(String value) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(value), duration: Duration(seconds: 3))); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(value), duration: Duration(seconds: 3))); } void _handleSubmitted() { @@ -65,8 +64,7 @@ class ChangePasswordPageState extends State { _changePassword(); } - bool _validateEmpty(TextEditingController value) => - (value.text.isEmpty); + bool _validateEmpty(TextEditingController value) => (value.text.isEmpty); _changePassword() async { var res = await UserSync.changePassword( @@ -76,14 +74,13 @@ class ChangePasswordPageState extends State { context); if (res.statusCode != 200) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(res.reasonPhrase!), - duration: Duration(seconds: 3))); + content: Text(res.reasonPhrase!), duration: Duration(seconds: 3))); return; } var obj = Result.fromJson(res.body); if (!obj.success!) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(obj.error!), duration: Duration(seconds: 3))); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(obj.error!), duration: Duration(seconds: 3))); return; } var dialog = AlertDialog( @@ -124,13 +121,12 @@ class ChangePasswordPageState extends State { return Scaffold( key: _scaffoldKey, resizeToAvoidBottomInset: false, - appBar: AppBar( - title: Text(NSSLStrings.of(context)!.changePasswordPD())), + appBar: AppBar(title: Text(NSSLStrings.of(context)!.changePasswordPD())), body: Container( padding: const EdgeInsets.symmetric(horizontal: 32.0), - child: - Column(mainAxisAlignment: MainAxisAlignment.start, children: [ - Flexible(child: TextField( + child: Column(mainAxisAlignment: MainAxisAlignment.start, children: [ + Flexible( + child: TextField( key: oldPwInput.key, decoration: oldPwInput.decoration, focusNode: oldPwInput.focusNode, @@ -141,7 +137,8 @@ class ChangePasswordPageState extends State { }, ), ), - Flexible(child: TextField( + Flexible( + child: TextField( key: newPwInput.key, decoration: newPwInput.decoration, focusNode: newPwInput.focusNode, @@ -153,7 +150,7 @@ class ChangePasswordPageState extends State { ), ), Flexible( - child: TextField( + child: TextField( key: newPw2Input.key, decoration: newPw2Input.decoration, focusNode: newPw2Input.focusNode, @@ -165,16 +162,16 @@ class ChangePasswordPageState extends State { ), ), Flexible( - child: Container( + child: Container( padding: const EdgeInsets.only(top: 32.0), child: ElevatedButton( // child: Center( - child: Text( - NSSLStrings.of(context)!.changePasswordButton(), - ), + child: Text( + NSSLStrings.of(context)!.changePasswordButton(), + ), // ), onPressed: _handleSubmitted, - ), + ), /* TextButton( onPressed: () { diff --git a/lib/pages/contributors.dart b/lib/pages/contributors.dart index d7b5797..92dea2c 100644 --- a/lib/pages/contributors.dart +++ b/lib/pages/contributors.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:nssl/localization/nssl_strings.dart'; import 'package:nssl/models/model_export.dart'; -import 'package:flutter/widgets.dart'; import 'package:nssl/server_communication//s_c.dart'; import 'dart:async'; import 'package:nssl/server_communication/return_classes.dart'; @@ -11,7 +10,8 @@ class ContributorsPage extends StatefulWidget { final String? title; final int listId; @override - _ContributorsPagePageState createState() => new _ContributorsPagePageState(listId); + _ContributorsPagePageState createState() => + new _ContributorsPagePageState(listId); } class _ContributorsPagePageState extends State { @@ -37,7 +37,9 @@ class _ContributorsPagePageState extends State { } GetContributorsResult z = GetContributorsResult.fromJson(o.body); if (!z.success! || z.contributors.length <= 0) - showInSnackBar(NSSLStrings.of(context)!.genericErrorMessageSnackbar() + o.reasonPhrase!, + showInSnackBar( + NSSLStrings.of(context)!.genericErrorMessageSnackbar() + + o.reasonPhrase!, duration: Duration(seconds: 10)); else setState(() => conList.addAll(z.contributors)); @@ -52,7 +54,9 @@ class _ContributorsPagePageState extends State { title: Form( child: TextField( key: _iff, - decoration: InputDecoration(hintText: NSSLStrings.of(context)!.nameOfNewContributorHint()), + decoration: InputDecoration( + hintText: NSSLStrings.of(context)! + .nameOfNewContributorHint()), onSubmitted: (x) => _addContributor(x), autofocus: true, controller: tec))), @@ -71,7 +75,9 @@ class _ContributorsPagePageState extends State { var o = await ShoppingListSync.addContributor(listId, value, context); AddContributorResult z = AddContributorResult.fromJson(o.body); if (!z.success!) - showInSnackBar(NSSLStrings.of(context)!.genericErrorMessageSnackbar() + z.error!, duration: Duration(seconds: 10)); + showInSnackBar( + NSSLStrings.of(context)!.genericErrorMessageSnackbar() + z.error!, + duration: Duration(seconds: 10)); else setState(() => conList.add(ContributorResult() ..name = z.name @@ -82,7 +88,10 @@ class _ContributorsPagePageState extends State { Widget buildBody() { bool? isAdmin = false; if (conList.length > 0) { - isAdmin = conList.firstWhere((x) => x.name!.toLowerCase() == User.username!.toLowerCase()).isAdmin; + isAdmin = conList + .firstWhere( + (x) => x.name!.toLowerCase() == User.username!.toLowerCase()) + .isAdmin; var listView = ListView.builder( itemBuilder: (c, i) { return ListTile( @@ -90,11 +99,14 @@ class _ContributorsPagePageState extends State { (conList[i].isAdmin! ? NSSLStrings.of(context)!.contributorAdmin() : NSSLStrings.of(context)!.contributorUser())), - trailing: isAdmin! && conList[i].name!.toLowerCase() != User.username!.toLowerCase() + trailing: isAdmin! && + conList[i].name!.toLowerCase() != + User.username!.toLowerCase() ? PopupMenuButton( padding: EdgeInsets.zero, onSelected: popupMenuClicked, - itemBuilder: (BuildContext context) => >[ + itemBuilder: (BuildContext context) => + >[ PopupMenuItem( value: conList[i].userId.toString() + "\u{1E}ChangeRight", //x.id.toString() + "\u{1E}" + 'Rename', @@ -103,14 +115,18 @@ class _ContributorsPagePageState extends State { ? const Icon(Icons.arrow_downward) : const Icon(Icons.arrow_upward)), title: (conList[i].isAdmin! - ? Text(NSSLStrings.of(context)!.demoteMenu()) - : Text(NSSLStrings.of(context)!.promoteMenu())))), + ? Text(NSSLStrings.of(context)! + .demoteMenu()) + : Text(NSSLStrings.of(context)! + .promoteMenu())))), const PopupMenuDivider(), // ignore: list_element_type_not_assignable PopupMenuItem( value: conList[i].userId.toString() + "\u{1E}Remove", //x.id.toString() + "\u{1E}" + 'Remove', child: ListTile( - leading: const Icon(Icons.delete), title: Text(NSSLStrings.of(context)!.remove()))) + leading: const Icon(Icons.delete), + title: Text( + NSSLStrings.of(context)!.remove()))) ]) : const Text(""), onTap: () => {}); @@ -122,17 +138,21 @@ class _ContributorsPagePageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Container( - child: SizedBox(width: 40.0, height: 40.0, child: CircularProgressIndicator()), + child: SizedBox( + width: 40.0, height: 40.0, child: CircularProgressIndicator()), padding: const EdgeInsets.only(top: 16.0), ) ], ); } - void showInSnackBar(String value, {Duration? duration, SnackBarAction? action}) { + void showInSnackBar(String value, + {Duration? duration, SnackBarAction? action}) { ScaffoldMessenger.of(context).removeCurrentSnackBar(); - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(value), duration: duration ?? Duration(seconds: 3), action: action)); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(value), + duration: duration ?? Duration(seconds: 3), + action: action)); } Future popupMenuClicked(String value) async { @@ -141,12 +161,14 @@ class _ContributorsPagePageState extends State { switch (command) { case "Remove": var userId = int.parse(splitted[0]); - var res = await ShoppingListSync.deleteContributor(listId, userId, context); + var res = + await ShoppingListSync.deleteContributor(listId, userId, context); var enres = Result.fromJson(res.body); if (!enres.success!) showInSnackBar(enres.error!); else { - showInSnackBar(conList.firstWhere((x) => x.userId == userId).name! + " was removed successfully"); + showInSnackBar(conList.firstWhere((x) => x.userId == userId).name! + + " was removed successfully"); setState(() => conList.removeWhere((x) => x.userId == userId)); } break; @@ -164,7 +186,9 @@ class _ContributorsPagePageState extends State { } GetContributorsResult z = GetContributorsResult.fromJson(o.body); if (!z.success! || z.contributors.length <= 0) - showInSnackBar(NSSLStrings.of(context)!.genericErrorMessageSnackbar() + z.error!, + showInSnackBar( + NSSLStrings.of(context)!.genericErrorMessageSnackbar() + + z.error!, duration: Duration(seconds: 10)); else conList.clear(); diff --git a/lib/pages/login.dart b/lib/pages/login.dart index b5a722a..2c0331b 100644 --- a/lib/pages/login.dart +++ b/lib/pages/login.dart @@ -1,15 +1,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:nssl/firebase/cloud_messsaging.dart'; import 'package:nssl/localization/nssl_strings.dart'; import 'package:nssl/main.dart'; import 'package:nssl/models/model_export.dart'; -import 'package:nssl/models/user.dart'; -import 'package:nssl/server_communication/helper_methods.dart'; import 'package:nssl/server_communication/return_classes.dart'; import 'package:nssl/server_communication/s_c.dart'; -import 'package:nssl/server_communication/user_sync.dart'; class LoginPage extends StatefulWidget { LoginPage({Key? key, this.scaffoldKey}) : super(key: key); @@ -44,7 +40,8 @@ class LoginPageState extends State { var validateMode = AutovalidateMode.disabled; void showInSnackBar(String value) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(value), duration: Duration(seconds: 3))); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(value), duration: Duration(seconds: 3))); } Future _handleSubmitted() async { @@ -79,13 +76,15 @@ class LoginPageState extends State { if (_validateEmail(nameInput.textEditingController.text) != null) { var res = await UserSync.login(name, password, context); - if (!HelperMethods.reactToRespone(res, context, scaffoldState: _scaffoldKey.currentState)) + if (!HelperMethods.reactToRespone(res, context, + scaffoldState: _scaffoldKey.currentState)) return; else _handleLoggedIn(LoginResult.fromJson(res.body)); } else { var res = await UserSync.loginEmail(name, password, context); - if (!HelperMethods.reactToRespone(res, context, scaffoldState: _scaffoldKey.currentState)) + if (!HelperMethods.reactToRespone(res, context, + scaffoldState: _scaffoldKey.currentState)) return; else _handleLoggedIn(LoginResult.fromJson(res.body)); @@ -123,8 +122,10 @@ class LoginPageState extends State { } String? _validateName(String? value) { - if (value!.isEmpty) return NSSLStrings.of(context)!.nameEmailRequiredError(); - if (value.length < 4) return NSSLStrings.of(context)!.usernameToShortError(); + if (value!.isEmpty) + return NSSLStrings.of(context)!.nameEmailRequiredError(); + if (value.length < 4) + return NSSLStrings.of(context)!.usernameToShortError(); return null; } @@ -133,20 +134,25 @@ class LoginPageState extends State { if (value.isEmpty) return NSSLStrings.of(context)!.emailRequiredError(); RegExp email = RegExp( r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'); - if (!email.hasMatch(value)) return NSSLStrings.of(context)!.emailIncorrectFormatError(); + if (!email.hasMatch(value)) + return NSSLStrings.of(context)!.emailIncorrectFormatError(); return null; } String? _validatePassword(String? value) { - if (pwInput.textEditingController.text.isEmpty) return NSSLStrings.of(context)!.passwordEmptyError(); + if (pwInput.textEditingController.text.isEmpty) + return NSSLStrings.of(context)!.passwordEmptyError(); return null; } _resetInput() { - nameInput.decoration = - InputDecoration(helperText: NSSLStrings.of(context)!.usernameOrEmailForLoginHint(), labelText: NSSLStrings.of(context)!.usernameOrEmailTitle()); + nameInput.decoration = InputDecoration( + helperText: NSSLStrings.of(context)!.usernameOrEmailForLoginHint(), + labelText: NSSLStrings.of(context)!.usernameOrEmailTitle()); - pwInput.decoration = InputDecoration(helperText: NSSLStrings.of(context)!.choosenPasswordHint(), labelText: NSSLStrings.of(context)!.password()); + pwInput.decoration = InputDecoration( + helperText: NSSLStrings.of(context)!.choosenPasswordHint(), + labelText: NSSLStrings.of(context)!.password()); } @override @@ -177,7 +183,10 @@ class LoginPageState extends State { //onChanged: (input) => nameInput.errorText = _validateName(input), controller: nameInput.textEditingController, keyboardType: TextInputType.emailAddress, - autofillHints: [AutofillHints.username, AutofillHints.email], + autofillHints: [ + AutofillHints.username, + AutofillHints.email + ], autocorrect: false, autofocus: true, validator: _validateName, @@ -201,7 +210,8 @@ class LoginPageState extends State { title: Container( child: ElevatedButton( key: submit.key, - child: Center(child: Text(NSSLStrings.of(context)!.loginButton())), + child: Center( + child: Text(NSSLStrings.of(context)!.loginButton())), onPressed: _handleSubmitted, ), padding: const EdgeInsets.only(top: 16.0)), @@ -211,7 +221,9 @@ class LoginPageState extends State { padding: const EdgeInsets.only(top: 40.0), child: TextButton( onPressed: () { - User.username == null ? Navigator.pushNamed(context, "/registration") : Navigator.popAndPushNamed(context, "/registration"); + User.username == null + ? Navigator.pushNamed(context, "/registration") + : Navigator.popAndPushNamed(context, "/registration"); }, child: Text(NSSLStrings.of(context)!.registerTextOnLogin()), ), diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart index 8eccab2..d607ac3 100644 --- a/lib/pages/main_page.dart +++ b/lib/pages/main_page.dart @@ -23,7 +23,8 @@ class MainPage extends StatefulWidget { MainPageState createState() => MainPageState(); } -class MainPageState extends State with TickerProviderStateMixin, WidgetsBindingObserver { +class MainPageState extends State + with TickerProviderStateMixin, WidgetsBindingObserver { BuildContext? cont; final ScrollController _mainController = ScrollController(); @@ -54,7 +55,7 @@ class MainPageState extends State with TickerProviderStateMixin, Widge WidgetsBinding.instance!.addObserver(this); Startup.deleteMessagesFromFolder(); Startup.initializeNewListsFromServer(setState); - + _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 200), @@ -84,14 +85,20 @@ class MainPageState extends State with TickerProviderStateMixin, Widge : [ PopupMenuButton( onSelected: selectedOption, - itemBuilder: (BuildContext context) => >[ + itemBuilder: (BuildContext context) => + >[ PopupMenuItem( - value: 'Options', child: Text(NSSLStrings.of(context)!.changeTheme())), + value: 'Options', + child: Text( + NSSLStrings.of(context)!.changeTheme())), PopupMenuItem( value: 'deleteCrossedOut', - child: Text(NSSLStrings.of(context)!.deleteCrossedOutPB())), + child: Text(NSSLStrings.of(context)! + .deleteCrossedOutPB())), PopupMenuItem( - value: 'reorderItems', child: Text(NSSLStrings.of(context)!.reorderItems())), + value: 'reorderItems', + child: Text( + NSSLStrings.of(context)!.reorderItems())), ]) ]), body: buildBody(context), @@ -101,21 +108,33 @@ class MainPageState extends State with TickerProviderStateMixin, Widge ? [] : [ TextButton( - child: Text(NSSLStrings.of(context)!.addPB()), onPressed: () => _addWithoutSearchDialog(context)) + child: Text(NSSLStrings.of(context)!.addPB()), + onPressed: () => _addWithoutSearchDialog(context)) ] + (Platform.isAndroid - ? [TextButton(child: Text(NSSLStrings.of(context)!.scanPB()), onPressed: _getEAN)] + ? [ + TextButton( + child: Text(NSSLStrings.of(context)!.scanPB()), + onPressed: _getEAN) + ] : []) + - [TextButton(child: Text(NSSLStrings.of(context)!.searchPB()), onPressed: search)]); + [ + TextButton( + child: Text(NSSLStrings.of(context)!.searchPB()), + onPressed: search) + ]); } Widget buildBody(BuildContext context) { cont = context; - if (User.currentList == null || User.currentList!.shoppingItems == null) return const Text(""); - if (User.currentList!.shoppingItems!.any((item) => item?.sortOrder == null)) updateOrderIndiciesAndSave(); + if (User.currentList == null || User.currentList!.shoppingItems == null) + return const Text(""); + if (User.currentList!.shoppingItems!.any((item) => item?.sortOrder == null)) + updateOrderIndiciesAndSave(); - User.currentList!.shoppingItems!.sort((a, b) => a!.sortOrder!.compareTo(b!.sortOrder!)); + User.currentList!.shoppingItems! + .sort((a, b) => a!.sortOrder!.compareTo(b!.sortOrder!)); var lv; if (User.currentList!.shoppingItems!.length > 0) { var mainList = User.currentList!.shoppingItems!.map((x) { @@ -130,7 +149,10 @@ class MainPageState extends State with TickerProviderStateMixin, Widge x.name ?? "", maxLines: 2, softWrap: true, - style: TextStyle(decoration: x.crossedOut ? TextDecoration.lineThrough : TextDecoration.none), + style: TextStyle( + decoration: x.crossedOut + ? TextDecoration.lineThrough + : TextDecoration.none), ), ], ), @@ -143,7 +165,7 @@ class MainPageState extends State with TickerProviderStateMixin, Widge ]), ), initialValue: x.amount.toString(), - onSelected: (y) => shoppingItemChange(x, int.parse(y) - x.amount!), + onSelected: (y) => shoppingItemChange(x, int.parse(y) - x.amount), itemBuilder: buildChangeMenuItems, ), trailing: isReorderingItems ? Icon(Icons.reorder) : null, @@ -172,13 +194,17 @@ class MainPageState extends State with TickerProviderStateMixin, Widge }).toList(growable: true); if (isReorderingItems) { - lv = ReorderableListView(onReorder: _onReorderItems, scrollDirection: Axis.vertical, children: mainList); + lv = ReorderableListView( + onReorder: _onReorderItems, + scrollDirection: Axis.vertical, + children: mainList); } else { lv = CustomScrollView( controller: _mainController, slivers: [ SliverFixedExtentList( - delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { return Container( alignment: FractionalOffset.center, child: mainList[index], @@ -211,12 +237,14 @@ class MainPageState extends State with TickerProviderStateMixin, Widge for (var old = oldIndex + 1; old < newIndex; old++) { item = User.currentList!.shoppingItems![old]; if (!item!.crossedOut) - User.currentList!.shoppingItems![old]!.sortOrder = User.currentList!.shoppingItems![old]!.sortOrder! - 1; + User.currentList!.shoppingItems![old]!.sortOrder = + User.currentList!.shoppingItems![old]!.sortOrder! - 1; } for (var newI = newIndex; newI < oldIndex; newI++) { item = User.currentList!.shoppingItems![newI]; if (!item!.crossedOut) - User.currentList!.shoppingItems![newI]!.sortOrder = User.currentList!.shoppingItems![newI]!.sortOrder! - 1; + User.currentList!.shoppingItems![newI]!.sortOrder = + User.currentList!.shoppingItems![newI]!.sortOrder! - 1; } }, ); @@ -225,14 +253,14 @@ class MainPageState extends State with TickerProviderStateMixin, Widge void sortAndOrderCrossedOut() { final crossedOffset = 0xFFFFFFFF; setState(() { - for (var crossedOut - in User.currentList?.shoppingItems?.where((x) => x!.crossedOut && x.sortOrder! < crossedOffset) ?? - []) { + for (var crossedOut in User.currentList?.shoppingItems + ?.where((x) => x!.crossedOut && x.sortOrder! < crossedOffset) ?? + []) { crossedOut?.sortOrder = crossedOut.sortOrder! + crossedOffset; } - for (var notCrossedOut - in User.currentList?.shoppingItems?.where((x) => !x!.crossedOut && x.sortOrder! > crossedOffset) ?? - []) { + for (var notCrossedOut in User.currentList?.shoppingItems + ?.where((x) => !x!.crossedOut && x.sortOrder! > crossedOffset) ?? + []) { notCrossedOut!.sortOrder = notCrossedOut.sortOrder! - crossedOffset; } }); @@ -248,14 +276,20 @@ class MainPageState extends State with TickerProviderStateMixin, Widge User.currentList?.save(); } - void showInSnackBar(String value, {Duration? duration, SnackBarAction? action}) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(value), duration: duration ?? Duration(seconds: 3), action: action)); + void showInSnackBar(String value, + {Duration? duration, SnackBarAction? action}) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(value), + duration: duration ?? Duration(seconds: 3), + action: action)); } - void showInDrawerSnackBar(String value, {Duration? duration, SnackBarAction? action}) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(value), duration: duration ?? Duration(seconds: 3), action: action)); + void showInDrawerSnackBar(String value, + {Duration? duration, SnackBarAction? action}) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(value), + duration: duration ?? Duration(seconds: 3), + action: action)); } Future register() => Navigator.pushNamed(cont!, "/registration"); @@ -268,20 +302,24 @@ class MainPageState extends State with TickerProviderStateMixin, Widge void handleDismissMain(DismissDirection dir, ShoppingItem s) async { var list = User.currentList; - final String action = - (dir == DismissDirection.endToStart) ? NSSLStrings.of(context)!.archived() : NSSLStrings.of(context)!.deleted(); + final String action = (dir == DismissDirection.endToStart) + ? NSSLStrings.of(context)!.archived() + : NSSLStrings.of(context)!.deleted(); var index = list!.shoppingItems!.indexOf(s); await list.deleteSingleItem(s); setState(() {}); ShoppingListSync.deleteProduct(list.id, s.id, context); updateOrderIndiciesAndSave(); - showInSnackBar(NSSLStrings.of(context)!.youHaveActionItemMessage() + "${s.name} $action", + showInSnackBar( + NSSLStrings.of(context)!.youHaveActionItemMessage() + + "${s.name} $action", action: SnackBarAction( label: NSSLStrings.of(context)!.undo(), onPressed: () { setState(() { list.addSingleItem(s, index: index); - ShoppingListSync.changeProductAmount(list.id, s.id, s.amount, context); + ShoppingListSync.changeProductAmount( + list.id, s.id, s.amount, context); ScaffoldMessenger.of(context).removeCurrentSnackBar(); updateOrderIndiciesAndSave(); }); @@ -301,8 +339,10 @@ class MainPageState extends State with TickerProviderStateMixin, Widge builder: (BuildContext context) => CustomThemePage(), fullscreenDialog: true, )) - .whenComplete(() => AdaptiveTheme.of(context) - .setTheme(light: Themes.lightTheme.theme!, dark: Themes.darkTheme.theme, notify: true)); + .whenComplete(() => AdaptiveTheme.of(context).setTheme( + light: Themes.lightTheme.theme!, + dark: Themes.darkTheme.theme, + notify: true)); break; case "PerformanceOverlay": setState(() => performanceOverlay = !performanceOverlay); @@ -335,13 +375,12 @@ class MainPageState extends State with TickerProviderStateMixin, Widge }); Future _getEAN() async { - ean = await Navigator.push( - cont!, - MaterialPageRoute( - builder: (BuildContext context) => BarcodeScannerScreen(), - fullscreenDialog: true, - )); + cont!, + MaterialPageRoute( + builder: (BuildContext context) => BarcodeScannerScreen(), + fullscreenDialog: true, + )); if (ean == null || ean == "" || ean == "Permissions denied") return; @@ -352,18 +391,23 @@ class MainPageState extends State with TickerProviderStateMixin, Widge if (k.success!) { RegExp reg = RegExp("([0-9]+[.,]?[0-9]*(\\s)?[gkmlGKML]{1,2})"); - String? name = reg.hasMatch(k.name!) ? k.name : "${k.name} ${k.quantity}${k.unit}"; - var item = list?.shoppingItems?.firstWhere((x) => x!.name == name, orElse: () => null); + String? name = + reg.hasMatch(k.name!) ? k.name : "${k.name} ${k.quantity}${k.unit}"; + var item = list?.shoppingItems + ?.firstWhere((x) => x!.name == name, orElse: () => null); ShoppingItem afterAdd; if (item != null) { - var answer = await ShoppingListSync.changeProductAmount(list!.id, item.id, 1, cont); + var answer = await ShoppingListSync.changeProductAmount( + list!.id, item.id, 1, cont); var p = ChangeListItemResult.fromJson((answer).body); setState(() { item.amount = p.amount; item.changed = p.changed; }); } else { - var p = AddListItemResult.fromJson((await ShoppingListSync.addProduct(list!.id, name, '-', 1, cont)).body); + var p = AddListItemResult.fromJson( + (await ShoppingListSync.addProduct(list!.id, name, '-', 1, cont)) + .body); afterAdd = ShoppingItem("${p.name}") ..amount = 1 ..id = p.productId; @@ -378,7 +422,8 @@ class MainPageState extends State with TickerProviderStateMixin, Widge Navigator.push( context, MaterialPageRoute( - builder: (BuildContext context) => AddProductToDatabase(ean), fullscreenDialog: true)); + builder: (BuildContext context) => AddProductToDatabase(ean), + fullscreenDialog: true)); } void addListDialog() { @@ -389,7 +434,10 @@ class MainPageState extends State with TickerProviderStateMixin, Widge title: NSSLStrings.of(context)!.addNewListTitle(), context: cont); - showDialog(builder: (BuildContext context) => sd, context: cont!, barrierDismissible: false); + showDialog( + builder: (BuildContext context) => sd, + context: cont!, + barrierDismissible: false); } Future renameListDialog(int listId) { @@ -410,23 +458,28 @@ class MainPageState extends State with TickerProviderStateMixin, Widge var newList = ShoppingList(newListRes.id, newListRes.name); setState(() => User.shoppingLists.add(newList)); changeCurrentList(User.shoppingLists.indexOf(newList)); - firebaseMessaging?.subscribeToTopic(newList.id.toString() + "shoppingListTopic"); + firebaseMessaging + ?.subscribeToTopic(newList.id.toString() + "shoppingListTopic"); newList.save(); } Widget _buildDrawer(BuildContext context) { var isDarkTheme = AdaptiveTheme.of(context).mode == AdaptiveThemeMode.dark; var userheader = UserAccountsDrawerHeader( - accountName: Text(User.username ?? NSSLStrings.of(context)!.notLoggedInYet()), - accountEmail: Text(User.eMail ?? NSSLStrings.of(context)!.notLoggedInYet()), + accountName: + Text(User.username ?? NSSLStrings.of(context)!.notLoggedInYet()), + accountEmail: + Text(User.eMail ?? NSSLStrings.of(context)!.notLoggedInYet()), currentAccountPicture: CircleAvatar( child: Text( User.username?.substring(0, 2).toUpperCase() ?? "", style: TextStyle(color: isDarkTheme ? Colors.black : Colors.white), ), backgroundColor: isDarkTheme - ? Themes.darkTheme.theme!.floatingActionButtonTheme.backgroundColor - : Themes.lightTheme.theme!.floatingActionButtonTheme.backgroundColor), + ? Themes + .darkTheme.theme!.floatingActionButtonTheme.backgroundColor + : Themes + .lightTheme.theme!.floatingActionButtonTheme.backgroundColor), onDetailsPressed: () { _showDrawerContents = !_showDrawerContents; _showDrawerContents ? _controller!.reverse() : _controller!.forward(); @@ -437,24 +490,29 @@ class MainPageState extends State with TickerProviderStateMixin, Widge ? User.shoppingLists .map((x) => ListTile( title: Text(x.name ?? ""), - onTap: () => - changeCurrentList(User.shoppingLists.indexOf(User.shoppingLists.firstWhere((y) => y.id == x.id))), + onTap: () => changeCurrentList(User.shoppingLists.indexOf( + User.shoppingLists.firstWhere((y) => y.id == x.id))), trailing: PopupMenuButton( padding: EdgeInsets.zero, - onSelected: (v) async => await drawerListItemMenuClicked(v, context), - itemBuilder: (BuildContext context) => >[ + onSelected: (v) async => + await drawerListItemMenuClicked(v), + itemBuilder: (BuildContext context) => + >[ PopupMenuItem( - value: x.id.toString() + "\u{1E}" + "Contributors", + value: + x.id.toString() + "\u{1E}" + "Contributors", child: ListTile( leading: const Icon(Icons.person_add), - title: Text(NSSLStrings.of(context)!.contributors()), + title: Text( + NSSLStrings.of(context)!.contributors()), ), ), PopupMenuItem( value: x.id.toString() + "\u{1E}" + "BoughtList", child: ListTile( leading: const Icon(Icons.history), - title: Text(NSSLStrings.of(context)!.boughtProducts()), + title: Text( + NSSLStrings.of(context)!.boughtProducts()), // NSSLStrings.of(context)!.contributors()), ), ), @@ -469,22 +527,29 @@ class MainPageState extends State with TickerProviderStateMixin, Widge value: x.id.toString() + "\u{1E}" + 'Rename', child: ListTile( leading: const Icon(Icons.mode_edit), - title: Text(NSSLStrings.of(context)!.rename()))), + title: Text( + NSSLStrings.of(context)!.rename()))), PopupMenuItem( value: x.id.toString() + "\u{1E}" + 'Auto-Sync', child: ListTile( - leading: Icon(x.messagingEnabled ? Icons.check_box : Icons.check_box_outline_blank), - title: Text(NSSLStrings.of(context)!.autoSync()))), + leading: Icon(x.messagingEnabled + ? Icons.check_box + : Icons.check_box_outline_blank), + title: Text( + NSSLStrings.of(context)!.autoSync()))), const PopupMenuDivider(), PopupMenuItem( value: x.id.toString() + "\u{1E}" + 'Remove', child: ListTile( - leading: const Icon(Icons.delete), title: Text(NSSLStrings.of(context)!.remove()))) + leading: const Icon(Icons.delete), + title: Text( + NSSLStrings.of(context)!.remove()))) ]), )) .toList() : [ - ListTile(title: Text(NSSLStrings.of(context)!.noListsInDrawerMessage())), + ListTile( + title: Text(NSSLStrings.of(context)!.noListsInDrawerMessage())), ]; var emptyListTiles = []; for (int i = 0; i < list.length - 2; i++) @@ -547,29 +612,32 @@ class MainPageState extends State with TickerProviderStateMixin, Widge onRefresh: _handleDrawerRefresh, displacement: 1.0), persistentFooterButtons: [ - TextButton(child: Text(NSSLStrings.of(context)!.addListPB()), onPressed: addListDialog) + TextButton( + child: Text(NSSLStrings.of(context)!.addListPB()), + onPressed: addListDialog) ]); return Drawer(child: d); } - Future drawerListItemMenuClicked(String value, BuildContext conte) async { + Future drawerListItemMenuClicked(String value) async { var splitted = value.split('\u{1E}'); int id = int.parse(splitted[0]); switch (splitted[1]) { case "Contributors": - Navigator.maybeOf(conte)?.push(MaterialPageRoute( + Navigator.maybeOf(context)?.push(MaterialPageRoute( builder: (BuildContext context) => ContributorsPage(id), fullscreenDialog: true, )); break; case "BoughtList": - Navigator.push( + await Navigator.push( cont!, MaterialPageRoute( builder: (BuildContext context) => BoughtItemsPage(id), fullscreenDialog: true, )); + setState(() {}); break; case "Rename": renameListDialog(id); @@ -580,30 +648,39 @@ class MainPageState extends State with TickerProviderStateMixin, Widge context: cont!, barrierDismissible: false, builder: (BuildContext context) => SimpleDialogAcceptDeny.create( - title: NSSLStrings.of(cont)?.deleteListTitle() ?? "" + deleteList.name!, + title: NSSLStrings.of(cont)?.deleteListTitle() ?? + "" + deleteList.name!, text: NSSLStrings.of(cont)?.deleteListText() ?? "", onSubmitted: (s) async { - var res = Result.fromJson((await ShoppingListSync.deleteList(id, cont)).body); + var res = Result.fromJson( + (await ShoppingListSync.deleteList(id, cont)).body); if (!(res.success ?? false)) showInDrawerSnackBar(res.error!); else { - showInDrawerSnackBar(deleteList.name! + " " + NSSLStrings.of(cont)!.removed()); + showInDrawerSnackBar(deleteList.name! + + " " + + NSSLStrings.of(cont)!.removed()); if (User.currentList!.id! == id) { - changeCurrentList(User.shoppingLists.indexOf(User.shoppingLists.firstWhere((l) => l.id != id))); + changeCurrentList(User.shoppingLists.indexOf( + User.shoppingLists.firstWhere((l) => l.id != id))); } - setState(() => User.shoppingLists.removeWhere((x) => x.id == id)); + setState(() => + User.shoppingLists.removeWhere((x) => x.id == id)); } }, context: cont)); break; case "Auto-Sync": var list = User.shoppingLists.firstWhere((x) => x.id == id); - list.messagingEnabled ? list.unsubscribeFromFirebaseMessaging() : list.subscribeForFirebaseMessaging(); + list.messagingEnabled + ? list.unsubscribeFromFirebaseMessaging() + : list.subscribeForFirebaseMessaging(); list.messagingEnabled = !list.messagingEnabled; list.save(); break; case "ExportAsPdf": - ExportManager.exportAsPDF(User.shoppingLists.firstWhere((x) => x.id == id), context); + ExportManager.exportAsPDF( + User.shoppingLists.firstWhere((x) => x.id == id), context); break; } } @@ -613,7 +690,8 @@ class MainPageState extends State with TickerProviderStateMixin, Widge setState(() => {}); } - Future _handleMainListRefresh() => _handleListRefresh(User.currentList!.id); + Future _handleMainListRefresh() => + _handleListRefresh(User.currentList!.id); Future _handleListRefresh(int? listId) async { await User.shoppingLists.firstWhere((s) => s.id == listId).refresh(cont); @@ -622,7 +700,9 @@ class MainPageState extends State with TickerProviderStateMixin, Widge Future shoppingItemChange(ShoppingItem s, int change) async { var res = ChangeListItemResult.fromJson( - (await ShoppingListSync.changeProductAmount(User.currentList!.id!, s.id, change, cont)).body); + (await ShoppingListSync.changeProductAmount( + User.currentList!.id!, s.id, change, cont)) + .body); setState(() { s.id = res.id; s.amount = res.amount; @@ -635,7 +715,8 @@ class MainPageState extends State with TickerProviderStateMixin, Widge List> buildChangeMenuItems(BuildContext context) { if (amountPopList.length == 0) for (int i = 1; i <= 99; i++) - amountPopList.add(PopupMenuItem(value: i.toString(), child: Text(i.toString()))); + amountPopList.add(PopupMenuItem( + value: i.toString(), child: Text(i.toString()))); return amountPopList; } @@ -669,9 +750,11 @@ class MainPageState extends State with TickerProviderStateMixin, Widge Future _addWithoutSearch(String value) async { var list = User.currentList; - var same = list!.shoppingItems!.where((x) => x!.name!.toLowerCase() == value.toLowerCase()); + var same = list!.shoppingItems! + .where((x) => x!.name!.toLowerCase() == value.toLowerCase()); if (same.length > 0) { - var res = await ShoppingListSync.changeProductAmount(list.id, same.first!.id!, 1, cont); + var res = await ShoppingListSync.changeProductAmount( + list.id, same.first!.id!, 1, cont); if (res.statusCode != 200) showInSnackBar(res.reasonPhrase!); var product = ChangeListItemResult.fromJson(res.body); if (!product.success!) showInSnackBar(product.error!); @@ -681,7 +764,8 @@ class MainPageState extends State with TickerProviderStateMixin, Widge }); same.first; } else { - var res = await ShoppingListSync.addProduct(list.id, value, null, 1, cont); + var res = + await ShoppingListSync.addProduct(list.id, value, null, 1, cont); if (res.statusCode != 200) showInSnackBar(res.reasonPhrase!); var product = AddListItemResult.fromJson(res.body); if (!product.success!) showInSnackBar(product.error!); @@ -696,7 +780,8 @@ class MainPageState extends State with TickerProviderStateMixin, Widge Future _deleteCrossedOutItems() async { var list = User.currentList; var sublist = list!.shoppingItems!.where((s) => s!.crossedOut).toList(); - var res = await ShoppingListSync.deleteProducts(list.id, sublist.map((s) => s!.id).toList(), cont); + var res = await ShoppingListSync.deleteProducts( + list.id, sublist.map((s) => s!.id).toList(), cont); if (!Result.fromJson(res.body).success!) return; setState(() { for (var item in sublist) list.shoppingItems?.remove(item); @@ -708,10 +793,13 @@ class MainPageState extends State with TickerProviderStateMixin, Widge label: NSSLStrings.of(context)!.undo(), onPressed: () async { var res = await ShoppingListSync.changeProducts( - list.id, sublist.map((s) => s!.id).toList(), sublist.map((s) => s!.amount).toList(), cont); + list.id, + sublist.map((s) => s!.id).toList(), + sublist.map((s) => s!.amount).toList(), + cont); var hashResult = HashResult.fromJson(res.body); int ownHash = 0; - for (var item in sublist) ownHash += item!.id! + item.amount!; + for (var item in sublist) ownHash += item!.id! + item.amount; if (ownHash == hashResult.hash) { setState(() => list.shoppingItems?.addAll(sublist)); updateOrderIndiciesAndSave(); @@ -734,7 +822,9 @@ class MainPageState extends State with TickerProviderStateMixin, Widge maxLines: 2, onSubmitted: (s) async { var res = ChangeListItemResult.fromJson( - (await ShoppingListSync.changeProductName(User.currentList!.id, x!.id, s, cont)).body); + (await ShoppingListSync.changeProductName( + User.currentList!.id, x!.id, s, cont)) + .body); setState(() { x.id = res.id; x.amount = res.amount; @@ -752,10 +842,12 @@ class MainPageState extends State with TickerProviderStateMixin, Widge var ids = []; ids.addAll(User.currentList!.shoppingItems!.map((e) => e!.clone())); ids.forEach((element) { - if (element.sortOrder! > 0xffffffff) element.sortOrder = element.sortOrder! - 0xffffffff; + if (element.sortOrder! > 0xffffffff) + element.sortOrder = element.sortOrder! - 0xffffffff; }); ids.sort((x, y) => x.sortOrder!.compareTo(y.sortOrder!)); - await ShoppingListSync.reorderProducts(User.currentList!.id, ids.map((e) => e.id).toList(), context); + await ShoppingListSync.reorderProducts( + User.currentList!.id, ids.map((e) => e.id).toList(), context); setState(() { isReorderingItems = false; }); diff --git a/lib/pages/product_add_to_database.dart b/lib/pages/product_add_to_database.dart index c5c5b88..0becf27 100644 --- a/lib/pages/product_add_to_database.dart +++ b/lib/pages/product_add_to_database.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unnecessary_import + import 'dart:async'; import 'package:flutter/material.dart'; import 'package:nssl/localization/nssl_strings.dart'; @@ -17,8 +19,7 @@ class AddProductToDatabase extends StatefulWidget { final String? gtin; @override - AddProductToDatabaseState createState() => - AddProductToDatabaseState(gtin); + AddProductToDatabaseState createState() => AddProductToDatabaseState(gtin); } class AddProductToDatabaseState extends State { @@ -43,32 +44,31 @@ class AddProductToDatabaseState extends State { if (!_saveNeeded) return true; final ThemeData theme = Theme.of(context); - final TextStyle dialogTextStyle = - theme.textTheme.subtitle1!.copyWith(color: theme.textTheme.caption!.color); + final TextStyle dialogTextStyle = theme.textTheme.subtitle1! + .copyWith(color: theme.textTheme.caption!.color); return await (showDialog( context: context, builder: (BuildContext context) => AlertDialog( - content: Text(NSSLStrings.of(context)!.discardNewProduct(), - style: dialogTextStyle), - actions: [ - TextButton( - child: Text(NSSLStrings.of(context)!.cancelButton()), - onPressed: () { - Navigator.of(context).pop(false); - }), - TextButton( - child: Text(NSSLStrings.of(context)!.discardButton()), - onPressed: () { - Navigator.of(context).pop(true); - }) - ])) as FutureOr?) ?? + content: Text(NSSLStrings.of(context)!.discardNewProduct(), + style: dialogTextStyle), + actions: [ + TextButton( + child: Text(NSSLStrings.of(context)!.cancelButton()), + onPressed: () { + Navigator.of(context).pop(false); + }), + TextButton( + child: Text(NSSLStrings.of(context)!.discardButton()), + onPressed: () { + Navigator.of(context).pop(true); + }) + ])) as FutureOr?) ?? false; } void showInSnackBar(String value) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(value))); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(value))); } Future _handleSubmitted() async { @@ -78,7 +78,8 @@ class AddProductToDatabaseState extends State { } final FormState form = _formKey.currentState!; if (!form.validate()) { - showInSnackBar(NSSLStrings.of(context)!.fixErrorsBeforeSubmittingPrompt()); + showInSnackBar( + NSSLStrings.of(context)!.fixErrorsBeforeSubmittingPrompt()); validateMode = AutovalidateMode.onUserInteraction; return false; } else { @@ -142,15 +143,16 @@ class AddProductToDatabaseState extends State { actions: [ TextButton( child: Text(NSSLStrings.of(context)!.saveButton(), - style: theme.textTheme.bodyText2!.copyWith(color: Colors.white)), + style: theme.textTheme.bodyText2! + .copyWith(color: Colors.white)), onPressed: () => _handleSubmitted()) ]), body: Form( key: _formKey, onWillPop: _onWillPop, - autovalidateMode: validateMode, - child: ListView(padding: const EdgeInsets.all(16.0), children: < - Widget>[ + autovalidateMode: validateMode, + child: + ListView(padding: const EdgeInsets.all(16.0), children: [ Container( child: TextFormField( decoration: InputDecoration( @@ -184,8 +186,8 @@ class AddProductToDatabaseState extends State { Container( padding: const EdgeInsets.symmetric(vertical: 8.0), decoration: BoxDecoration( - border: Border( - bottom: BorderSide(color: theme.dividerColor))), + border: + Border(bottom: BorderSide(color: theme.dividerColor))), alignment: FractionalOffset.bottomLeft, child: Text(NSSLStrings.of(context)!.codeText() + gtin!)), Container( @@ -199,8 +201,7 @@ class AddProductToDatabaseState extends State { ])), Container( padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - NSSLStrings.of(context)!.newProductStarExplanation(), + child: Text(NSSLStrings.of(context)!.newProductStarExplanation(), style: Theme.of(context).textTheme.caption), ), ])), diff --git a/lib/pages/shopping_item_search.dart b/lib/pages/shopping_item_search.dart index b20421b..91a6ad0 100644 --- a/lib/pages/shopping_item_search.dart +++ b/lib/pages/shopping_item_search.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:nssl/localization/nssl_strings.dart'; import 'package:nssl/models/model_export.dart'; -import 'package:flutter/widgets.dart'; import 'package:nssl/server_communication//s_c.dart'; import 'dart:async'; import 'dart:convert'; @@ -41,18 +40,21 @@ class _ProductAddPageState extends State { if (list != null) { if (list.shoppingItems == null) list.shoppingItems = []; - var item = list.shoppingItems!.firstWhere((x) => x!.name == name, orElse: () => null); + var item = list.shoppingItems! + .firstWhere((x) => x!.name == name, orElse: () => null); ShoppingItem? afterAdd; if (item != null) { - var answer = await ShoppingListSync.changeProductAmount(list.id!, item.id!, 1, context); + var answer = await ShoppingListSync.changeProductAmount( + list.id!, item.id!, 1, context); var p = ChangeListItemResult.fromJson((answer).body); setState(() { item.amount = p.amount; item.changed = p.changed; }); } else { - var p = AddListItemResult.fromJson( - (await ShoppingListSync.addProduct(list.id!, name!, gtin ?? '-', 1, context)).body); + var p = AddListItemResult.fromJson((await ShoppingListSync.addProduct( + list.id!, name!, gtin ?? '-', 1, context)) + .body); afterAdd = ShoppingItem(p.name) ..amount = 1 ..id = p.productId; @@ -68,12 +70,15 @@ class _ProductAddPageState extends State { label: NSSLStrings.of(context)!.undo(), onPressed: () async { var res = item == null - ? await ShoppingListSync.deleteProduct(list.id!, afterAdd!.id!, context) - : await ShoppingListSync.changeProductAmount(list.id!, item.id!, -1, context); + ? await ShoppingListSync.deleteProduct( + list.id!, afterAdd!.id!, context) + : await ShoppingListSync.changeProductAmount( + list.id!, item.id!, -1, context); if (Result.fromJson(res.body).success!) { if (item == null) list.shoppingItems!.remove(afterAdd); - else if (item.amount != null) item.amount = item.amount! - 1; + else + item.amount = item.amount - 1; } })); list.save(); @@ -93,7 +98,8 @@ class _ProductAddPageState extends State { title: Form( child: TextField( key: _iff, - decoration: InputDecoration(hintText: NSSLStrings.of(context)!.searchProductHint()), + decoration: InputDecoration( + hintText: NSSLStrings.of(context)!.searchProductHint()), onSubmitted: (x) => _searchProducts(x, 1), autofocus: true, controller: tec, @@ -126,7 +132,8 @@ class _ProductAddPageState extends State { List? z = jsonDecode(o.body); // .decode(o.body); if (!noMoreProducts && z!.length <= 0) { noMoreProducts = true; - showInSnackBar(NSSLStrings.of(context)!.noMoreProductsMessage(), duration: Duration(seconds: 3)); + showInSnackBar(NSSLStrings.of(context)!.noMoreProductsMessage(), + duration: Duration(seconds: 3)); } else setState(() => prList.addAll(z! .map((f) => ProductResult() @@ -145,7 +152,8 @@ class _ProductAddPageState extends State { lastLength = prList.length; } return ListTile( - title: Text(prList[i].name!), onTap: () => _addProductToList(prList[i].name, prList[i].gtin)); + title: Text(prList[i].name!), + onTap: () => _addProductToList(prList[i].name, prList[i].gtin)); }, itemCount: prList.length); return listView; @@ -153,9 +161,12 @@ class _ProductAddPageState extends State { return Text(""); } - void showInSnackBar(String value, {Duration? duration, SnackBarAction? action}) { + void showInSnackBar(String value, + {Duration? duration, SnackBarAction? action}) { ScaffoldMessenger.of(context).removeCurrentSnackBar(); - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(value), duration: duration ?? Duration(seconds: 3), action: action)); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(value), + duration: duration ?? Duration(seconds: 3), + action: action)); } } diff --git a/lib/server_communication/return_classes.dart b/lib/server_communication/return_classes.dart index 7ae4e6d..732983a 100644 --- a/lib/server_communication/return_classes.dart +++ b/lib/server_communication/return_classes.dart @@ -3,12 +3,12 @@ import 'dart:convert'; import 'package:nssl/models_json.dart'; class BaseResult { - bool? success; + bool? success; String? error; } class CreateResult extends BaseResult { - int? id; + int? id; String? username; String? eMail; static CreateResult fromJson(String dataString) => @@ -21,13 +21,13 @@ class CreateResult extends BaseResult { r.id = data["id"]; r.username = data["username"]; r.eMail = data["eMail"]; - + return r; } } class LoginResult extends BaseResult { - int? id; + int? id; String? username; String? eMail; String? token; @@ -47,7 +47,7 @@ class LoginResult extends BaseResult { class AddContributorResult extends BaseResult { String? name; - int? id; + int? id; static AddContributorResult fromJson(String dataString) => _fromJson(jsonDecode(dataString)); @@ -63,8 +63,8 @@ class AddContributorResult extends BaseResult { class ContributorResult { String? name; - int? userId; - bool? isAdmin; + int? userId; + bool? isAdmin; } class GetContributorsResult extends BaseResult { @@ -88,7 +88,7 @@ class GetContributorsResult extends BaseResult { } } -class ProductResult extends BaseResult{ +class ProductResult extends BaseResult { String? name; String? gtin; double? quantity; @@ -108,8 +108,8 @@ class ProductResult extends BaseResult{ } } -class AddListItemResult extends BaseResult{ - int? productId; +class AddListItemResult extends BaseResult { + int? productId; String? name; String? gtin; static AddListItemResult fromJson(String dataString) => @@ -126,12 +126,12 @@ class AddListItemResult extends BaseResult{ } } -class ChangeListItemResult extends BaseResult{ +class ChangeListItemResult extends BaseResult { String? name; - int? id; - int? amount; - int? listId; - DateTime? changed; + int? id; + late int amount; + int? listId; + DateTime? changed; static ChangeListItemResult fromJson(String dataString) => _fromJson(jsonDecode(dataString)); @@ -148,8 +148,8 @@ class ChangeListItemResult extends BaseResult{ } } -class AddListResult extends BaseResult{ - int? id; +class AddListResult extends BaseResult { + int? id; String? name; static AddListResult fromJson(String dataString) => _fromJson(jsonDecode(dataString)); @@ -165,12 +165,12 @@ class AddListResult extends BaseResult{ } class GetListResult { - int? id; + int? id; String? name; - int? userId; + int? userId; String? owner; - DateTime? changed; - DateTime? created; + DateTime? changed; + DateTime? created; Iterable? products; String? contributors; @@ -184,10 +184,14 @@ class GetListResult { r.userId = data["userId"]; r.owner = data["owner"]; var unMaped = data["products"] ?? []; - r.products = - unMaped.map((x) => - ShoppingItem(x["id"], x["amount"], x["name"], DateTime.tryParse(x["changed"]), DateTime.tryParse(x["created"]), x["sortOrder"])); - + r.products = unMaped.map((x) => ShoppingItem( + x["id"], + x["amount"], + x["name"], + DateTime.tryParse(x["changed"]), + DateTime.tryParse(x["created"]), + x["sortOrder"])); + r.contributors = data["contributors"]; return r; @@ -206,7 +210,15 @@ class GetListsResult { List unmappedShoppingLists = data["lists"]; r.shoppingLists = unmappedShoppingLists.map((s) => ShoppingList() ..products = s["products"] - .map((x) => ShoppingItem(x["id"], x["amount"], x["name"],DateTime.tryParse(x["changed"]), DateTime.tryParse(x["created"]), x["sortOrder"])).toList().cast() + .map((x) => ShoppingItem( + x["id"], + x["amount"], + x["name"], + DateTime.tryParse(x["changed"]), + DateTime.tryParse(x["created"]), + x["sortOrder"])) + .toList() + .cast() ..id = s["id"] ..name = s["name"]); @@ -214,8 +226,8 @@ class GetListsResult { } } -class GetBoughtListResult{ - int? id; +class GetBoughtListResult { + int? id; String? name; late Iterable products; @@ -227,15 +239,19 @@ class GetBoughtListResult{ r.id = data["id"]; r.name = data["name"]; List unMaped = data["products"] ?? []; - r.products = - unMaped.map((x) => ShoppingItem(x["id"], x["boughtAmount"], x["name"],DateTime.tryParse(x["changed"]), DateTime.tryParse(x["created"]), x["sortOrder"])); + r.products = unMaped.map((x) => ShoppingItem( + x["id"], + x["boughtAmount"], + x["name"], + DateTime.tryParse(x["changed"]), + DateTime.tryParse(x["created"]), + x["sortOrder"])); return r; } } - class InfoResult { - int? id; + int? id; String? username; String? eMail; List? listIds; @@ -253,7 +269,7 @@ class InfoResult { } class HashResult extends Result { - int? hash; + int? hash; static HashResult fromJson(String dataString) => _fromJson(jsonDecode(dataString)); @@ -266,7 +282,7 @@ class HashResult extends Result { } } -class Result extends BaseResult{ +class Result extends BaseResult { static Result fromJson(String dataString) => _fromJson(jsonDecode(dataString)); diff --git a/tool/env.dart b/tool/env.dart index 44b055b..f362125 100644 --- a/tool/env.dart +++ b/tool/env.dart @@ -1,13 +1,11 @@ -import 'dart:convert'; import 'dart:io'; - Future main() async { - - var content = 'final String scanditLicenseKey = "${Platform.environment['Scandit']}";'; - content += '\r\nfinal String scanditLicenseKeyDebug = "${Platform.environment['ScanditDebug']}";'; - + var content = + 'final String scanditLicenseKey = "${Platform.environment['Scandit']}";'; + content += + '\r\nfinal String scanditLicenseKeyDebug = "${Platform.environment['ScanditDebug']}";'; final filename = 'lib/.license.dart'; File(filename).writeAsString(content); -} \ No newline at end of file +}