From 1a602f0d2cd3478c501c5f48ead18e3cb4c3df01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maty=C3=A1=C5=A1=20Caras?= Date: Mon, 16 May 2022 20:26:39 +0200 Subject: [PATCH] Zprovoznit offline ukladani jidelnicku a nastaveni --- CHANGELOG.md | 1 + ios/Runner/Info.plist | 2 +- lib/lang/lang.dart | 7 ++ lib/lang/lang_cz.dart | 9 ++ lib/lang/lang_en.dart | 11 ++- lib/main.dart | 116 ++++++++++++++++------ lib/okna/jidelnicek.dart | 27 +++++- lib/okna/nastaveni.dart | 80 ++++++++++++---- lib/okna/offline_jidelnicek.dart | 160 +++++++++++++++++++++++++++++++ lib/util.dart | 17 ++++ 10 files changed, 377 insertions(+), 53 deletions(-) create mode 100644 lib/okna/offline_jidelnicek.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ea0837..9aa21b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - Stránka s možnostmi nastavení aplikace - Přidán anglický překlad - Opravy chyb +- Přidání možnosti přeskočení víkendu # 0.1.1 - Přidán RefreshIndicator na obrazovku s jídelníčkem - Přidáno odsazení od okrajů u jídelníčku diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index e6d5fc7..e8f7e87 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -10,7 +10,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Opencanteen + OpenCanteen CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/lib/lang/lang.dart b/lib/lang/lang.dart index 336d6c9..cd5c1e7 100644 --- a/lib/lang/lang.dart +++ b/lib/lang/lang.dart @@ -112,4 +112,11 @@ abstract class Languages { String get settings; String get saveOffline; + + String get skipWeekend; + + // Offline + String get offline; + + String get mustLogout; } diff --git a/lib/lang/lang_cz.dart b/lib/lang/lang_cz.dart index 435d0cf..9ea0573 100644 --- a/lib/lang/lang_cz.dart +++ b/lib/lang/lang_cz.dart @@ -69,6 +69,9 @@ class LanguageCz extends Languages { @override String get monday => "Pondělí"; + @override + String get mustLogout => "Online přejdete přetažením dolů."; + @override String get no => "Ne"; @@ -85,6 +88,9 @@ class LanguageCz extends Languages { String get notOfficial => "Toto není oficiální aplikace k ovládání iCanteen. Autor neručí za ztráty nebo nefunkčnost v souvislosti s používáním této aplikace. Tato zpráva se znovu neukáže."; + @override + String get offline => "JSTE OFFLINE"; + @override String get ok => "OK"; @@ -124,6 +130,9 @@ class LanguageCz extends Languages { @override String get signOut => "Odhlásit se"; + @override + String get skipWeekend => "Při procházení menu přeskočit víkend"; + @override String get sunday => "Neděle"; diff --git a/lib/lang/lang_en.dart b/lib/lang/lang_en.dart index fc8665f..cf3c07c 100644 --- a/lib/lang/lang_en.dart +++ b/lib/lang/lang_en.dart @@ -69,6 +69,9 @@ class LanguageEn extends Languages { @override String get monday => "Monday"; + @override + String get mustLogout => "To go online, pull down."; + @override String get no => "No"; @@ -85,6 +88,9 @@ class LanguageEn extends Languages { String get notOfficial => "This is not an official app for accessing iCanteen. The author is not responsible for non-functionality or losses while using this app. This message will not appear again."; + @override + String get offline => "YOU ARE OFFLINE"; + @override String get ok => "OK"; @@ -116,7 +122,7 @@ class LanguageEn extends Languages { String get saturday => "Saturday"; @override - String get saveOffline => "Save today`s menu offline"; + String get saveOffline => "Save today's menu offline"; @override String get settings => "Settings"; @@ -124,6 +130,9 @@ class LanguageEn extends Languages { @override String get signOut => "Sign out"; + @override + String get skipWeekend => "Skip weekends when browsing menu"; + @override String get sunday => "Sunday"; diff --git a/lib/main.dart b/lib/main.dart index d0a9852..a83c2ac 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; @@ -5,6 +8,9 @@ import 'package:opencanteen/lang/lang_cz.dart'; import 'package:opencanteen/loginmanager.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:canteenlib/canteenlib.dart'; +import 'package:opencanteen/okna/offline_jidelnicek.dart'; +import 'package:opencanteen/util.dart'; +import 'package:path_provider/path_provider.dart'; import 'lang/lang.dart'; import 'lang/lang_en.dart'; @@ -82,6 +88,7 @@ class _LoginPageState extends State { content: Text(Languages.of(context)!.errorContacting), ), ); + goOffline(); } if (r != null) { @@ -101,24 +108,34 @@ class _LoginPageState extends State { ), )); var canteen = Canteen(r["url"]!); - var l = await canteen.login(r["user"]!, r["pass"]!); - if (!l) { + try { + var l = await canteen.login(r["user"]!, r["pass"]!); + if (!l) { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(Languages.of(context)!.loginFailed), + ), + ); + return; + } + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => JidelnicekPage( + user: r["user"]!, + canteen: canteen, + )), + ); + } catch (_) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(Languages.of(context)!.loginFailed), + content: Text(Languages.of(context)!.errorContacting), ), ); - return; + goOffline(); } - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => JidelnicekPage( - user: r["user"]!, - canteen: canteen, - )), - ); } }); } @@ -138,7 +155,7 @@ class _LoginPageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - Languages.of(context)!.logIn, + Languages.of(context)!.appName, textAlign: TextAlign.center, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 40), @@ -237,29 +254,41 @@ class _LoginPageState extends State { "https://" + canteenControl.text; } var canteen = Canteen(canteenControl.text); - var l = await canteen.login( - userControl.text, passControl.text); - if (!l) { + try { + var l = await canteen.login( + userControl.text, passControl.text); + if (!l) { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: + Text(Languages.of(context)!.loginFailed), + ), + ); + return; + } + if (rememberMe) { + LoginManager.setDetails(userControl.text, + passControl.text, canteenControl.text); + } + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => JidelnicekPage( + user: userControl.text, + canteen: canteen, + )), + ); + } on Exception catch (_) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(Languages.of(context)!.loginFailed), + content: + Text(Languages.of(context)!.errorContacting), ), ); - return; + goOffline(); } - if (rememberMe) { - LoginManager.setDetails(userControl.text, - passControl.text, canteenControl.text); - } - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => JidelnicekPage( - user: userControl.text, - canteen: canteen, - )), - ); }, child: Text(Languages.of(context)!.logIn)), ], @@ -268,6 +297,32 @@ class _LoginPageState extends State { ), )); } + + void goOffline() async { + Directory appDocDir = await getApplicationDocumentsDirectory(); + var den = DateTime.now(); + var soubor = File(appDocDir.path + + "/jidelnicek_${den.year}-${den.month}-${den.day}.json"); + if (soubor.existsSync()) { + // načteme offline jídelníček + var input = await soubor.readAsString(); + var r = jsonDecode(input); + List jidla = []; + for (var j in r) { + jidla.add(OfflineJidlo( + nazev: j["nazev"], + varianta: j["varianta"], + objednano: j["objednano"], + cena: j["cena"], + naBurze: j["naBurze"], + den: DateTime.parse(j["den"]))); + } + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: ((context) => OfflineJidelnicek(jidla: jidla)))); + } + } } class AppLocalizationsDelegate extends LocalizationsDelegate { @@ -280,7 +335,6 @@ class AppLocalizationsDelegate extends LocalizationsDelegate { Future load(Locale locale) => _load(locale); static Future _load(Locale locale) async { - debugPrint(locale.countryCode); switch (locale.languageCode) { case 'cs': return LanguageCz(); diff --git a/lib/okna/jidelnicek.dart b/lib/okna/jidelnicek.dart index 9594fb9..26fc388 100644 --- a/lib/okna/jidelnicek.dart +++ b/lib/okna/jidelnicek.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:canteenlib/canteenlib.dart'; import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:opencanteen/okna/nastaveni.dart'; import 'package:opencanteen/util.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -27,6 +28,7 @@ class _JidelnicekPageState extends State { DateTime den = DateTime.now(); String denTydne = ""; double kredit = 0.0; + bool _skipWeekend = false; Future nactiJidlo() async { obsah = [const CircularProgressIndicator()]; switch (den.weekday) { @@ -207,9 +209,17 @@ class _JidelnicekPageState extends State { } else if (value == Languages.of(context)!.about) { Navigator.push( context, MaterialPageRoute(builder: (c) => const AboutPage())); + } else if (value == Languages.of(context)!.settings) { + Navigator.push( + context, MaterialPageRoute(builder: (c) => const Nastaveni())); } } + void nactiNastaveni() async { + var prefs = await SharedPreferences.getInstance(); + _skipWeekend = prefs.getBool("skip") ?? false; + } + /// uložení jídelníčku pro dnešek offline void ulozitDnesekOffline() async { var prefs = await SharedPreferences.getInstance(); @@ -233,7 +243,8 @@ class _JidelnicekPageState extends State { "varianta": jidlo.varianta, "objednano": jidlo.objednano, "cena": jidlo.cena, - "naBurze": jidlo.naBurze + "naBurze": jidlo.naBurze, + "den": den.toString() }); } await soubor.writeAsString(json.encode(jidla)); @@ -243,6 +254,7 @@ class _JidelnicekPageState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); + nactiNastaveni(); ulozitDnesekOffline(); nactiJidlo(); } @@ -259,6 +271,7 @@ class _JidelnicekPageState extends State { itemBuilder: (BuildContext context) { return { Languages.of(context)!.reportBugs, + Languages.of(context)!.settings, Languages.of(context)!.about, Languages.of(context)!.signOut }.map((String choice) { @@ -283,6 +296,9 @@ class _JidelnicekPageState extends State { onPressed: () { setState(() { den = den.subtract(const Duration(days: 1)); + if (den.weekday == 7 && _skipWeekend) { + den = den.subtract(const Duration(days: 2)); + } nactiJidlo(); }); }, @@ -308,6 +324,9 @@ class _JidelnicekPageState extends State { onPressed: () { setState(() { den = den.add(const Duration(days: 1)); + if (den.weekday == 6 && _skipWeekend) { + den = den.add(const Duration(days: 2)); + } nactiJidlo(); }); }, @@ -328,11 +347,17 @@ class _JidelnicekPageState extends State { if (details.primaryVelocity?.compareTo(0) == -1) { setState(() { den = den.add(const Duration(days: 1)); + if (den.weekday == 6 && _skipWeekend) { + den = den.add(const Duration(days: 2)); + } nactiJidlo(); }); } else { setState(() { den = den.subtract(const Duration(days: 1)); + if (den.weekday == 7 && _skipWeekend) { + den = den.subtract(const Duration(days: 2)); + } nactiJidlo(); }); } diff --git a/lib/okna/nastaveni.dart b/lib/okna/nastaveni.dart index 2ec7c5b..d7ba2bc 100644 --- a/lib/okna/nastaveni.dart +++ b/lib/okna/nastaveni.dart @@ -1,6 +1,10 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; -// TODO + +import '../lang/lang.dart'; class Nastaveni extends StatefulWidget { const Nastaveni({Key? key}) : super(key: key); @@ -11,10 +15,14 @@ class Nastaveni extends StatefulWidget { class _NastaveniState extends State { bool _ukladatOffline = false; + bool _preskakovatVikend = false; void najitNastaveni() async { var preferences = await SharedPreferences.getInstance(); - _ukladatOffline = preferences.getBool("offline") ?? false; + setState(() { + _ukladatOffline = preferences.getBool("offline") ?? false; + _preskakovatVikend = preferences.getBool("skip") ?? false; + }); } void zmenitNastaveni(String key, bool value) async { @@ -22,29 +30,63 @@ class _NastaveniState extends State { preferences.setBool(key, value); } + @override + void initState() { + super.initState(); + najitNastaveni(); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Nastavení'), + title: Text(Languages.of(context)!.settings), ), body: Center( - child: Column( - children: [ - Row( - children: [ - const Text("Ukládat jídelníček na dnešní den offline"), - Switch( - value: _ukladatOffline, - onChanged: (value) { - setState(() { - _ukladatOffline = value; - zmenitNastaveni("offline", value); - }); - }) - ], - ) - ], + child: SizedBox( + width: MediaQuery.of(context).size.width / 1.1, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(Languages.of(context)!.saveOffline), + Switch( + value: _ukladatOffline, + onChanged: (value) { + setState(() async { + _ukladatOffline = value; + if (!value) { + Directory appDocDir = + await getApplicationDocumentsDirectory(); + for (var f in appDocDir.listSync()) { + // Vymažeme obsah + if (f.path.contains("jidelnicek")) { + f.deleteSync(); + } + } + } + zmenitNastaveni("offline", value); + }); + }) + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(Languages.of(context)!.skipWeekend), + Switch( + value: _preskakovatVikend, + onChanged: (value) { + setState(() { + _preskakovatVikend = value; + zmenitNastaveni("skip", value); + }); + }) + ], + ) + ], + ), )), ); } diff --git a/lib/okna/offline_jidelnicek.dart b/lib/okna/offline_jidelnicek.dart new file mode 100644 index 0000000..69cceb6 --- /dev/null +++ b/lib/okna/offline_jidelnicek.dart @@ -0,0 +1,160 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:opencanteen/okna/nastaveni.dart'; +import 'package:opencanteen/util.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../lang/lang.dart'; +import '../main.dart'; +import 'about.dart'; + +class OfflineJidelnicek extends StatefulWidget { + const OfflineJidelnicek({Key? key, required this.jidla}) : super(key: key); + final List jidla; + @override + State createState() => _OfflineJidelnicekState(); +} + +class _OfflineJidelnicekState extends State { + List obsah = [const CircularProgressIndicator()]; + DateTime den = DateTime.now(); + String denTydne = ""; + Future nactiJidlo() async { + den = widget.jidla[0].den; + switch (den.weekday) { + case 2: + denTydne = Languages.of(context)!.tuesday; + break; + case 3: + denTydne = Languages.of(context)!.wednesday; + break; + case 4: + denTydne = Languages.of(context)!.thursday; + break; + case 5: + denTydne = Languages.of(context)!.friday; + break; + case 6: + denTydne = Languages.of(context)!.saturday; + break; + case 7: + denTydne = Languages.of(context)!.sunday; + break; + default: + denTydne = Languages.of(context)!.monday; + } + obsah = []; + for (OfflineJidlo j in widget.jidla) { + obsah.add( + Padding( + padding: const EdgeInsets.only(top: 15), + child: InkWell( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(j.varianta), + const SizedBox(width: 10), + Flexible( + child: Text( + j.nazev, + ), + ), + Text((j.naBurze) + ? Languages.of(context)!.inExchange + : "${j.cena} Kč"), + Checkbox( + value: j.objednano, + fillColor: MaterialStateProperty.all(Colors.grey), + onChanged: (v) async { + return; + }) + ], + )), + ), + ); + } + setState(() {}); + } + + void kliknuti(String value, BuildContext context) { + if (value == Languages.of(context)!.signOut) { + const storage = FlutterSecureStorage(); + storage.deleteAll(); + Navigator.pushReplacement( + context, MaterialPageRoute(builder: (c) => const LoginPage())); + } else if (value == Languages.of(context)!.reportBugs) { + launch("https://github.com/hernikplays/opencanteen/issues/new/choose"); + } else if (value == Languages.of(context)!.about) { + Navigator.push( + context, MaterialPageRoute(builder: (c) => const AboutPage())); + } else if (value == Languages.of(context)!.settings) { + Navigator.push( + context, MaterialPageRoute(builder: (c) => const Nastaveni())); + } + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + nactiJidlo(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(Languages.of(context)!.menu), + automaticallyImplyLeading: false, + actions: [ + PopupMenuButton( + onSelected: ((String value) => kliknuti(value, context)), + itemBuilder: (BuildContext context) { + return { + Languages.of(context)!.reportBugs, + Languages.of(context)!.settings, + Languages.of(context)!.about, + Languages.of(context)!.signOut + }.map((String choice) { + return PopupMenuItem( + value: choice, + child: Text(choice), + ); + }).toList(); + }, + ), + ], + ), + body: RefreshIndicator( + child: Center( + child: SizedBox( + child: Column( + children: [ + const SizedBox(height: 10), + Text( + Languages.of(context)!.offline, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + Text(Languages.of(context)!.mustLogout), + const SizedBox(height: 10), + TextButton( + child: + Text("${den.day}. ${den.month}. ${den.year} - $denTydne"), + onPressed: () => {}, + ), + SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: Column( + children: obsah, + ), + ), + ], + ), + width: MediaQuery.of(context).size.width - 50, + ), + ), + onRefresh: () => Navigator.pushReplacement(context, + MaterialPageRoute(builder: ((context) => const LoginPage()))), + ), + ); + } +} diff --git a/lib/util.dart b/lib/util.dart index aa0f312..43c9ac9 100644 --- a/lib/util.dart +++ b/lib/util.dart @@ -66,3 +66,20 @@ Drawer drawerGenerator( } return drawer; } + +class OfflineJidlo { + String nazev; + String varianta; + bool objednano; + double cena; + bool naBurze; + DateTime den; + + OfflineJidlo( + {required this.nazev, + required this.varianta, + required this.objednano, + required this.cena, + required this.naBurze, + required DateTime this.den}); +}