Připravit první release

This commit is contained in:
Matyáš Caras 2022-04-05 19:48:14 +02:00
parent d0aa394009
commit bf7dc7a165
13 changed files with 423 additions and 118 deletions

20
PRIVACY.md Normal file
View file

@ -0,0 +1,20 @@
## Zásady používání a ochrany soukromí aplikace OpenCanteen
Platí od 5. 4. 2022
### Používání
- Tato aplikace není vyvíjena nebo podporována firmou Z-WARE s.r.o. a není oficiální klient pro systém iCanteen
- Uživatel aplikace sám zodpovídá za svůj účet a údaje
- Vývojář aplikace neručí za stabilitu aplikace ani za vzniklé škody, které mohly vzniknout používáním aplikace
- Použití aplikace se řídí případnými podmínkami k používání systému iCanteen
## Ochrana soukromí
- Aplikace neobsahuje žádné analytické nebo sledovací systémy
- Aplikace odesílá data pouze na URL instance systému iCanteen, kterou uživatel aplikace sám zadá
- V případě, že tato instance není zabezpečena protokolem HTTPS, neručí vývojář aplikace za vzniklá nebezpečí
- Aplikace neobsahuje reklamy
- Aplikace dlouhodobě ukládá údaje uživatele (přihlašovací jméno, heslo, URL k instanci) pouze v případě, že uživatel při přihlašování povolí možnost `Zapamatovat si mě`. V tom případě jsou data zašifrována a uložena na zařízení uživatele
- V opačném případě jsou údaje uložené v mezipaměti jen na dobu nezbytně nutnou a následně smazány operačním systémem
### Kontakt
Preferovaná forma komunikace je skrze Diskuze nebo Issues.
V případech nejvyšší nutnosti je můj e-mail `contact zavináč hernikplays.cz`

View file

@ -12,7 +12,8 @@
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues

View file

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/loginmanager.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
@ -16,6 +17,9 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
localizationsDelegates: GlobalMaterialLocalizations.delegates,
supportedLocales: const [Locale("cs")],
title: 'OpenCanteen',
theme: ThemeData(
primarySwatch: Colors.purple,
@ -58,6 +62,18 @@ class _LoginPageState extends State<LoginPage> {
}
if (r != null) {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => Dialog(
child: SizedBox(
height: 100,
child: Row(children: const [
CircularProgressIndicator(),
Text("Přihlašuji vás")
]),
),
));
var canteen = Canteen(r["url"]!);
var l = await canteen.login(r["user"]!, r["pass"]!);
if (!l) {
@ -88,7 +104,7 @@ class _LoginPageState extends State<LoginPage> {
title: const Text("Přihlášení"),
),
body: Center(
child: Container(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@ -120,13 +136,16 @@ class _LoginPageState extends State<LoginPage> {
keyboardType: TextInputType.url,
controller: canteenControl,
),
Switch(
value: rememberMe,
onChanged: (value) {
setState(() {
rememberMe = value;
});
}),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Switch(
value: rememberMe,
onChanged: (value) {
setState(() {
rememberMe = value;
});
}),
const Text("Zapamatovat si mě")
]),
TextButton(
onPressed: () async {
if (canteenControl.text.contains("http://")) {

106
lib/okna/burza.dart Normal file
View file

@ -0,0 +1,106 @@
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart';
import 'package:opencanteen/util.dart';
import '../main.dart';
class BurzaPage extends StatefulWidget {
const BurzaPage({Key? key, required this.canteen, required this.user})
: super(key: key);
final Canteen canteen;
final String user;
@override
State<BurzaPage> createState() => _BurzaPageState();
}
class _BurzaPageState extends State<BurzaPage> {
List<Widget> obsah = [];
double kredit = 0.0;
Future<void> nactiBurzu() async {
obsah = [const CircularProgressIndicator()];
widget.canteen.ziskejUzivatele().then((kr) {
kredit = kr.kredit;
widget.canteen.ziskatBurzu().then((burza) {
setState(() {
obsah = [];
if (burza.isEmpty) {
obsah = [
const Text(
"Žádné jídlo v burze.",
style: TextStyle(fontSize: 20),
),
const Text("Potáhněte zvrchu pro načtení.")
];
} else {
for (var b in burza) {
obsah.add(
Padding(
padding: const EdgeInsets.only(top: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("${b.den.day}. ${b.den.month}."),
const SizedBox(width: 10),
Flexible(
child: Text(
b.nazev,
),
),
Text("${b.pocet}x"),
TextButton(
onPressed: () {
widget.canteen.objednatZBurzy(b).then((_) {
nactiBurzu();
});
},
child: const Text("Objednat")),
],
),
),
);
}
}
});
});
}).catchError((o) {
if (!widget.canteen.prihlasen) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (c) => const LoginPage()));
}
});
}
@override
void initState() {
super.initState();
nactiBurzu();
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: drawerGenerator(context, widget.canteen, widget.user, 3),
appBar: AppBar(
title: const Text('Burza'),
),
body: RefreshIndicator(
child: Center(
child: Column(
children: [
const SizedBox(height: 10),
Text("Kredit: $kredit"),
const SizedBox(height: 10),
SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: SizedBox(
height: MediaQuery.of(context).size.height - 120,
child: Column(children: obsah),
),
)
],
),
),
onRefresh: () => nactiBurzu()),
);
}
}

