feat: umožnit ukládat více dnů offline

This commit is contained in:
Matyáš Caras 2022-11-21 20:26:55 +01:00
parent db84284eca
commit c913e3bb95
11 changed files with 226 additions and 97 deletions

View file

@ -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

View file

@ -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;

View file

@ -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.";
}

View file

@ -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.";
}

View file

@ -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<LoginPage> {
}
} 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<LoginPage> {
);
} 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<LoginPage> {
/// 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<OfflineJidlo> 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);
}
}

View file

@ -382,44 +382,52 @@ class _JidelnicekPageState extends State<JidelnicekPage> {
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<JidelnicekPage> {
void didChangeDependencies() {
super.didChangeDependencies();
nactiNastaveni();
ulozitDnesekOffline();
ulozitDoOffline();
nactiJidlo();
}

View file

@ -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<Nastaveni> {
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<Nastaveni> {
}
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<Nastaveni> {
})
],
),
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: [

View file

@ -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<OfflineJidlo> jidla;
const OfflineJidelnicek({Key? key}) : super(key: key);
@override
State<OfflineJidelnicek> createState() => _OfflineJidelnicekState();
}
class _OfflineJidelnicekState extends State<OfflineJidelnicek> {
List<Widget> obsah = [const CircularProgressIndicator()];
var _skipWeekend = false;
DateTime den = DateTime.now();
String denTydne = "";
List<List<OfflineJidlo>> 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<OfflineJidlo> 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<void> 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<OfflineJidelnicek> {
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}"),
Checkbox(
value: j.objednano,
fillColor: MaterialStateProperty.all(Colors.grey),
onChanged: (v) async {
return;
})
],
)),
Text((j.naBurze)
? Languages.of(context)!.inExchange
: "${j.cena}"),
Checkbox(
value: j.objednano,
fillColor: MaterialStateProperty.all(Colors.grey),
onChanged: (v) async {
return;
})
],
),
),
),
);
}
@ -116,7 +147,14 @@ class _OfflineJidelnicekState extends State<OfflineJidelnicek> {
@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<OfflineJidelnicek> {
),
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(

View file

@ -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

View file

@ -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

View file

@ -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"