diff --git a/CHANGELOG.md b/CHANGELOG.md index 92b831c..4e2c84f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.5.0 +- umožnit ukládat více dnů offline +- chyba při ukládání offline vás nyní již nevyhodí ale zobrazí pouze zprávu +- "Přihlašování" pop-up zmizí, když není přihlášení úspěšné # 1.4.2 - aktualizace knihovny flutter_local_notifications - lepší podpora pro Android 13 diff --git a/lib/lang/lang.dart b/lib/lang/lang.dart index 3abea23..97dbed5 100644 --- a/lib/lang/lang.dart +++ b/lib/lang/lang.dart @@ -147,6 +147,8 @@ abstract class Languages { String get saveOffline; + String get saveCount; + String get skipWeekend; String get checkOrdered; @@ -162,6 +164,8 @@ abstract class Languages { String get mustLogout; + String get errorSaving; + // Oznámit před obědem String get lunchNotif; diff --git a/lib/lang/lang_cz.dart b/lib/lang/lang_cz.dart index 65dcc88..1e2397b 100644 --- a/lib/lang/lang_cz.dart +++ b/lib/lang/lang_cz.dart @@ -237,4 +237,11 @@ class LanguageCz extends Languages { @override String get review => "Ohodnotit aplikaci"; + + @override + String get saveCount => "Počet dnů dostupných offline (Akt. limit je 7)"; + + @override + String get errorSaving => + "Při ukládání offline nastala chyba, zkuste to znovu později."; } diff --git a/lib/lang/lang_en.dart b/lib/lang/lang_en.dart index bd26eec..5163b2f 100644 --- a/lib/lang/lang_en.dart +++ b/lib/lang/lang_en.dart @@ -235,4 +235,11 @@ class LanguageEn extends Languages { @override String get review => "Review the app"; + + @override + String get saveCount => "Number of days to save offline (Current limit is 7)"; + + @override + String get errorSaving => + "An error occured while trying to save menu offline, try again later."; } diff --git a/lib/main.dart b/lib/main.dart index 7b3eee1..a7dfc66 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; @@ -13,7 +12,6 @@ import 'package:canteenlib/canteenlib.dart'; import 'package:opencanteen/okna/offline_jidelnicek.dart'; import 'package:opencanteen/okna/welcome.dart'; import 'package:opencanteen/util.dart'; -import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:intl/intl.dart'; import 'package:timezone/data/latest_all.dart' as tz; @@ -237,6 +235,7 @@ class _LoginPageState extends State { } } on PlatformException { if (!mounted) return; + Navigator.of(context).pop(); ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -245,6 +244,7 @@ class _LoginPageState extends State { ); } catch (_) { if (!mounted) return; + Navigator.of(context).pop(); ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -418,31 +418,11 @@ class _LoginPageState extends State { /// Získá offline soubor a zobrazí údaje 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"]))); - } - if (!mounted) return; - Navigator.pushAndRemoveUntil( - context, - MaterialPageRoute( - builder: ((context) => OfflineJidelnicek(jidla: jidla))), - (route) => false); - } + if (!mounted) return; + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute(builder: ((context) => const OfflineJidelnicek())), + (route) => false); } } diff --git a/lib/okna/jidelnicek.dart b/lib/okna/jidelnicek.dart index deb8e32..52cbe1c 100644 --- a/lib/okna/jidelnicek.dart +++ b/lib/okna/jidelnicek.dart @@ -382,44 +382,52 @@ class _JidelnicekPageState extends State { kontrolaTyden(context); } - /// uložení jídelníčku pro dnešek offline - void ulozitDnesekOffline() async { + void ulozitDoOffline() async { var prefs = await SharedPreferences.getInstance(); - if (prefs.getBool("offline") != null && prefs.getBool("offline")!) { + if (prefs.getBool("offline") ?? false) { + // vyčistit offline Directory appDocDir = await getApplicationDocumentsDirectory(); for (var f in appDocDir.listSync()) { - // Vymažeme obsah if (f.path.contains("jidelnicek")) { f.deleteSync(); } } - // Uložíme nová data - Jidelnicek j = Jidelnicek(DateTime.now(), []); - try { - j = await widget.canteen.jidelnicekDen(); - } catch (e) { - if (!widget.canteen.prihlasen) { - if (!mounted) return; // ! Přidat chybu, pokud není mounted - Navigator.pushReplacement( - context, MaterialPageRoute(builder: (c) => const LoginPage())); + // uložit *pocet* jídelníčků pro offline použití + var pocet = prefs.getInt("offline_pocet") ?? 1; + if (pocet > 7) pocet = 7; + for (var i = 0; i < pocet; i++) { + var d = den.add(Duration(days: i)); + Jidelnicek? j; + try { + j = await widget.canteen.jidelnicekDen(den: d); + } catch (e) { + if (!widget.canteen.prihlasen) { + if (!mounted) return; // ! Přidat chybu, pokud není mounted + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(Languages.of(context)!.errorSaving), + duration: const Duration(seconds: 5), + )); + break; + } } + var soubor = File( + "${appDocDir.path}/jidelnicek_${d.year}-${d.month}-${d.day}.json"); + soubor.createSync(); + var jidla = []; + for (var jidlo in j!.jidla) { + jidla.add({ + "nazev": jidlo.nazev, + "varianta": jidlo.varianta, + "objednano": jidlo.objednano, + "cena": jidlo.cena, + "naBurze": jidlo.naBurze, + "den": d.toString() + }); + } + await soubor.writeAsString(json.encode(jidla)); } - var soubor = File( - "${appDocDir.path}/jidelnicek_${den.year}-${den.month}-${den.day}.json"); - soubor.createSync(); - var jidla = []; - for (var jidlo in j.jidla) { - jidla.add({ - "nazev": jidlo.nazev, - "varianta": jidlo.varianta, - "objednano": jidlo.objednano, - "cena": jidlo.cena, - "naBurze": jidlo.naBurze, - "den": den.toString() - }); - } - await soubor.writeAsString(json.encode(jidla)); } } @@ -427,7 +435,7 @@ class _JidelnicekPageState extends State { void didChangeDependencies() { super.didChangeDependencies(); nactiNastaveni(); - ulozitDnesekOffline(); + ulozitDoOffline(); nactiJidlo(); } diff --git a/lib/okna/nastaveni.dart b/lib/okna/nastaveni.dart index 9bc6cbf..9ad82a7 100644 --- a/lib/okna/nastaveni.dart +++ b/lib/okna/nastaveni.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:canteenlib/canteenlib.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_native_timezone/flutter_native_timezone.dart'; import 'package:path_provider/path_provider.dart'; @@ -28,21 +29,25 @@ class _NastaveniState extends State { bool _oznameniObed = false; bool _zapamatovany = false; TimeOfDay _oznameniCas = TimeOfDay.now(); - + final TextEditingController _countController = + TextEditingController(text: "1"); + SharedPreferences? preferences; void najitNastaveni() async { - var preferences = await SharedPreferences.getInstance(); + preferences = await SharedPreferences.getInstance(); _zapamatovany = await LoginManager.zapamatovat(); setState(() { - _ukladatOffline = preferences.getBool("offline") ?? false; - _preskakovatVikend = preferences.getBool("skip") ?? false; - _kontrolovatTyden = preferences.getBool("tyden") ?? false; - _oznameniObed = preferences.getBool("oznamit") ?? false; - var casStr = preferences.getString("oznameni_cas"); + _ukladatOffline = preferences!.getBool("offline") ?? false; + _preskakovatVikend = preferences!.getBool("skip") ?? false; + _kontrolovatTyden = preferences!.getBool("tyden") ?? false; + _oznameniObed = preferences!.getBool("oznamit") ?? false; + _countController.text = + (preferences!.getInt("offline_pocet") ?? 1).toString(); + var casStr = preferences!.getString("oznameni_cas"); if (casStr == null) { var now = DateTime.now(); _oznameniCas = TimeOfDay.fromDateTime( DateTime.now().add(const Duration(hours: 1))); - preferences.setString("oznameni_cas", now.toString()); + preferences!.setString("oznameni_cas", now.toString()); } else { _oznameniCas = TimeOfDay.fromDateTime(DateTime.parse(casStr)); } @@ -50,8 +55,7 @@ class _NastaveniState extends State { } void zmenitNastaveni(String key, bool value) async { - var preferences = await SharedPreferences.getInstance(); - preferences.setBool(key, value); + preferences!.setBool(key, value); } @override @@ -86,6 +90,27 @@ class _NastaveniState extends State { }) ], ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(Languages.of(context)!.saveCount), + SizedBox( + width: 35, + child: TextField( + controller: _countController, + enabled: _ukladatOffline, + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + onChanged: (c) { + var cislo = int.tryParse(c); + if (cislo != null) { + preferences!.setInt("offline_pocet", cislo); + } + }, + ), + ) + ], + ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/lib/okna/offline_jidelnicek.dart b/lib/okna/offline_jidelnicek.dart index 2a13397..fb25934 100644 --- a/lib/okna/offline_jidelnicek.dart +++ b/lib/okna/offline_jidelnicek.dart @@ -1,27 +1,57 @@ +import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:opencanteen/util.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_launcher/url_launcher.dart'; import '../lang/lang.dart'; import '../main.dart'; class OfflineJidelnicek extends StatefulWidget { - const OfflineJidelnicek({Key? key, required this.jidla}) : super(key: key); - final List jidla; + const OfflineJidelnicek({Key? key}) : super(key: key); @override State createState() => _OfflineJidelnicekState(); } class _OfflineJidelnicekState extends State { List obsah = [const CircularProgressIndicator()]; + var _skipWeekend = false; DateTime den = DateTime.now(); String denTydne = ""; + List> data = []; + var jidloIndex = 0; + + void nactiZeSouboru() async { + Directory appDocDir = await getApplicationDocumentsDirectory(); + for (var f in appDocDir.listSync()) { + if (f.path.contains("jidelnicek")) { + var soubor = File(f.path); + 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"]))); + } + data.add(jidla); + } + } + nactiJidlo(); + } + Future nactiJidlo() async { - den = widget.jidla[0].den; + var jidelnicek = data[jidloIndex]; + den = jidelnicek[0].den; switch (den.weekday) { case 2: denTydne = Languages.of(context)!.tuesday; @@ -45,32 +75,33 @@ class _OfflineJidelnicekState extends State { denTydne = Languages.of(context)!.monday; } obsah = []; - for (OfflineJidlo j in widget.jidla) { + for (OfflineJidlo j in jidelnicek) { 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, + 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; - }) - ], - )), + Text((j.naBurze) + ? Languages.of(context)!.inExchange + : "${j.cena} Kč"), + Checkbox( + value: j.objednano, + fillColor: MaterialStateProperty.all(Colors.grey), + onChanged: (v) async { + return; + }) + ], + ), + ), ), ); } @@ -116,7 +147,14 @@ class _OfflineJidelnicekState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - nactiJidlo(); + nactiNastaveni(); + } + + void nactiNastaveni() async { + var prefs = await SharedPreferences.getInstance(); + _skipWeekend = prefs.getBool("skip") ?? false; + if (!mounted) return; + nactiZeSouboru(); } @override @@ -157,11 +195,61 @@ class _OfflineJidelnicekState extends State { ), Text(Languages.of(context)!.mustLogout), const SizedBox(height: 10), - TextButton( - child: - Text("${den.day}. ${den.month}. ${den.year} - $denTydne"), - onPressed: () => {}, - ), + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + IconButton( + onPressed: () { + if (data.length <= 1) return; + obsah = [const CircularProgressIndicator()]; + setState(() { + if (den.weekday == 1 && _skipWeekend) { + // pokud je pondělí a chceme přeskočit víkend + if (jidloIndex - 2 >= 0) { + jidloIndex -= data.length - 3; + } else { + jidloIndex = data.length - 1; + } + } else if (jidloIndex == 0) { + jidloIndex = data.length - 1; + } else { + jidloIndex -= 1; + } + + nactiJidlo(); + }); + }, + icon: const Icon(Icons.arrow_left)), + TextButton( + onPressed: () async {}, + child: Text( + "${den.day}. ${den.month}. ${den.year} - $denTydne")), + IconButton( + onPressed: () { + if (data.length <= 1) return; + obsah = [const CircularProgressIndicator()]; + setState(() { + if (den.weekday == 5 && _skipWeekend) { + // pokud je pondělí a chceme přeskočit víkend + if (jidloIndex + 2 <= data.length - 1) { + jidloIndex += 2; + } else { + jidloIndex = 0; + } + } else if (jidloIndex == data.length) { + jidloIndex = 0; + } else { + jidloIndex += 1; + } + nactiJidlo(); + }); + }, + icon: const Icon(Icons.arrow_right), + ), + IconButton( + onPressed: () { + jidloIndex = 0; + }, + icon: const Icon(Icons.today)) + ]), SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Column( diff --git a/metadata/cs-CZ/changelogs/23.txt b/metadata/cs-CZ/changelogs/23.txt new file mode 100644 index 0000000..e68aad5 --- /dev/null +++ b/metadata/cs-CZ/changelogs/23.txt @@ -0,0 +1,3 @@ +- Umožnit ukládat více dnů offline +- Pop-up "Přihlašování" nyní zmizí v případě neúspěšného přihlášení +- chyba při ukládání offline vás nyní již nevyhodí ale zobrazí pouze zprávu \ No newline at end of file diff --git a/metadata/en-US/changelogs/23.txt b/metadata/en-US/changelogs/23.txt new file mode 100644 index 0000000..395a084 --- /dev/null +++ b/metadata/en-US/changelogs/23.txt @@ -0,0 +1,3 @@ +- Support downloading multiple days of meal lists offline +- "Signing in" pop-up disappears when not signed in succesfully +- Error while saving menu offline no longer throws you to the log-in screen, but shows just an error message \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index ba796e1..cd456bf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ publish_to: 'none' # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. -version: 1.4.2+22 +version: 1.5.0+23 environment: sdk: ">=2.16.1 <3.0.0"