View file

@ -34,6 +34,7 @@ class _HomePageState extends State<HomePage> {
Padding(
padding: const EdgeInsets.only(top: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(j.varianta),
const SizedBox(width: 10),

View file

@ -1,7 +1,6 @@
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart';
import 'package:opencanteen/util.dart';
import 'package:flutter_holo_date_picker/flutter_holo_date_picker.dart';
import '../main.dart';
@ -15,60 +14,12 @@ class JidelnicekPage extends StatefulWidget {
}
class _JidelnicekPageState extends State<JidelnicekPage> {
List<Widget> obsah = [];
List<Widget> obsah = [const Text("Načítám...")];
DateTime den = DateTime.now();
String denTydne = "";
double kredit = 0.0;
Future<void> nactiJidlo() async {
obsah = [];
widget.canteen.ziskejUzivatele().then((kr) {
kredit = kr.kredit;
widget.canteen.jidelnicekDen(den: den).then((jd) {
setState(() {
for (var j in jd.jidla) {
obsah.add(
Padding(
padding: const EdgeInsets.only(top: 15),
child: Row(
children: [
Text(j.varianta),
const SizedBox(width: 10),
Flexible(
child: Text(
j.nazev,
),
),
Text("${j.cena}"),
Checkbox(
value: j.objednano,
fillColor: (j.lzeObjednat)
? MaterialStateProperty.all(Colors.blue)
: MaterialStateProperty.all(Colors.grey),
onChanged: (v) async {
if (!j.lzeObjednat) return;
widget.canteen
.objednat(j)
.then((value) => nactiJidlo);
})
],
),
),
);
}
});
});
}).catchError((o) {
if (!widget.canteen.prihlasen) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (c) => const LoginPage()));
}
});
}
@override
void initState() {
super.initState();
obsah = [const CircularProgressIndicator()];
switch (den.weekday) {
case 2:
denTydne = "Úterý";
@ -91,6 +42,136 @@ class _JidelnicekPageState extends State<JidelnicekPage> {
default:
denTydne = "Pondělí";
}
widget.canteen.ziskejUzivatele().then((kr) {
kredit = kr.kredit;
widget.canteen.jidelnicekDen(den: den).then((jd) {
setState(() {
obsah = [];
if (jd.jidla.isEmpty) {
obsah.add(const Text(
"Žádné jídlo pro tento den",
style: TextStyle(fontSize: 15),
));
} else {
for (var j in jd.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) ? "V BURZE" : "${j.cena}"),
Checkbox(
value: j.objednano,
fillColor: (j.lzeObjednat)
? MaterialStateProperty.all(Colors.blue)
: MaterialStateProperty.all(Colors.grey),
onChanged: (v) async {
return;
})
],
),
onTap: () async {
if (!j.lzeObjednat) return;
widget.canteen
.objednat(j)
.then((value) => nactiJidlo())
.catchError((o) {
showDialog(
context: context,
builder: (bc) => AlertDialog(
title: const Text(
"Nepodařilo se vložit jídlo na burzu"),
content: Text(o.toString()),
actions: [
TextButton(
child: const Text("Zavřít"),
onPressed: () {
Navigator.pop(bc);
},
)
],
));
});
},
onLongPress: () async {
if (!j.objednano) return;
if (!j.naBurze) {
// pokud není na burze, radši se zeptáme
var d = await showDialog(
context: context,
builder: (bc) => SimpleDialog(
title: const Text(
"Opravdu chcete vložit jídlo na burzu?"),
children: [
SimpleDialogOption(
onPressed: () {
Navigator.pop(bc, true);
},
child: const Text("Ano"),
),
SimpleDialogOption(
onPressed: () {
Navigator.pop(bc, false);
},
child: const Text("Ne"),
),
],
));
if (d) {
widget.canteen
.doBurzy(j)
.then((_) => nactiJidlo())
.catchError((o) {
showDialog(
context: context,
builder: (bc) => AlertDialog(
title: const Text(
"Nepodařilo se vložit jídlo na burzu"),
content: Text(o.toString()),
actions: [
TextButton(
child: const Text("Zavřít"),
onPressed: () {
Navigator.pop(bc);
},
)
],
));
});
}
} else {
// jinak ne
widget.canteen.doBurzy(j).then((_) => nactiJidlo());
}
},
),
),
);
}
}
});
});
}).catchError((o) {
if (!widget.canteen.prihlasen) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (c) => const LoginPage()));
}
});
}
@override
void initState() {
super.initState();
nactiJidlo();
}
@ -105,47 +186,60 @@ class _JidelnicekPageState extends State<JidelnicekPage> {
child: Column(
children: [
const SizedBox(height: 10),
Text("Kredit: $kredit"),
TextButton(
onPressed: () async {
var datePicked = await DatePicker.showSimpleDatePicker(
context,
initialDate: den,
dateFormat: "dd-MMMM-yyyy",
locale: DateTimePickerLocale.en_us,
looping: true,
);
if (datePicked == null) return;
setState(() {
den = datePicked;
switch (den.weekday) {
case 2:
denTydne = "Úterý";
break;
case 3:
denTydne = "Středa";
break;
case 4:
denTydne = "Čtvrtek";
break;
case 5:
denTydne = "Pátek";
break;
case 6:
denTydne = "Sobota";
break;
case 7:
denTydne = "Neděle";
break;
default:
denTydne = "Pondělí";
}
nactiJidlo();
});
Text("Kredit: $kredit"),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
IconButton(
onPressed: () {
setState(() {
den = den.subtract(const Duration(days: 1));
nactiJidlo();
});
},
icon: const Icon(Icons.arrow_left)),
TextButton(
onPressed: () async {
var datePicked = await showDatePicker(
context: context,
initialDate: den,
currentDate: den,
firstDate: DateTime(2019, 1, 1),
lastDate: DateTime(den.year + 1, 12, 31),
locale: const Locale("cs"));
if (datePicked == null) return;
setState(() {
den = datePicked;
nactiJidlo();
});
},
child: Text(
"${den.day}. ${den.month}. ${den.year} - $denTydne")),
IconButton(
onPressed: () {
setState(() {
den = den.add(const Duration(days: 1));
nactiJidlo();
});
},
icon: const Icon(Icons.arrow_right)),
]),
SingleChildScrollView(
child: GestureDetector(
child: Column(children: obsah),
onHorizontalDragEnd: (details) {
if (details.primaryVelocity?.compareTo(0) == -1) {
setState(() {
den = den.subtract(const Duration(days: 1));
nactiJidlo();
});
} else {
setState(() {
den = den.add(const Duration(days: 1));
nactiJidlo();
});
}
},
child:
Text("${den.day}. ${den.month}. ${den.year} - $denTydne")),
...obsah
),
)
],
),
),

View file

@ -1,5 +1,6 @@
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart';
import 'package:opencanteen/okna/burza.dart';
import 'okna/home.dart';
import 'okna/jidelnicek.dart';
@ -18,6 +19,7 @@ Drawer drawerGenerator(
),
ListTile(
selected: true,
title: const Text("Domů"),
leading: const Icon(Icons.home),
onTap: () => Navigator.pop(context),
),
@ -32,6 +34,16 @@ Drawer drawerGenerator(
),
),
),
ListTile(
leading: const Icon(Icons.store),
title: const Text('Burza'),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BurzaPage(canteen: canteen, user: user),
),
),
),
],
),
);
@ -46,6 +58,7 @@ Drawer drawerGenerator(
child: Text("OpenCanteen"),
),
ListTile(
title: const Text("Domů"),
leading: const Icon(Icons.home),
onTap: () => Navigator.push(
context,
@ -58,10 +71,55 @@ Drawer drawerGenerator(
title: const Text('Jídelníček'),
onTap: () => Navigator.pop(context),
),
ListTile(
leading: const Icon(Icons.store),
title: const Text('Burza'),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BurzaPage(canteen: canteen, user: user),
),
),
),
],
),
);
break;
case 3:
drawer = Drawer(
child: ListView(
children: [
const DrawerHeader(
child: Text("OpenCanteen"),
),
ListTile(
leading: const Icon(Icons.home),
title: const Text("Domů"),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (c) => HomePage(canteen: canteen, user: user))),
),
ListTile(
leading: const Icon(Icons.restaurant),
title: const Text('Jídelníček'),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
JidelnicekPage(canteen: canteen, user: user),
),
),
),
ListTile(
leading: const Icon(Icons.store),
selected: true,
title: const Text('Burza'),
onTap: () => Navigator.pop(context),
),
],
),
);
}
return drawer;
}

View file

@ -22,20 +22,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.2"
auto_size_text:
dependency: transitive
description:
name: auto_size_text
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
canteenlib:
dependency: "direct main"
description:
name: canteenlib
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0-alpha.7"
version: "0.1.0-alpha.13"
characters:
dependency: transitive
description:
@ -50,6 +43,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
@ -132,13 +132,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_holo_date_picker:
dependency: "direct main"
description:
name: flutter_holo_date_picker
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
flutter_launcher_icons:
dependency: "direct dev"
description:
@ -153,6 +146,11 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_secure_storage:
dependency: "direct main"
description:
@ -221,6 +219,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.3"
intl:
dependency: transitive
description:
name: intl
url: "https://pub.dartlang.org"
source: hosted
version: "0.17.0"
js:
dependency: transitive
description:

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.0.0+1
version: 0.1.0+1
environment:
sdk: ">=2.16.1 <3.0.0"
@ -14,11 +14,12 @@ environment:
dependencies:
flutter:
sdk: flutter
canteenlib: ^0.1.0-alpha.7
flutter_localizations:
sdk: flutter
canteenlib: ^0.1.0-alpha.13
cupertino_icons: ^1.0.2
connectivity_plus: ^2.2.1
flutter_secure_storage: ^5.0.2
flutter_holo_date_picker: ^1.0.3
dev_dependencies:
flutter_lints: ^1.0.0

BIN
screenshots/01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
screenshots/02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
screenshots/03.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
screenshots/04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB