Compare commits

...

35 Commits
1.6.0 ... main

Author SHA1 Message Date
Matyáš Caras cddeaafde7 feat: přidat zobrazení alergenů, opravit věci po upgradu závislostí (#52)
Reviewed-on: #52
2023-11-28 20:10:34 +01:00
Matyáš Caras 69074c7d28 ci: změnit RenovateBot base branch 2023-11-28 19:17:06 +01:00
Renovate Bot a6ab0525e1 fix(deps): update dependency flutter_local_notifications to v16 (#46)
Reviewed-on: #46
Co-authored-by: Renovate Bot <kontakt+renovator@caras.cafe>
Co-committed-by: Renovate Bot <kontakt+renovator@caras.cafe>
2023-11-28 19:12:58 +01:00
Renovate Bot 346ea5bb43 fix(deps): update dependency flutter_secure_storage to v9 (#45)
Reviewed-on: #45
Co-authored-by: Renovate Bot <kontakt+renovator@caras.cafe>
Co-committed-by: Renovate Bot <kontakt+renovator@caras.cafe>
2023-11-28 19:12:37 +01:00
Renovate Bot 855f4caf04 chore(deps): update dependency flutter_lints to v3 (#49)
Reviewed-on: #49
Co-authored-by: Renovate Bot <kontakt+renovator@caras.cafe>
Co-committed-by: Renovate Bot <kontakt+renovator@caras.cafe>
2023-11-28 19:12:19 +01:00
Renovate Bot 64cc85e195 fix(deps): update dependency package_info_plus to v5 (#50)
Reviewed-on: #50
Co-authored-by: Renovate Bot <kontakt+renovator@caras.cafe>
Co-committed-by: Renovate Bot <kontakt+renovator@caras.cafe>
2023-11-28 19:09:42 +01:00
Matyáš Caras 5b06e5708c
fix: opravit chybu s propisováním HTML 2023-10-06 13:28:13 +02:00
Matyáš Caras 1950d850b8
chore: bump 2023-09-12 22:40:17 +02:00
Matyáš Caras 3c918e7e41
docs: aktualizovat changelog 2023-09-12 22:39:57 +02:00
Matyáš Caras 21f69975e6 fix: opravit iOS vzhled + burzu (#41)
Reviewed-on: #41
2023-09-12 22:38:56 +02:00
Matyáš Caras 0b5967271a
fix: opravit nesoulad závislostí 2023-09-12 21:00:54 +02:00
Matyáš Caras 0232694142
chore: ⬆️ aktualizace dalších závislostí 2023-09-12 20:37:03 +02:00
Matyáš Caras 3423aa0368 Merge pull request 'chore(deps): update dependency flutter_launcher_icons to ^0.13.0' (#39) from renovate/flutter_launcher_icons-0.x into main
Reviewed-on: #39
2023-09-12 20:31:26 +02:00
Renovate Bot 7bfc8412d9 chore(deps): update dependency flutter_launcher_icons to ^0.13.0 2023-09-12 20:30:30 +02:00
Matyáš Caras 0f9de6080f Merge pull request 'fix(deps): update dependency intl to ^0.18.0' (#40) from renovate/intl-0.x into main
Reviewed-on: #40
2023-09-12 20:29:59 +02:00
Renovate Bot 757b94bbd5 fix(deps): update dependency intl to ^0.18.0 2023-09-12 20:21:55 +02:00
Matyáš Caras 9da89cfd2a Merge pull request 'Configure Renovate' (#38) from renovate/configure into main
Reviewed-on: #38
2023-09-12 20:08:58 +02:00
Matyáš Caras 7782226253 Update renovate.json 2023-09-12 20:08:51 +02:00
Renovate Bot f2d794e507 Add renovate.json 2023-09-12 20:05:33 +02:00
Matyáš Caras 9b25f3b0e7
feat(nastaveni): 💄 předělat nastavení 2023-09-04 20:45:45 +02:00
Matyáš Caras 963ad1de20
chore: odstranit nepotřebné soubory 2023-09-04 19:47:42 +02:00
Matyáš Caras 6de8b85765
docs: přidat info o změně na git 2023-09-04 19:28:18 +02:00
Matyáš Caras 55a8cadf35
chore(dependencies): ⬆️ update závislostí 2023-09-04 19:17:00 +02:00
Matyáš Caras b948959e67 feat: implementovat Material 3 a upravit systém lokalizací (#37)
Reviewed-on: #37
2023-01-28 15:59:41 +01:00
Matyáš Caras 2227bb59ce chore: aktualizovat changelogy 2023-01-28 15:57:48 +01:00
Matyáš Caras 6a3d0249ab chore: aktualizovat secure_storage 2023-01-28 15:54:21 +01:00
Matyáš Caras fb737dc40f fix: locale? 2023-01-28 15:44:06 +01:00
Matyáš Caras 17d14cb175 feat: předělat jazykový systém 2023-01-28 15:41:17 +01:00
Matyáš Caras 813c8cf3da feat: Material 3 2023-01-28 15:02:44 +01:00
Matyáš Caras 2615769679 Merge pull request 'fix: přepsat Platform UI a zmenit RefreshIndicator' (#36) from better-ui into main
Reviewed-on: #36
2023-01-28 14:42:11 +01:00
Matyáš Caras 64b7f261a0 fix: přepsat Platform UI a zmenit RefreshIndicator 2023-01-28 14:30:54 +01:00
Matyáš Caras c8d1878a39 chore: update Flutter 2023-01-28 14:30:09 +01:00
Matyáš Caras 26c6e4604e fix: vratit UI zmeny 2022-12-14 20:02:32 +01:00
Matyáš Caras 44130bdc09 Merge pull request 'fix: opravit problém s burzou' (#35) from fix-burza into main
Reviewed-on: #35
2022-12-14 18:50:23 +00:00
Matyáš Caras 33ff600bbe fix: opravit problém s burzou 2022-12-14 19:44:32 +01:00
62 changed files with 2579 additions and 6069 deletions

View File

@ -1,5 +0,0 @@
{
"name": "Flutter",
"image": "matspfeiffer/flutter:beta",
"extensions": ["dart-code.dart-code", "dart-code.flutter"]
}

@ -1 +1 @@
Subproject commit 52b3dc25f6471c27b2144594abb11c741cb88f57
Subproject commit 7f20e5d18ce4cb80c621533090a7c5113f5bdc52

13
.github/FUNDING.yml vendored
View File

@ -1,13 +0,0 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: hernikplays
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -1,28 +0,0 @@
---
name: Chybové hlášení
about: Použijte tuto předlohu, pokud se něco rozbilo
title: ''
labels: bug
assignees: hernikplays
---
**Popis chyby**
Zde popište co se stalo, mělo stát apod.
**Kroky pro replikaci**
Kroky ke spuštění chyby:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Snímky obrazovky**
Chcete-li, přiložte snímky
**Info o zařízení**
- Verze systému zařízení: [např. Android 12, iOS 9]
- Verze aplikace [např. 0.1.0]
**Ostatní**
Sem vepište doplňující informace

View File

@ -1,11 +0,0 @@
---
name: Nápad na vylepšení
about: Navrhněte co změnit nebo přidat do aplikace
title: ''
labels: vylepšení
assignees: hernikplays
---
**Souvisí váš navrh s nějakým problémem?**
Popište váš problém a co čekáte za řešení.

8
.github/labeler.yml vendored
View File

@ -1,8 +0,0 @@
'pr: pubspec':
- pubspec.yaml
'pr: funkce':
- lib/*
'pr: okno':
- lib/okna/*

View File

@ -1,14 +0,0 @@
Opravuje/implementuje # .
Změny:
- 1
- 2
```
Text výše slouží jen jako předloha, není nutné se ho držet, ale šetříte ostatním čas
```
- [ ] Otestoval jsem funkčnost celé aplikace po přidání svého kódu
- [ ] Přidaný kód nebo knihovny neobsahují žádný kód pod nekompatibilní licencí
Doplňující/ostatní informace:

View File

@ -1,30 +0,0 @@
# This is a basic workflow to help you get started with Actions
name: Analyze
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ main ]
pull_request:
branches: [ main ]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
fossa-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: fossas/fossa-action@main
with:
api-key: ${{secrets.fossaApiKey}}
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- run: flutter pub get
- run: flutter analyze

View File

@ -1,33 +0,0 @@
name: "Lint PR"
on:
pull_request_target:
types:
- opened
- edited
- synchronize
jobs:
main:
name: Validate PR
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: amannn/action-semantic-pull-request@v4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: wagoid/commitlint-github-action@v4
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
branch:
name: Uzavřít PRy mimo dev větev
needs: main
if: github.base_ref == 'main' && github.actor != 'hernikplays'
runs-on: ubuntu-latest
steps:
- uses: superbrothers/close-pull-request@v3
with:
comment: "Všechny pull requesty by měly směřovat na jinou větev, než `main`. Pokud je tohle chyba, napište správci repozitáře."

View File

@ -1,3 +1,7 @@
{
"dart.flutterSdkPath": ".flutter",
"conventionalCommits.scopes": [
"dependencies",
"nastaveni"
],
}

View File

@ -1,3 +1,27 @@
# 1.10.0
- Aktualizovat závislosti
- Změnit minSdk na 21
- Přidat možnost zobrazit alergeny
- Změnit fungování oznámení s aktualizací knihovny
- Žádost o oprávnění k posílání oznámení by se mělo posílat už jen v případě, že uživatel bude chtít používat funkci oznámení
# 1.9.1
- Opravit chybu s propisováním HTML do názvu obědů
# 1.9.0
- Opravit vzhled na iOS
- Opravit chybu s burzou (update canteenlib)
# 1.8.1
- aktualizace závislostí
# 1.8.0
- aktualizace závislostí
- předělání nastavení
- změna nastavení nyní nevyžaduje restart aplikace
# 1.7.0
- Implementovat Material 3 (Android)
- Upravit chování dle platformy
- Předělat jazykový systém na ARB
- Aktualizovat flutter_secure_storage
# 1.6.1
- opravit chybu s přidáváním do burzy aktualizací knihovny
# 1.6.0
- rozdělit iOS a Android UI zvlášť pro možnost využití Cupertino knihovny
- opravit chybu s FlutterLocalNotifications na iOS
@ -64,4 +88,4 @@
- Dialogové okno k burze se nezobrazí u jídel, která nelze přidat do burzy
- Přidán načítací dialog při objednání (#6)
# 0.1.0
- První verze
- První verze

View File

@ -2,6 +2,16 @@
OpenCanteen je **aplikace** pro přístup do iCanteen. Pokud chcete přispět kód související s komunikací s iCanteen, podívejte se na [canteenlib](https://github.com/hernikplays/canteenlib).
## Jak přispět do vývoje
### Používám GitHub pod protestem
Tento projekt je aktuálně hostován i na GitHubu. To není ideální; GitHub je uzavřený systém plný obchodních tajemství. Doporučuji přečíst si o kampani
[Give up GitHub](https://GiveUpGitHub.org) od
[Software Freedom Conservancy](https://sfconservancy.org) pro pochopení proč GitHub není ideální místo pro FOSS projekty.
**Příspěvky do kódu přijímám **pouze** skrz mou Forgejo instanci na adrese https://git.mnau.xyz/hernik/opencanteen**
Jakékoli použití kódu pro učení umělých inteligencí, které neumožňuje samotná licence GNU GPL 3.0 či novější, je děláno bez mého souhlasu a vědomí.
### Nahlašování chyb
Prosté vyhledání a nahlášení chyby je asi nejjednodušší a zároveň nejpřínosnější způsob přispívání. Stačí vám běžné zařízení a stažená aplikace, pokud objevíte jakoukoliv chybu nebo nesrovnalost, nahlaste ji v [Issues](https://github.com/hernikplays/opencanteen/issues/new/choose).

View File

@ -29,11 +29,20 @@ Jednoduchý návod k použití naleznete [zde](https://github.com/hernikplays/op
## Přispět
Přispět na vývoj můžete skrz
- Liberapay: https://liberapay.com/hernikplays
- Monero: `49uzFSxAsT92sXUsCEgh11VX7pmNZcoeKi4fEWbLGc2oWGMUVNcDDqGVB97ak96LqEBqsMXLS752bjgyVzbFcVwLNke4pNd`
- Bitcoin: `3NzqkBZgWgjj1NYj2JwmBBptPWw6wt7dHS`
- Kód: viz CONTRIBUTING
## Používám GitHub pod protestem
Tento projekt je aktuálně hostován i na GitHubu. To není ideální; GitHub je uzavřený systém plný obchodních tajemství. Doporučuji přečíst si o kampani
[Give up GitHub](https://GiveUpGitHub.org) od
[Software Freedom Conservancy](https://sfconservancy.org) pro pochopení proč GitHub není ideální místo pro FOSS projekty.
Příspěvky do kódu přijímám **pouze** skrz mou Forgejo instanci na adrese https://git.mnau.xyz/hernik/opencanteen
Jakékoli použití kódu pro učení umělých inteligencí, které neumožňuje samotná licence GNU GPL 3.0 či novější, je děláno bez mého souhlasu a vědomí.
## Licence
**Copyright (c) 2022 Matyáš Caras a přispěvatelé**

View File

@ -51,7 +51,7 @@ android {
defaultConfig {
applicationId "cz.hernikplays.opencanteen"
minSdkVersion 18
minSdkVersion 21
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cz.hernikplays.opencanteen">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<application
android:label="OpenCanteen"
android:name="${applicationName}"

View File

@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.9.10'
repositories {
google()
mavenCentral()
@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

@ -1 +0,0 @@
module.exports = {extends: ['@commitlint/config-conventional']}

View File

@ -1,58 +1,79 @@
PODS:
- Flutter (1.0.0)
- flutter_keyboard_visibility (0.0.1):
- Flutter
- flutter_local_notifications (0.0.1):
- Flutter
- flutter_native_timezone (0.0.1):
- Flutter
- flutter_secure_storage (3.3.1):
- flutter_secure_storage (6.0.0):
- Flutter
- fluttertoast (0.0.2):
- Flutter
- Toast
- package_info_plus (0.4.5):
- Flutter
- path_provider_ios (0.0.1):
- path_provider_foundation (0.0.1):
- Flutter
- shared_preferences_ios (0.0.1):
- FlutterMacOS
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- Toast (4.0.0)
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_native_timezone (from `.symlinks/plugins/flutter_native_timezone/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS:
trunk:
- Toast
EXTERNAL SOURCES:
Flutter:
:path: Flutter
flutter_keyboard_visibility:
:path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios"
flutter_native_timezone:
:path: ".symlinks/plugins/flutter_native_timezone/ios"
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios"
shared_preferences_ios:
:path: ".symlinks/plugins/shared_preferences_ios/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
flutter_native_timezone: 5f05b2de06c9776b4cc70e1839f03de178394d22
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
COCOAPODS: 1.11.3
COCOAPODS: 1.12.1

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@ -156,7 +156,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@ -222,10 +222,12 @@
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
@ -236,6 +238,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -4,7 +4,7 @@
<dict>
<key>CFBundleLocalizations</key>
<array>
<string>Czech</string>
<string>cs</string>
<string>en</string>
</array>
<key>CFBundleDevelopmentRegion</key>
@ -50,5 +50,7 @@
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

3
l10n.yaml Normal file
View File

@ -0,0 +1,3 @@
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

69
lib/color_schemes.g.dart Normal file
View File

@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
const lightColorScheme = ColorScheme(
brightness: Brightness.light,
primary: Color(0xFF5F52A7),
onPrimary: Color(0xFFFFFFFF),
primaryContainer: Color(0xFFE5DEFF),
onPrimaryContainer: Color(0xFF1A0261),
secondary: Color(0xFF5F5C71),
onSecondary: Color(0xFFFFFFFF),
secondaryContainer: Color(0xFFE5DFF9),
onSecondaryContainer: Color(0xFF1C192B),
tertiary: Color(0xFF763EC3),
onTertiary: Color(0xFFFFFFFF),
tertiaryContainer: Color(0xFFEDDCFF),
onTertiaryContainer: Color(0xFF290056),
error: Color(0xFFBA1A1A),
errorContainer: Color(0xFFFFDAD6),
onError: Color(0xFFFFFFFF),
onErrorContainer: Color(0xFF410002),
background: Color(0xFFFFFBFF),
onBackground: Color(0xFF1C1B1F),
surface: Color(0xFFFFFBFF),
onSurface: Color(0xFF1C1B1F),
surfaceVariant: Color(0xFFE5E0EC),
onSurfaceVariant: Color(0xFF48454E),
outline: Color(0xFF79767F),
onInverseSurface: Color(0xFFF4EFF4),
inverseSurface: Color(0xFF313033),
inversePrimary: Color(0xFFC8BFFF),
shadow: Color(0xFF000000),
surfaceTint: Color(0xFF5F52A7),
outlineVariant: Color(0xFFC9C5D0),
scrim: Color(0xFF000000),
);
const darkColorScheme = ColorScheme(
brightness: Brightness.dark,
primary: Color(0xFFC8BFFF),
onPrimary: Color(0xFF302175),
primaryContainer: Color(0xFF473A8D),
onPrimaryContainer: Color(0xFFE5DEFF),
secondary: Color(0xFFC9C3DC),
onSecondary: Color(0xFF312E41),
secondaryContainer: Color(0xFF484459),
onSecondaryContainer: Color(0xFFE5DFF9),
tertiary: Color(0xFFD7BAFF),
onTertiary: Color(0xFF440088),
tertiaryContainer: Color(0xFF5D20A9),
onTertiaryContainer: Color(0xFFEDDCFF),
error: Color(0xFFFFB4AB),
errorContainer: Color(0xFF93000A),
onError: Color(0xFF690005),
onErrorContainer: Color(0xFFFFDAD6),
background: Color(0xFF1C1B1F),
onBackground: Color(0xFFE5E1E6),
surface: Color(0xFF1C1B1F),
onSurface: Color(0xFFE5E1E6),
surfaceVariant: Color(0xFF48454E),
onSurfaceVariant: Color(0xFFC9C5D0),
outline: Color(0xFF938F99),
onInverseSurface: Color(0xFF1C1B1F),
inverseSurface: Color(0xFFE5E1E6),
inversePrimary: Color(0xFF5F52A7),
shadow: Color(0xFF000000),
surfaceTint: Color(0xFFC8BFFF),
outlineVariant: Color(0xFF48454E),
scrim: Color(0xFF000000),
);

83
lib/l10n/app_cs.arb Normal file
View File

@ -0,0 +1,83 @@
{
"about": "O Aplikaci",
"agree": "Souhlasím",
"appName": "OpenCanteen",
"balance": "Kredit: ",
"cannotOrder": "Toto jídlo není možné objednat.",
"close": "Zavřít",
"copyright": "© 2022 Matyáš Caras a přispěvatelé",
"disagree": "Nesouhlasím",
"errorContacting": "Nastala chyba při kontaktování serveru, zkontrolujte připojení",
"errorOrdering": "Jídlo se nepodařilo objednat",
"exchange": "Burza",
"exchangeError": "Nepodařilo se vložit jídlo na burzu",
"friday": "Pátek",
"home": "Domů",
"httpLogin": "Snažíte se přihlásit přes nešifrované spojení HTTP, jste si jisti, že tak chcete učinit?",
"iCanteenUrl": "iCanteen URL",
"inExchange": "V BURZE",
"license": "Vydáno pod licencí GNU GPLv3",
"loading": "Načítání...",
"logIn": "Přihlášení",
"loggingIn": "Přihlašuji vás...",
"loginFailed": "Přihlášení se nezdařilo",
"menu": "Jídelníček",
"monday": "Pondělí",
"mustLogout": "Online přejdete přetažením dolů.",
"no": "Ne",
"noChange": "Ne, změnit",
"noExchange": "Žádné jídlo v burze",
"noFood": "Žádné jídlo pro tento den",
"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. Pokračováním souhlasíte.",
"offline": "JSTE OFFLINE",
"ok": "OK",
"order": "Objednat",
"orderSuccess": "Jídlo bylo úspěšně objednáno",
"ordered": "Objednáno",
"ordering": "Objednávám...",
"password": "Heslo",
"pullToReload": "Potáhněte zvrchu pro načtení",
"rememberMe": "Zapamatovat si mě",
"reportBugs": "Zpětná vazba",
"saturday": "Sobota",
"saveOffline": "Ukládat jídelníček offline",
"settings": "Nastavení",
"signOut": "Odhlásit se",
"skipWeekend": "Při procházení menu přeskočit víkend",
"sunday": "Neděle",
"thursday": "Čtvrtek",
"tuesday": "Úterý",
"username": "Uživatelské jméno",
"verifyExchange": "Opravdu chcete vložit jídlo na burzu?",
"warning": "Pozor!",
"wednesday": "Středa",
"yes": "Ano",
"aboutFromExch": "Žádné jídlo? Žádný problém!",
"aboutOrder": "Klepnutím objednáte",
"aboutToExch": "Nemáte chuť?",
"appDesc": "OpenCanteen je neoficiální aplikace pro přístup do obědového systému iCanteen",
"howFromExch": "Z vysunovacího menu přejděte do burzy a objednejte si z dostupných jídel",
"howOrder": "Jednoduše klepněte na jídlo s modrým políčkem a máte objednáno",
"howToExch": "Stačí dlouze podržet jméno objednaného jídla a můžete ho přesunout na nebo z burzy",
"welcome": "Vítejte v OpenCanteen",
"next": "Další",
"checkOrdered": "Kontrolovat, jestli mám objednáno na příští týden",
"noOrder": "Na přístí týden nemáte objednané žádné jídlo!",
"corrupted": "Nastal problém s dešifrováním uložených údajů, prosím zkuste vyčistit veškerá data této aplikace.",
"notifyAt": "Odeslat v",
"notifyLunch": "V určený čas odeslat notifikaci s informacemi o obědě",
"lunchNotif": "Dnes máte objednáno",
"error": "Chyba",
"needRemember": "Musíte své přihlašovací údaje uložit na přihlašovací obrazovce",
"notifyWarning": "Vaše zařízení může mít povolenou optimalizaci baterie, což může způsobovat neodesílání oznámení. Zkontrolujte nastavení ve vašem zařízení.",
"signOutWarn": "Opravdu se chcete odhlásit?",
"jump": "Přeskočit",
"source": "Zdrojový kód",
"review": "Ohodnotit aplikaci",
"saveCount": "Počet dnů dostupných offline (Akt. limit je 7)",
"errorSaving": "Při ukládání offline nastala chyba, zkuste to znovu později.",
"todayTooltip": "Přejít na dnešní jídelníček",
"settingsExperience":"Zážitek",
"settingsFunctions":"Extra funkce",
"showAllergens":"Zobrazovat alergeny, jsou-li dostupné"
}

83
lib/l10n/app_en.arb Normal file
View File

@ -0,0 +1,83 @@
{
"about": "About",
"agree": "I agree",
"appName": "OpenCanteen",
"balance": "Balance: ",
"cannotOrder": "This food cannot be ordered.",
"close": "Close",
"copyright": "© 2022 Matyáš Caras and contributors",
"disagree": "I disagree",
"errorContacting": "Failed to contact the server, check your connection.",
"errorOrdering": "Could not order food.",
"exchange": "Exchange",
"exchangeError": "Could not put food on exchange.",
"friday": "Friday",
"home": "Home",
"httpLogin": "You are trying to sign in using an insecure HTTP connection, are you sure you want to continue?",
"iCanteenUrl": "iCanteen URL",
"inExchange": "ON EXCHANGE",
"license": "Released under the GNU GPLv3",
"loading": "Loading...",
"logIn": "Sign in",
"loggingIn": "Signing you in...",
"loginFailed": "Sign in failed",
"menu": "Food Menu",
"monday": "Monday",
"mustLogout": "To go online, pull down.",
"no": "No",
"noChange": "No, change",
"noExchange": "No meal in exchange",
"noFood": "No meal for this day",
"notOfficial": "This is not an official app for accessing iCanteen. The author is not responsible for non-functionality or losses while using this app. By continuing you agree.",
"offline": "YOU ARE OFFLINE",
"ok": "OK",
"order": "Order",
"orderSuccess": "Meal ordered succesfully",
"ordered": "Ordered",
"ordering": "Ordering...",
"password": "Password",
"pullToReload": "Pull to reload",
"rememberMe": "Remember me",
"reportBugs": "Feedback",
"saturday": "Saturday",
"saveOffline": "Save menu offline",
"settings": "Settings",
"signOut": "Sign out",
"skipWeekend": "Skip weekends when browsing menu",
"sunday": "Sunday",
"thursday": "Thursday",
"tuesday": "Tuesday",
"username": "Username",
"verifyExchange": "Are you sure you want to put this meal on exchange?",
"warning": "Warning!",
"wednesday": "Wednesday",
"yes": "Yes",
"appDesc": "OpenCanteen is a mobile app for accessing iCanteen.",
"welcome": "Welcome to OpenCanteen",
"aboutOrder": "Order with a tap",
"howOrder": "Simply tap on a meal with a blue checkbox next to it and it's done!",
"aboutToExch": "Don't want your food?",
"howToExch": "If you cannot cancel your order, simply long-tap on the ordered food and put it into the exchange.",
"aboutFromExch": "No food? No problem!",
"howFromExch": "Simply check the exchange from the sidebar and order when a meal is available.",
"next": "Next",
"checkOrdered": "Check if I have ordered food for the next week",
"noOrder": "You did not order any food for the next week!",
"corrupted": "The saved credentials seem to be corrupted, please try clearing the application's data.",
"notifyAt": "Send notification at",
"notifyLunch": "Send a notification with meal info",
"lunchNotif": "Today's ordered meal",
"error": "Error",
"needRemember": "You need to save your login details on the login screen first",
"notifyWarning": "Your device may have battery optimization enabled. This may cause notifications to not be sent. Check the application info in your device's settings.",
"signOutWarn": "Do you really want to sign out?",
"jump": "Jump",
"source": "Source code",
"review": "Review the app",
"saveCount": "Number of days to save offline (Current limit is 7)",
"errorSaving": "An error occured while trying to save menu offline, try again later.",
"todayTooltip": "Go to today's meal",
"settingsExperience":"App Experience",
"settingsFunctions":"Extra Functions",
"showAllergens":"Show allergens, if available"
}

View File

@ -1,175 +0,0 @@
import 'package:flutter/material.dart';
abstract class Languages {
static Languages? of(BuildContext context) {
return Localizations.of<Languages>(context, Languages);
}
String get appName;
String get home;
// Login
String get errorContacting;
String get loggingIn;
String get logIn;
String get username;
String get password;
String get iCanteenUrl;
String get rememberMe;
String get httpLogin;
String get yes;
String get noChange;
String get notOfficial;
String get agree;
String get disagree;
String get loginFailed;
String get warning;
String get corrupted;
// Jídelníček
String get loading;
String get monday;
String get tuesday;
String get wednesday;
String get thursday;
String get friday;
String get saturday;
String get sunday;
String get noFood;
String get inExchange;
String get ordering;
String get errorOrdering;
String get close;
String get verifyExchange;
String get no;
String get exchangeError;
String get signOut;
String get reportBugs;
String get review;
String get about;
String get menu;
String get balance;
String get noOrder;
String get signOutWarn;
String get jump;
// Uvítací obrazovka
String get welcome;
String get appDesc;
String get aboutOrder;
String get howOrder;
String get aboutToExch;
String get howToExch;
String get aboutFromExch;
String get howFromExch;
String get next;
// Burza
String get exchange;
String get noExchange;
String get pullToReload;
String get ordered;
String get orderSuccess;
String get ok;
String get cannotOrder;
String get order;
// About
String get license;
String get copyright;
String get source;
// Nastavení
String get settings;
String get saveOffline;
String get saveCount;
String get skipWeekend;
String get checkOrdered;
String get notifyLunch;
String get notifyAt;
String get notifyWarning;
// Offline
String get offline;
String get mustLogout;
String get errorSaving;
// Oznámit před obědem
String get lunchNotif;
String get error;
String get needRemember;
}

View File

@ -1,247 +0,0 @@
import 'package:opencanteen/lang/lang.dart';
class LanguageCz extends Languages {
@override
String get about => "O Aplikaci";
@override
String get agree => "Souhlasím";
@override
String get appName => "OpenCanteen";
@override
String get balance => "Kredit: ";
@override
String get cannotOrder => "Toto jídlo není možné objednat.";
@override
String get close => "Zavřít";
@override
String get copyright => "© 2022 Matyáš Caras a přispěvatelé";
@override
String get disagree => "Nesouhlasím";
@override
String get errorContacting =>
"Nastala chyba při kontaktování serveru, zkontrolujte připojení";
@override
String get errorOrdering => "Jídlo se nepodařilo objednat";
@override
String get exchange => "Burza";
@override
String get exchangeError => "Nepodařilo se vložit jídlo na burzu";
@override
String get friday => "Pátek";
@override
String get home => "Domů";
@override
String get httpLogin =>
"Snažíte se přihlásit přes nešifrované spojení HTTP, jste si jisti, že tak chcete učinit?";
@override
String get iCanteenUrl => "iCanteen URL";
@override
String get inExchange => "V BURZE";
@override
String get license => "Vydáno pod licencí GNU GPLv3";
@override
String get loading => "Načítání...";
@override
String get logIn => "Přihlášení";
@override
String get loggingIn => "Přihlašuji vás...";
@override
String get loginFailed => "Přihlášení se nezdařilo";
@override
String get menu => "Jídelníček";
@override
String get monday => "Pondělí";
@override
String get mustLogout => "Online přejdete přetažením dolů.";
@override
String get no => "Ne";
@override
String get noChange => "Ne, změnit";
@override
String get noExchange => "Žádné jídlo v burze";
@override
String get noFood => "Žádné jídlo pro tento den";
@override
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. Pokračováním souhlasíte.";
@override
String get offline => "JSTE OFFLINE";
@override
String get ok => "OK";
@override
String get order => "Objednat";
@override
String get orderSuccess => "Jídlo bylo úspěšně objednáno";
@override
String get ordered => "Objednáno";
@override
String get ordering => "Objednávám...";
@override
String get password => "Heslo";
@override
String get pullToReload => "Potáhněte zvrchu pro načtení";
@override
String get rememberMe => "Zapamatovat si mě";
@override
String get reportBugs => "Zpětná vazba";
@override
String get saturday => "Sobota";
@override
String get saveOffline => "Ukládat jídelníček na dnešní den offline";
@override
String get settings => "Nastavení";
@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";
@override
String get thursday => "Čtvrtek";
@override
String get tuesday => "Úterý";
@override
String get username => "Uživatelské jméno";
@override
String get verifyExchange => "Opravdu chcete vložit jídlo na burzu?";
@override
String get warning => "Pozor!";
@override
String get wednesday => "Středa";
@override
String get yes => "Ano";
@override
String get aboutFromExch => "Žádné jídlo? Žádný problém!";
@override
String get aboutOrder => "Klepnutím objednáte";
@override
String get aboutToExch => "Nemáte chuť?";
@override
String get appDesc =>
"OpenCanteen je neoficiální aplikace pro přístup do obědového systému iCanteen";
@override
String get howFromExch =>
"Z vysunovacího menu přejděte do burzy a objednejte si z dostupných jídel";
@override
String get howOrder =>
"Jednoduše klepněte na jídlo s modrým políčkem a máte objednáno";
@override
String get howToExch =>
"Stačí dlouze podržet jméno objednaného jídla a můžete ho přesunout na nebo z burzy";
@override
String get welcome => "Vítejte v OpenCanteen";
@override
String get next => "Další";
@override
String get checkOrdered =>
"Kontrolovat, jestli mám objednáno na příští týden";
@override
String get noOrder => "Na přístí týden nemáte objednané žádné jídlo!";
@override
String get corrupted =>
"Nastal problém s dešifrováním uložených údajů, prosím zkuste vyčistit veškerá data této aplikace.";
@override
String get notifyAt => "Odeslat v";
@override
String get notifyLunch =>
"V určený čas odeslat notifikaci s informacemi o obědě";
@override
String get lunchNotif => "Dnes máte objednáno";
@override
String get error => "Chyba";
@override
String get needRemember =>
"Musíte své přihlašovací údaje uložit na přihlašovací obrazovce";
@override
String get notifyWarning =>
"Vaše zařízení může mít povolenou optimalizaci baterie, což může způsobovat neodesílání oznámení. Zkontrolujte nastavení ve vašem zařízení.";
@override
String get signOutWarn => "Opravdu se chcete odhlásit?";
@override
String get jump => "Přeskočit";
@override
String get source => "Zdrojový kód";
@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

@ -1,245 +0,0 @@
import 'package:opencanteen/lang/lang.dart';
class LanguageEn extends Languages {
@override
String get about => "About";
@override
String get agree => "I agree";
@override
String get appName => "OpenCanteen";
@override
String get balance => "Balance: ";
@override
String get cannotOrder => "This food cannot be ordered.";
@override
String get close => "Close";
@override
String get copyright => "© 2022 Matyáš Caras and contributors";
@override
String get disagree => "I disagree";
@override
String get errorContacting =>
"Failed to contact the server, check your connection.";
@override
String get errorOrdering => "Could not order food.";
@override
String get exchange => "Exchange";
@override
String get exchangeError => "Could not put food on exchange.";
@override
String get friday => "Friday";
@override
String get home => "Home";
@override
String get httpLogin =>
"You are trying to sign in using an insecure HTTP connection, are you sure you want to continue?";
@override
String get iCanteenUrl => "iCanteen URL";
@override
String get inExchange => "ON EXCHANGE";
@override
String get license => "Released under the GNU GPLv3";
@override
String get loading => "Loading...";
@override
String get logIn => "Sign in";
@override
String get loggingIn => "Signing you in...";
@override
String get loginFailed => "Sign in failed";
@override
String get menu => "Food Menu";
@override
String get monday => "Monday";
@override
String get mustLogout => "To go online, pull down.";
@override
String get no => "No";
@override
String get noChange => "No, change";
@override
String get noExchange => "No meal in exchange";
@override
String get noFood => "No meal for this day";
@override
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. By continuing you agree.";
@override
String get offline => "YOU ARE OFFLINE";
@override
String get ok => "OK";
@override
String get order => "Order";
@override
String get orderSuccess => "Meal ordered succesfully";
@override
String get ordered => "Ordered";
@override
String get ordering => "Ordering...";
@override
String get password => "Password";
@override
String get pullToReload => "Pull to reload";
@override
String get rememberMe => "Remember me";
@override
String get reportBugs => "Feedback";
@override
String get saturday => "Saturday";
@override
String get saveOffline => "Save today's menu offline";
@override
String get settings => "Settings";
@override
String get signOut => "Sign out";
@override
String get skipWeekend => "Skip weekends when browsing menu";
@override
String get sunday => "Sunday";
@override
String get thursday => "Thursday";
@override
String get tuesday => "Tuesday";
@override
String get username => "Username";
@override
String get verifyExchange =>
"Are you sure you want to put this meal on exchange?";
@override
String get warning => "Warning!";
@override
String get wednesday => "Wednesday";
@override
String get yes => "Yes";
@override
String get appDesc => "OpenCanteen is a mobile app for accessing iCanteen.";
@override
String get welcome => "Welcome to OpenCanteen";
@override
String get aboutOrder => "Order with a tap";
@override
String get howOrder =>
"Simply tap on a meal with a blue checkbox next to it and it's done!";
@override
String get aboutToExch => "Don't want your food?";
@override
String get howToExch =>
"If you cannot cancel your order, simply long-tap on the ordered food and put it into the exchange.";
@override
String get aboutFromExch => "No food? No problem!";
@override
String get howFromExch =>
"Simply check the exchange from the sidebar and order when a meal is available.";
@override
String get next => "Next";
@override
String get checkOrdered => "Check if I have ordered food for the next week";
@override
String get noOrder => "You did not order any food for the next week!";
@override
String get corrupted =>
"The saved credentials seem to be corrupted, please try clearing the application's data.";
@override
String get notifyAt => "Send notification at";
@override
String get notifyLunch => "Send a notification with meal info";
@override
String get lunchNotif => "Today's ordered meal";
@override
String get error => "Error";
@override
String get needRemember =>
"You need to save your login details on the login screen first";
@override
String get notifyWarning =>
"Your device may have battery optimization enabled. This may cause notifications to not be sent. Check the application info in your device's settings.";
@override
String get signOutWarn => "Do you really want to sign out?";
@override
String get jump => "Jump";
@override
String get source => "Source code";
@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

@ -2,7 +2,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class LoginManager {
static Future<Map<String, String>?> getDetails() async {
// zkontrolovat secure storage pokud je něco uložené
// check secure storage for details
const storage = FlutterSecureStorage();
var user = await storage.read(key: "oc_user");
var pass = await storage.read(key: "oc_pass");
@ -18,7 +18,7 @@ class LoginManager {
await storage.write(key: "oc_url", value: url);
}
static Future<bool> zapamatovat() async {
static Future<bool> rememberme() async {
const storage = FlutterSecureStorage();
return await storage.containsKey(key: "oc_pass");
}

View File

@ -1,23 +1,21 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:opencanteen/lang/lang_cz.dart';
import 'package:opencanteen/loginmanager.dart';
import 'package:canteenlib/canteenlib.dart';
import 'package:opencanteen/okna/android/login.dart';
import 'package:opencanteen/okna/ios/login.dart';
import 'package:opencanteen/okna/login.dart';
import 'package:opencanteen/util.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:intl/intl.dart';
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'lang/lang.dart';
import 'lang/lang_en.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'color_schemes.g.dart';
import 'loginmanager.dart';
/*
Copyright (C) 2022 Matyáš Caras a přispěvatelé
@ -37,38 +35,40 @@ Copyright (C) 2022 Matyáš Caras a přispěvatelé
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
final settings = SettingsManager();
void oznamitPredem(SharedPreferences prefs, tz.Location l) async {
/// Used to setup notifications about ordered food
void setupNotification(SharedPreferences prefs, tz.Location l) async {
String title;
String locale = Intl.getCurrentLocale();
switch (locale) {
case "cs_CZ":
title = LanguageCz().lunchNotif;
title = "Dnes máte objednáno";
break;
default:
title = LanguageEn().lunchNotif;
title = "Today's ordered meal";
}
/*if (prefs.getBool("offline") ?? false) {
// TODO možnost brát z offline dat
// TODO grab data from offline storage
} else {*/
// bere online
var d = await LoginManager.getDetails(); // získat údaje
// data from the web
var d = await LoginManager.getDetails(); // grab login
if (d != null) {
var c = Canteen(d["url"]!);
if (await c.login(d["user"]!, d["pass"]!)) {
var jidla = await c.jidelnicekDen();
try {
var jidlo = jidla.jidla.singleWhere(
(element) => element.objednano); // získá objednané jídlo
var jidlo = jidla.jidla
.singleWhere((element) => element.objednano); // grab ordered meal
var kdy = DateTime.parse(prefs.getString(
"oznameni_cas")!); // uložíme čas, kdy se odeslat oznámení
var cas = casNaDate(
"oznameni_cas")!); // save the time the notif should be sent
var cas = timeToDate(
TimeOfDay(hour: kdy.hour, minute: kdy.minute),
);
if (cas.isBefore(DateTime.now())) return;
// data o oznámení
// notif data
const AndroidNotificationDetails androidSpec =
AndroidNotificationDetails('predobedem', 'Oznámení před obědem',
channelDescription: 'Oznámení o dnešním jídle',
@ -77,18 +77,18 @@ void oznamitPredem(SharedPreferences prefs, tz.Location l) async {
styleInformation: BigTextStyleInformation(''),
ticker: 'today meal');
// naplánovat
// plan through lib
await flutterLocalNotificationsPlugin.zonedSchedule(
0,
title,
"${jidlo.varianta} - ${jidlo.nazev}",
tz.TZDateTime.from(cas, l),
const NotificationDetails(android: androidSpec),
androidAllowWhileIdle: true,
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
} on StateError catch (_) {
// nenalezeno
// no ordered meal found
}
}
// }
@ -103,10 +103,14 @@ void main() async {
var prefs = await SharedPreferences.getInstance();
if (prefs.getBool("oznamit") ?? false) {
oznamitPredem(prefs, l);
setupNotification(prefs, l);
}
settings.checkOrdered = prefs.getBool("tyden") ?? false;
settings.saveOffline = prefs.getBool("oznamit") ?? false;
settings.skipWeekend = prefs.getBool("skip") ?? false;
settings.allergens = prefs.getBool("allergens") ?? false;
// nastavit oznámení
// notif library setup
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('notif_icon');
@ -116,53 +120,51 @@ void main() async {
InitializationSettings(android: initializationSettingsAndroid, iOS: ios);
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
// spustit aplikaci
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
localizationsDelegates: const [
AppLocalizationsDelegate(),
...GlobalMaterialLocalizations.delegates
],
supportedLocales: const [Locale("cs", ""), Locale("en", "")],
title: "OpenCanteen",
theme: ThemeData(
primarySwatch: Colors.purple,
),
darkTheme: ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.purple,
),
home: (Platform.isAndroid) ? const AndroidLogin() : const IOSLogin(),
);
return (Platform
.isAndroid) // run app based on current platform to make use of the platform's respective UI lib
? MaterialApp(
debugShowCheckedModeBanner: false,
localizationsDelegates: const [
AppLocalizations.delegate,
...GlobalMaterialLocalizations.delegates
],
supportedLocales: AppLocalizations.supportedLocales,
title: "OpenCanteen",
theme: ThemeData(useMaterial3: true, colorScheme: lightColorScheme),
darkTheme: ThemeData(
brightness: Brightness.dark,
useMaterial3: true,
colorScheme: darkColorScheme),
home: const LoginPage(),
)
: Theme(
data: ThemeData(
useMaterial3: true,
colorScheme: (MediaQuery.of(context).platformBrightness ==
Brightness.dark)
? darkColorScheme
: lightColorScheme),
child: const CupertinoApp(
debugShowCheckedModeBanner: false,
localizationsDelegates: [
AppLocalizations.delegate,
...GlobalMaterialLocalizations.delegates
],
supportedLocales: AppLocalizations.supportedLocales,
title: "OpenCanteen",
theme: CupertinoThemeData(
primaryColor: Colors.purple,
),
home: LoginPage(),
),
);
}
}
class AppLocalizationsDelegate extends LocalizationsDelegate<Languages> {
const AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) => ['cs', 'en'].contains(locale.languageCode);
@override
Future<Languages> load(Locale locale) => _load(locale);
static Future<Languages> _load(Locale locale) async {
switch (locale.languageCode) {
case 'cs':
return LanguageCz();
default:
return LanguageEn();
}
}
@override
bool shouldReload(LocalizationsDelegate<Languages> old) => false;
}

View File

@ -1,142 +0,0 @@
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart';
import 'package:opencanteen/okna/android/login.dart';
import 'package:opencanteen/util.dart';
import '../../lang/lang.dart';
class AndroidBurza extends StatefulWidget {
const AndroidBurza({Key? key, required this.canteen}) : super(key: key);
final Canteen canteen;
@override
State<AndroidBurza> createState() => _AndroidBurzaState();
}
class _AndroidBurzaState extends State<AndroidBurza> {
List<Widget> obsah = [];
double kredit = 0.0;
Future<void> nactiBurzu(BuildContext context) async {
obsah = [const CircularProgressIndicator()];
widget.canteen.ziskejUzivatele().then((kr) {
kredit = kr.kredit;
widget.canteen.ziskatBurzu().then((burza) {
setState(() {
obsah = [];
if (burza.isEmpty) {
obsah = [
Text(
Languages.of(context)!.noExchange,
style: const TextStyle(fontSize: 20),
),
Text(Languages.of(context)!.pullToReload)
];
} 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((a) {
if (a) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(Languages.of(context)!.ordered),
content: Text(
Languages.of(context)!.orderSuccess),
actions: [
TextButton(
child: Text(Languages.of(context)!.ok),
onPressed: () =>
Navigator.of(context).pop(),
)
],
),
);
} else {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
Languages.of(context)!.cannotOrder),
content: Text(
Languages.of(context)!.errorOrdering),
actions: [
TextButton(
child: Text(Languages.of(context)!.ok),
onPressed: () =>
Navigator.of(context).pop(),
)
],
),
);
}
nactiBurzu(context);
});
},
child: Text(Languages.of(context)!.order)),
],
),
),
);
}
}
});
});
}).catchError((o) {
if (!widget.canteen.prihlasen) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (c) => const AndroidLogin()));
}
});
}
@override
void initState() {
super.initState();
nactiBurzu(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: drawerGenerator(context, widget.canteen, 3),
appBar: AppBar(
title: Text(Languages.of(context)!.exchange),
),
body: RefreshIndicator(
child: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
child: Column(
children: [
const SizedBox(height: 10),
Text("${Languages.of(context)!.balance}$kredit"),
const SizedBox(height: 10),
SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: SizedBox(
height: MediaQuery.of(context).size.height / 1.3,
child: Column(children: obsah),
),
)
],
),
),
),
onRefresh: () => nactiBurzu(context)),
);
}
}

View File

@ -1,554 +0,0 @@
import 'dart:convert';
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/android/login.dart';
import 'package:opencanteen/okna/android/nastaveni.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';
class AndroidJidelnicek extends StatefulWidget {
const AndroidJidelnicek({Key? key, required this.canteen}) : super(key: key);
final Canteen canteen;
@override
State<AndroidJidelnicek> createState() => _AndroidJidelnicekState();
}
class _AndroidJidelnicekState extends State<AndroidJidelnicek> {
List<Widget> obsah = [const CircularProgressIndicator()];
DateTime den = DateTime.now();
String denTydne = "";
double kredit = 0.0;
bool _skipWeekend = false;
void kontrolaTyden(BuildContext context) async {
var prefs = await SharedPreferences.getInstance();
if (prefs.getBool("tyden") ?? false) {
// Zjistit jestli je objednáno na přístí týden
var pristi = den.add(const Duration(days: 6));
for (var i = 0; i < 5; i++) {
var jidelnicek = await widget.canteen
.jidelnicekDen(den: pristi.add(Duration(days: i + 1)));
if (jidelnicek.jidla.isNotEmpty &&
!jidelnicek.jidla.any((element) => element.objednano == true)) {
if (!mounted) break;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.noOrder),
duration: const Duration(seconds: 5),
action: SnackBarAction(
onPressed: () => setState(
() {
den = pristi.add(Duration(days: i + 1));
nactiJidlo();
},
),
label: Languages.of(context)!.jump,
),
),
);
break;
}
}
}
}
Future<void> nactiJidlo() async {
obsah = [const CircularProgressIndicator()];
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;
}
widget.canteen.ziskejUzivatele().then((kr) {
kredit = kr.kredit;
widget.canteen.jidelnicekDen(den: den).then((jd) async {
setState(() {
obsah = [];
if (jd.jidla.isEmpty) {
obsah.add(Text(
Languages.of(context)!.noFood,
style: const 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)
? Languages.of(context)!.inExchange
: "${j.cena}"),
Checkbox(
value: j.objednano,
fillColor: (j.lzeObjednat)
? MaterialStateProperty.all(Colors.purple)
: MaterialStateProperty.all(Colors.grey),
onChanged: (v) async {
if (!j.lzeObjednat) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(Languages.of(context)!
.errorOrdering),
content: Text(
Languages.of(context)!.cannotOrder),
actions: [
TextButton(
child:
Text(Languages.of(context)!.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
} else {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => Dialog(
child: SizedBox(
height: 100,
child: Row(children: [
const Padding(
padding: EdgeInsets.all(10),
child:
CircularProgressIndicator(),
),
Text(Languages.of(context)!
.ordering)
]),
),
));
widget.canteen.objednat(j).then((_) {
Navigator.of(context, rootNavigator: true)
.pop();
nactiJidlo();
}).catchError((o) {
Navigator.of(context, rootNavigator: true)
.pop();
showDialog(
context: context,
builder: (bc) => AlertDialog(
title: Text(Languages.of(context)!
.errorOrdering),
content: Text(o.toString()),
actions: [
TextButton(
child: Text(
Languages.of(context)!
.close),
onPressed: () {
Navigator.pop(bc);
},
)
],
));
});
}
})
],
),
onTap: () async {
if (!j.lzeObjednat) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title:
Text(Languages.of(context)!.errorOrdering),
content:
Text(Languages.of(context)!.cannotOrder),
actions: [
TextButton(
child: Text(Languages.of(context)!.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
} else {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => Dialog(
child: SizedBox(
height: 100,
child: Row(children: [
const Padding(
padding: EdgeInsets.all(10),
child: CircularProgressIndicator(),
),
Text(Languages.of(context)!.ordering)
]),
),
));
widget.canteen.objednat(j).then((_) {
Navigator.of(context, rootNavigator: true).pop();
nactiJidlo();
}).catchError((o) {
Navigator.of(context, rootNavigator: true).pop();
showDialog(
context: context,
builder: (bc) => AlertDialog(
title: Text(
Languages.of(context)!.errorOrdering),
content: Text(o.toString()),
actions: [
TextButton(
child:
Text(Languages.of(context)!.close),
onPressed: () {
Navigator.pop(bc);
},
)
],
));
});
}
},
onLongPress: () async {
if (!j.objednano || j.burzaUrl == null) return;
if (!j.naBurze) {
// pokud není na burze, radši se zeptáme
var d = await showDialog(
context: context,
builder: (bc) => SimpleDialog(
title: Text(
Languages.of(context)!.verifyExchange),
children: [
SimpleDialogOption(
onPressed: () {
Navigator.pop(bc, true);
},
child: Text(Languages.of(context)!.yes),
),
SimpleDialogOption(
onPressed: () {
Navigator.pop(bc, false);
},
child: Text(Languages.of(context)!.no),
),
],
));
if (d) {
widget.canteen
.doBurzy(j)
.then((_) => nactiJidlo())
.catchError((o) {
showDialog(
context: context,
builder: (bc) => AlertDialog(
title: Text(
Languages.of(context)!.exchangeError),
content: Text(o.toString()),
actions: [
TextButton(
child: Text(
Languages.of(context)!.close),
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 AndroidLogin()));
}
});
}
Future<void> kliknuti(String value, BuildContext context) async {
if (value == Languages.of(context)!.signOut) {
await showDialog<bool>(
context: context,
builder: (c) => AlertDialog(
title: Text(Languages.of(context)!.warning),
content: Text(Languages.of(context)!.signOutWarn),
actions: [
TextButton(
onPressed: () {
const storage = FlutterSecureStorage();
storage.deleteAll();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (c) => const AndroidLogin()),
(route) => false);
},
child: Text(Languages.of(context)!.yes)),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(Languages.of(context)!.no))
],
),
);
} else if (value == Languages.of(context)!.review) {
launchUrl(Uri.parse("market://details?id=cz.hernikplays.opencanteen"),
mode: LaunchMode.externalApplication);
} else if (value == Languages.of(context)!.reportBugs) {
launchUrl(Uri.parse("https://forms.gle/jKN7QeFJwpaApSbC8"),
mode: LaunchMode.externalApplication);
} else if (value == Languages.of(context)!.about) {
var packageInfo = await PackageInfo.fromPlatform();
if (!mounted) return;
showAboutDialog(
context: context,
applicationName: "OpenCanteen",
applicationLegalese:
"${Languages.of(context)!.copyright}\n${Languages.of(context)!.license}",
applicationVersion: packageInfo.version,
children: [
TextButton(
onPressed: (() => launchUrl(
Uri.parse("https://git.mnau.xyz/hernik/opencanteen"))),
child: Text(Languages.of(context)!.source))
]);
} else if (value == Languages.of(context)!.settings) {
Navigator.push(
context, MaterialPageRoute(builder: (c) => const AndroidNastaveni()));
}
}
void nactiNastaveni() async {
var prefs = await SharedPreferences.getInstance();
_skipWeekend = prefs.getBool("skip") ?? false;
if (!mounted) return;
kontrolaTyden(context);
}
void ulozitDoOffline() async {
var prefs = await SharedPreferences.getInstance();
if (prefs.getBool("offline") ?? false) {
// vyčistit offline
Directory appDocDir = await getApplicationDocumentsDirectory();
for (var f in appDocDir.listSync()) {
if (f.path.contains("jidelnicek")) {
f.deleteSync();
}
}
// 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;
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));
}
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
nactiNastaveni();
ulozitDoOffline();
nactiJidlo();
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: drawerGenerator(context, widget.canteen, 1),
appBar: AppBar(
title: Text(Languages.of(context)!.menu),
actions: [
PopupMenuButton(
onSelected: ((String value) => kliknuti(value, context)),
itemBuilder: (BuildContext context) {
return {
Languages.of(context)!.reportBugs,
Languages.of(context)!.review,
Languages.of(context)!.settings,
Languages.of(context)!.about,
Languages.of(context)!.signOut
}.map((String choice) {
return PopupMenuItem<String>(
value: choice,
child: Text(choice),
);
}).toList();
},
),
],
),
body: RefreshIndicator(
onRefresh: nactiJidlo,
child: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
child: Column(
children: [
const SizedBox(height: 10),
Text("${Languages.of(context)!.balance}$kredit"),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
IconButton(
onPressed: () {
setState(() {
den = den.subtract(const Duration(days: 1));
if (den.weekday == 7 && _skipWeekend) {
den = den.subtract(const Duration(days: 2));
}
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: Localizations.localeOf(context));
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));
if (den.weekday == 6 && _skipWeekend) {
den = den.add(const Duration(days: 2));
}
nactiJidlo();
});
},
icon: const Icon(Icons.arrow_right),
),
IconButton(
onPressed: () => setState(() {
den = DateTime.now();
nactiJidlo();
}),
icon: const Icon(Icons.today))
]),
SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: GestureDetector(
child: Container(
color: Theme.of(context)
.colorScheme
.onPrimary
.withOpacity(0),
height: MediaQuery.of(context).size.height / 1.3,
child: Column(children: obsah),
),
onHorizontalDragEnd: (details) {
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();
});
}
},
),
)
],
),
),
),
),
);
}
}

View File

@ -1,281 +0,0 @@
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_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/okna/android/welcome.dart';
import '../../lang/lang.dart';
import '../../loginmanager.dart';
import '../../main.dart';
import '../../util.dart';
import 'jidelnicek.dart';
import 'offline_jidelnicek.dart';
class AndroidLogin extends StatefulWidget {
const AndroidLogin({Key? key}) : super(key: key);
@override
State<AndroidLogin> createState() => _AndroidLoginState();
}
class _AndroidLoginState extends State<AndroidLogin> {
TextEditingController userControl = TextEditingController();
TextEditingController passControl = TextEditingController();
TextEditingController canteenControl = TextEditingController();
bool rememberMe = false;
bool _showUrl = false;
String dropdownUrl = instance.first["url"] ?? "";
@override
void initState() {
super.initState();
LoginManager.getDetails().then((r) async {
// žádat o oprávnění na android
flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.requestPermission();
if (r != null) {
// Automaticky přihlásit
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => Dialog(
child: SizedBox(
height: 100,
child: Row(children: [
const Padding(
padding: EdgeInsets.all(10),
child: CircularProgressIndicator(),
),
Text(Languages.of(context)!.loggingIn)
]),
),
));
var canteen = Canteen(r["url"]!);
try {
var l = await canteen.login(r["user"]!, r["pass"]!);
if (!l) {
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.loginFailed),
),
);
return;
}
const storage = FlutterSecureStorage();
var odsouhlasil = await storage.read(key: "oc_souhlas");
if (!mounted) return;
if (odsouhlasil == null || odsouhlasil != "ano") {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (c) => AndroidWelcome(canteen: canteen),
),
(route) => false);
} else {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => AndroidJidelnicek(canteen: canteen),
),
(route) => false);
}
} on PlatformException {
if (!mounted) return;
Navigator.of(context).pop();
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.corrupted),
),
);
} catch (_) {
if (!mounted) return;
Navigator.of(context).pop();
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.errorContacting),
),
);
goOffline();
}
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Languages.of(context)!.logIn),
automaticallyImplyLeading: false,
),
body: Center(
child: SingleChildScrollView(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
Languages.of(context)!.appName,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 40),
),
Text(
Languages.of(context)!.logIn,
textAlign: TextAlign.center,
),
TextField(
controller: userControl,
autofillHints: const [AutofillHints.username],
decoration: InputDecoration(
labelText: Languages.of(context)!.username),
),
TextField(
autofillHints: const [AutofillHints.password],
decoration: InputDecoration(
labelText: Languages.of(context)!.password),
controller: passControl,
obscureText: true,
),
const SizedBox(
height: 10,
),
DropdownButton(
isExpanded: true,
value: dropdownUrl,
items: instance.map<DropdownMenuItem<String>>((e) {
return DropdownMenuItem<String>(
value: e["url"],
child: Text(e["name"]!),
);
}).toList(),
onChanged: (String? value) {
setState(() {
if (value == "") {
_showUrl = true;
} else {
_showUrl = false;
}
dropdownUrl = value!;
});
},
),
AnimatedOpacity(
opacity: _showUrl ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: TextField(
autofillHints: const [AutofillHints.url],
decoration: InputDecoration(
labelText: Languages.of(context)!.iCanteenUrl),
keyboardType: TextInputType.url,
controller: canteenControl,
),
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Switch(
value: rememberMe,
onChanged: (value) {
setState(() {
rememberMe = value;
});
}),
Text(Languages.of(context)!.rememberMe)
]),
TextButton(
onPressed: () async {
var canteenUrl = (dropdownUrl == "")
? canteenControl.text
: dropdownUrl;
if (!canteenUrl.startsWith("https://") &&
!canteenUrl.startsWith("http://")) {
canteenUrl = "https://$canteenUrl";
}
var canteen = Canteen(canteenUrl);
try {
var l = await canteen.login(
userControl.text, passControl.text);
if (!l) {
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content:
Text(Languages.of(context)!.loginFailed),
),
);
return;
}
if (rememberMe) {
LoginManager.setDetails(
userControl.text, passControl.text, canteenUrl);
}
// souhlas
const storage = FlutterSecureStorage();
var odsouhlasil =
await storage.read(key: "oc_souhlas");
if (!mounted) return;
if (odsouhlasil == null || odsouhlasil != "ano") {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (c) => AndroidWelcome(
canteen: canteen,
)),
(route) => false);
} else {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => AndroidJidelnicek(
canteen: canteen,
)),
(route) => false);
}
} on PlatformException {
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.corrupted),
),
);
} on Exception catch (_) {
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content:
Text(Languages.of(context)!.errorContacting),
),
);
goOffline();
}
},
child: Text(Languages.of(context)!.logIn)),
],
),
),
),
));
}
/// Získá offline soubor a zobrazí údaje
void goOffline() async {
if (!mounted) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: ((context) => const AndroidOfflineJidelnicek())),
(route) => false);
}
}

View File

@ -1,282 +0,0 @@
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';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:timezone/timezone.dart' as tz;
import '../../lang/lang.dart';
import '../../loginmanager.dart';
import '../../main.dart';
import '../../util.dart';
class AndroidNastaveni extends StatefulWidget {
const AndroidNastaveni({Key? key}) : super(key: key);
@override
State<AndroidNastaveni> createState() => _AndroidNastaveniState();
}
class _AndroidNastaveniState extends State<AndroidNastaveni> {
bool _ukladatOffline = false;
bool _preskakovatVikend = false;
bool _kontrolovatTyden = false;
bool _oznameniObed = false;
bool _zapamatovany = false;
TimeOfDay _oznameniCas = TimeOfDay.now();
final TextEditingController _countController =
TextEditingController(text: "1");
SharedPreferences? preferences;
void najitNastaveni() async {
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;
_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());
} else {
_oznameniCas = TimeOfDay.fromDateTime(DateTime.parse(casStr));
}
});
}
void zmenitNastaveni(String key, bool value) async {
preferences!.setBool(key, value);
}
@override
void initState() {
super.initState();
najitNastaveni();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Languages.of(context)!.settings),
),
body: Center(
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,
activeColor: Colors.purple,
onChanged: (value) {
setState(() {
_ukladatOffline = value;
cistit(value);
zmenitNastaveni("offline", value);
});
})
],
),
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: [
Text(Languages.of(context)!.skipWeekend),
Switch(
activeColor: Colors.purple,
value: _preskakovatVikend,
onChanged: (value) {
setState(() {
_preskakovatVikend = value;
zmenitNastaveni("skip", value);
});
})
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(Languages.of(context)!.checkOrdered)),
Switch(
activeColor: Colors.purple,
value: _kontrolovatTyden,
onChanged: (value) {
setState(() {
_kontrolovatTyden = value;
zmenitNastaveni("tyden", value);
});
})
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(Languages.of(context)!.notifyLunch)),
Switch(
activeColor: Colors.purple,
value: _oznameniObed,
thumbColor: (!_zapamatovany
? MaterialStateProperty.all(Colors.grey)
: null),
onChanged: (value) {
if (!_zapamatovany) {
showDialog(
context: context,
builder: (bc) => AlertDialog(
title: Text(Languages.of(context)!.error),
content:
Text(Languages.of(context)!.needRemember),
actions: [
TextButton(
child: Text(Languages.of(context)!.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
} else {
setState(() {
_oznameniObed = value;
if (_oznameniObed) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title:
Text(Languages.of(context)!.warning),
content: Text(
Languages.of(context)!.notifyWarning),
actions: [
TextButton(
child:
Text(Languages.of(context)!.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
vytvoritOznameni(casNaDate(_oznameniCas));
}
zmenitNastaveni("oznamit", value);
});
}
})
],
),
Text(Languages.of(context)!.notifyAt),
TextButton(
onPressed: () async {
if (_oznameniObed) {
var cas = await showTimePicker(
context: context, initialTime: _oznameniCas);
if (cas != null) {
var prefs = await SharedPreferences.getInstance();
prefs.setString("oznameni_cas",
casNaDate(cas).toString()); // aktualizovat vybraný čas
var den = casNaDate(cas);
debugPrint(den.isAfter(DateTime.now()).toString());
if (den.isAfter(DateTime.now())) {
// znovu vytvořit oznámení POUZE když je čas v budoucnosti
vytvoritOznameni(den);
}
}
setState(() {
_oznameniCas = cas ?? _oznameniCas;
});
}
},
child: Text(
"${(_oznameniCas.hour < 10 ? "0" : "") + _oznameniCas.hour.toString()}:${(_oznameniCas.minute < 10 ? "0" : "") + _oznameniCas.minute.toString()}",
style: TextStyle(
color: (!_oznameniObed) ? Colors.grey : Colors.purple),
),
),
],
),
)),
);
}
void cistit(bool value) async {
if (!value) {
Directory appDocDir = await getApplicationDocumentsDirectory();
for (var f in appDocDir.listSync()) {
// Vymažeme obsah
if (f.path.contains("jidelnicek")) {
f.deleteSync();
}
}
}
}
void vytvoritOznameni(DateTime den) async {
await flutterLocalNotificationsPlugin.cancelAll();
var d = await LoginManager.getDetails(); // získat údaje
if (d != null) {
// Nové oznámení
var c = Canteen(d["url"]!);
if (await c.login(d["user"]!, d["pass"]!)) {
var jidla = await c.jidelnicekDen();
try {
var jidlo = jidla.jidla.singleWhere((element) => element.objednano);
const AndroidNotificationDetails androidSpec =
AndroidNotificationDetails('opencanteen', 'predobjedem',
channelDescription: 'Oznámení o dnešním jídle',
importance: Importance.max,
priority: Priority.high,
ticker: 'today meal');
var l =
tz.getLocation(await FlutterNativeTimezone.getLocalTimezone());
if (!mounted) return;
await flutterLocalNotificationsPlugin.zonedSchedule(
// Vytvoří nové oznámení pro daný čas a datum
0,
Languages.of(context)!.lunchNotif,
"${jidlo.varianta} - ${jidlo.nazev}",
tz.TZDateTime.from(den, l),
const NotificationDetails(android: androidSpec),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
} on StateError catch (_) {
// nenalezeno
}
}
}
}
}

View File

@ -1,264 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/okna/android/login.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';
class AndroidOfflineJidelnicek extends StatefulWidget {
const AndroidOfflineJidelnicek({Key? key}) : super(key: key);
@override
State<AndroidOfflineJidelnicek> createState() =>
_AndroidOfflineJidelnicekState();
}
class _AndroidOfflineJidelnicekState extends State<AndroidOfflineJidelnicek> {
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 {
var jidelnicek = data[jidloIndex];
den = jidelnicek[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 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,
),
),
Text((j.naBurze)
? Languages.of(context)!.inExchange
: "${j.cena}"),
Checkbox(
value: j.objednano,
fillColor: MaterialStateProperty.all(Colors.grey),
onChanged: (v) async {
return;
},
)
],
),
),
),
);
}
setState(() {});
}
void kliknuti(String value, BuildContext context) async {
if (value == Languages.of(context)!.signOut) {
const storage = FlutterSecureStorage();
storage.deleteAll();
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (c) => const AndroidLogin()));
} else if (value == Languages.of(context)!.review) {
launchUrl(Uri.parse("market://details?id=cz.hernikplays.opencanteen"),
mode: LaunchMode.externalApplication);
} else if (value == Languages.of(context)!.reportBugs) {
launchUrl(Uri.parse("https://forms.gle/jKN7QeFJwpaApSbC8"),
mode: LaunchMode.externalApplication);
} else if (value == Languages.of(context)!.about) {
var packageInfo = await PackageInfo.fromPlatform();
if (!mounted) return;
showAboutDialog(
context: context,
applicationName: "OpenCanteen",
applicationLegalese:
"${Languages.of(context)!.copyright}\n${Languages.of(context)!.license}",
applicationVersion: packageInfo.version,
children: [
TextButton(
onPressed: (() => launchUrl(
Uri.parse("https://git.mnau.xyz/hernik/opencanteen"))),
child: Text(Languages.of(context)!.source))
]);
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
nactiNastaveni();
}
void nactiNastaveni() async {
var prefs = await SharedPreferences.getInstance();
_skipWeekend = prefs.getBool("skip") ?? false;
if (!mounted) return;
nactiZeSouboru();
}
@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)!.review,
Languages.of(context)!.about,
Languages.of(context)!.signOut
}.map((String choice) {
return PopupMenuItem<String>(
value: choice,
child: Text(choice),
);
}).toList();
},
),
],
),
body: RefreshIndicator(
child: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
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),
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(
children: obsah,
),
),
],
),
),
),
onRefresh: () => Navigator.pushReplacement(context,
MaterialPageRoute(builder: ((context) => const AndroidLogin()))),
),
);
}
}

View File

@ -1,78 +0,0 @@
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:introduction_screen/introduction_screen.dart';
import 'package:opencanteen/lang/lang.dart';
import 'package:opencanteen/okna/android/jidelnicek.dart';
class AndroidWelcome extends StatefulWidget {
const AndroidWelcome({Key? key, required this.canteen}) : super(key: key);
final Canteen canteen;
@override
State<AndroidWelcome> createState() => _AndroidWelcomeState();
}
class _AndroidWelcomeState extends State<AndroidWelcome> {
@override
Widget build(BuildContext context) {
var listPagesViewModel = [
PageViewModel(
title: Languages.of(context)!.welcome,
body: Languages.of(context)!.appDesc,
image: const Center(
child: Icon(Icons.waving_hand_outlined, size: 175),
),
),
PageViewModel(
title: Languages.of(context)!.aboutOrder,
body: Languages.of(context)!.howOrder,
image: Center(
child: Image.asset('assets/objednavam.png',
width: MediaQuery.of(context).size.width * 0.85),
),
),
PageViewModel(
title: Languages.of(context)!.aboutToExch,
body: Languages.of(context)!.howToExch,
image: Center(
child: Image.asset('assets/doburzy.png',
width: MediaQuery.of(context).size.width * 0.85),
),
),
PageViewModel(
title: Languages.of(context)!.aboutFromExch,
body: Languages.of(context)!.howFromExch,
image: Center(
child: Image.asset('assets/burza.png',
width: MediaQuery.of(context).size.width * 0.85),
),
),
PageViewModel(
title: Languages.of(context)!.warning,
body: Languages.of(context)!.notOfficial,
image: const Center(
child: Icon(Icons.warning_amber_outlined, size: 175),
),
),
];
return Scaffold(
body: IntroductionScreen(
pages: listPagesViewModel,
next: Text(Languages.of(context)!.next),
done: Text(Languages.of(context)!.ok,
style: const TextStyle(fontWeight: FontWeight.w600)),
onDone: () async {
const storage = FlutterSecureStorage();
await storage.write(key: "oc_souhlas", value: "ano");
if (!mounted) return;
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (c) => AndroidJidelnicek(canteen: widget.canteen)),
(route) => false);
},
),
);
}
}

148
lib/okna/burza.dart Normal file
View File

@ -0,0 +1,148 @@
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart';
import 'package:opencanteen/okna/login.dart';
import 'package:opencanteen/pw/platformbutton.dart';
import 'package:opencanteen/pw/platformdialog.dart';
import 'package:opencanteen/util.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class BurzaView extends StatefulWidget {
const BurzaView({super.key, required this.canteen});
final Canteen canteen;
@override
State<BurzaView> createState() => _BurzaViewState();
}
class _BurzaViewState extends State<BurzaView> {
List<Widget> content = [];
double balance = 0.0;
Future<void> loadExchange(BuildContext context) async {
content = [const CircularProgressIndicator()];
var uzivatel = await widget.canteen.ziskejUzivatele().catchError((o) {
if (!widget.canteen.prihlasen) {
Navigator.pushReplacement(
context, platformRouter((c) => const LoginPage()));
}
return Uzivatel(kredit: 0);
});
balance = uzivatel.kredit;
var burza = await widget.canteen.ziskatBurzu();
setState(() {
content = [];
if (burza.isEmpty) {
content = [
Text(
AppLocalizations.of(context)!.noExchange,
style: const TextStyle(fontSize: 20),
),
Text(AppLocalizations.of(context)!.pullToReload)
];
} else {
for (var b in burza) {
content.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"),
PlatformButton(
onPressed: () {
widget.canteen.objednatZBurzy(b).then(
(a) {
if (a) {
showDialog(
context: context,
builder: (context) => PlatformDialog(
title: AppLocalizations.of(context)!.ordered,
content:
AppLocalizations.of(context)!.orderSuccess,
actions: [
PlatformButton(
text: AppLocalizations.of(context)!.ok,
onPressed: () =>
Navigator.of(context).pop(),
)
],
),
);
} else {
showDialog(
context: context,
builder: (context) => PlatformDialog(
title:
AppLocalizations.of(context)!.cannotOrder,
content:
AppLocalizations.of(context)!.errorOrdering,
actions: [
PlatformButton(
text: AppLocalizations.of(context)!.ok,
onPressed: () =>
Navigator.of(context).pop(),
)
],
),
);
}
loadExchange(context);
},
);
},
text: AppLocalizations.of(context)!.order,
),
],
),
),
);
}
}
});
return;
}
@override
void initState() {
super.initState();
loadExchange(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: drawerGenerator(context, widget.canteen, 3),
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.exchange),
),
body: RefreshIndicator(
child: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
child: Column(
children: [
const SizedBox(height: 10),
Text("${AppLocalizations.of(context)!.balance}$balance"),
const SizedBox(height: 10),
SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: SizedBox(
height: MediaQuery.of(context).size.height / 1.3,
child: Column(children: content),
),
)
],
),
),
),
onRefresh: () => loadExchange(context)),
);
}
}

View File

@ -1,143 +0,0 @@
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:opencanteen/okna/ios/login.dart';
import 'package:opencanteen/util.dart';
import '../../lang/lang.dart';
class IOSBurza extends StatefulWidget {
const IOSBurza({Key? key, required this.canteen}) : super(key: key);
final Canteen canteen;
@override
State<IOSBurza> createState() => _IOSBurzaState();
}
class _IOSBurzaState extends State<IOSBurza> {
List<Widget> obsah = [];
double kredit = 0.0;
Future<void> nactiBurzu(BuildContext context) async {
obsah = [const CircularProgressIndicator()];
widget.canteen.ziskejUzivatele().then((kr) {
kredit = kr.kredit;
widget.canteen.ziskatBurzu().then((burza) {
setState(() {
obsah = [];
if (burza.isEmpty) {
obsah = [
Text(
Languages.of(context)!.noExchange,
style: const TextStyle(fontSize: 20),
),
Text(Languages.of(context)!.pullToReload)
];
} 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"),
CupertinoButton(
onPressed: () {
widget.canteen.objednatZBurzy(b).then((a) {
if (a) {
showDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: Text(Languages.of(context)!.ordered),
content: Text(
Languages.of(context)!.orderSuccess),
actions: [
CupertinoButton(
child: Text(Languages.of(context)!.ok),
onPressed: () =>
Navigator.of(context).pop(),
)
],
),
);
} else {
showDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: Text(
Languages.of(context)!.cannotOrder),
content: Text(
Languages.of(context)!.errorOrdering),
actions: [
CupertinoButton(
child: Text(Languages.of(context)!.ok),
onPressed: () =>
Navigator.of(context).pop(),
)
],
),
);
}
nactiBurzu(context);
});
},
child: Text(Languages.of(context)!.order)),
],
),
),
);
}
}
});
});
}).catchError((o) {
if (!widget.canteen.prihlasen) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (c) => const IOSLogin()));
}
});
}
@override
void initState() {
super.initState();
nactiBurzu(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: drawerGenerator(context, widget.canteen, 3),
appBar: AppBar(
title: Text(Languages.of(context)!.exchange),
),
body: RefreshIndicator(
child: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
child: Column(
children: [
const SizedBox(height: 10),
Text("${Languages.of(context)!.balance}$kredit"),
const SizedBox(height: 10),
SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: SizedBox(
height: MediaQuery.of(context).size.height / 1.3,
child: Column(children: obsah),
),
)
],
),
),
),
onRefresh: () => nactiBurzu(context)),
);
}
}

View File

@ -1,568 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/okna/ios/login.dart';
import 'package:opencanteen/okna/ios/nastaveni.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';
class IOSJidelnicek extends StatefulWidget {
const IOSJidelnicek({Key? key, required this.canteen}) : super(key: key);
final Canteen canteen;
@override
State<IOSJidelnicek> createState() => _IOSJidelnicekState();
}
class _IOSJidelnicekState extends State<IOSJidelnicek> {
List<Widget> obsah = [const CircularProgressIndicator()];
DateTime den = DateTime.now();
String denTydne = "";
double kredit = 0.0;
bool _skipWeekend = false;
void kontrolaTyden(BuildContext context) async {
var prefs = await SharedPreferences.getInstance();
if (prefs.getBool("tyden") ?? false) {
// Zjistit jestli je objednáno na přístí týden
var pristi = den.add(const Duration(days: 6));
for (var i = 0; i < 5; i++) {
var jidelnicek = await widget.canteen
.jidelnicekDen(den: pristi.add(Duration(days: i + 1)));
if (jidelnicek.jidla.isNotEmpty &&
!jidelnicek.jidla.any((element) => element.objednano == true)) {
if (!mounted) break;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.noOrder),
duration: const Duration(seconds: 5),
action: SnackBarAction(
onPressed: () => setState(
() {
den = pristi.add(Duration(days: i + 1));
nactiJidlo();
},
),
label: Languages.of(context)!.jump,
),
),
);
break;
}
}
}
}
Future<void> nactiJidlo() async {
obsah = [const CircularProgressIndicator()];
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;
}
widget.canteen.ziskejUzivatele().then((kr) {
kredit = kr.kredit;
widget.canteen.jidelnicekDen(den: den).then((jd) async {
setState(() {
obsah = [];
if (jd.jidla.isEmpty) {
obsah.add(Text(
Languages.of(context)!.noFood,
style: const 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)
? Languages.of(context)!.inExchange
: "${j.cena}"),
Checkbox(
value: j.objednano,
fillColor: (j.lzeObjednat)
? MaterialStateProperty.all(Colors.purple)
: MaterialStateProperty.all(Colors.grey),
onChanged: (v) async {
if (!j.lzeObjednat) {
showDialog(
context: context,
builder: (context) {
return CupertinoAlertDialog(
title: Text(Languages.of(context)!
.errorOrdering),
content: Text(
Languages.of(context)!.cannotOrder),
actions: [
CupertinoButton(
child:
Text(Languages.of(context)!.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
} else {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => Dialog(
child: SizedBox(
height: 100,
child: Row(children: [
const Padding(
padding: EdgeInsets.all(10),
child:
CircularProgressIndicator(),
),
Text(Languages.of(context)!
.ordering)
]),
),
));
widget.canteen.objednat(j).then((_) {
Navigator.of(context, rootNavigator: true)
.pop();
nactiJidlo();
}).catchError((o) {
Navigator.of(context, rootNavigator: true)
.pop();
showDialog(
context: context,
builder: (bc) => CupertinoAlertDialog(
title: Text(Languages.of(context)!
.errorOrdering),
content: Text(o.toString()),
actions: [
CupertinoButton(
child: Text(
Languages.of(context)!
.close),
onPressed: () {
Navigator.pop(bc);
},
)
],
));
});
}
})
],
),
onTap: () async {
if (!j.lzeObjednat) {
showDialog(
context: context,
builder: (context) {
return CupertinoAlertDialog(
title:
Text(Languages.of(context)!.errorOrdering),
content:
Text(Languages.of(context)!.cannotOrder),
actions: [
CupertinoButton(
child: Text(Languages.of(context)!.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
} else {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => Dialog(
child: SizedBox(
height: 100,
child: Row(children: [
const Padding(
padding: EdgeInsets.all(10),
child: CircularProgressIndicator(),
),
Text(Languages.of(context)!.ordering)
]),
),
));
widget.canteen.objednat(j).then((_) {
Navigator.of(context, rootNavigator: true).pop();
nactiJidlo();
}).catchError(
(o) {
Navigator.of(context, rootNavigator: true).pop();
showDialog(
context: context,
builder: (bc) => CupertinoAlertDialog(
title:
Text(Languages.of(context)!.errorOrdering),
content: Text(o.toString()),
actions: [
CupertinoButton(
child: Text(Languages.of(context)!.close),
onPressed: () {
Navigator.pop(bc);
},
)
],
),
);
},
);
}
},
onLongPress: () async {
if (!j.objednano || j.burzaUrl == null) return;
if (!j.naBurze) {
// pokud není na burze, radši se zeptáme
var d = await showDialog(
context: context,
builder: (bc) => SimpleDialog(
title: Text(
Languages.of(context)!.verifyExchange),
children: [
SimpleDialogOption(
onPressed: () {
Navigator.pop(bc, true);
},
child: Text(Languages.of(context)!.yes),
),
SimpleDialogOption(
onPressed: () {
Navigator.pop(bc, false);
},
child: Text(Languages.of(context)!.no),
),
],
));
if (d) {
widget.canteen
.doBurzy(j)
.then((_) => nactiJidlo())
.catchError((o) {
showDialog(
context: context,
builder: (bc) => CupertinoAlertDialog(
title: Text(
Languages.of(context)!.exchangeError),
content: Text(o.toString()),
actions: [
CupertinoButton(
child: Text(
Languages.of(context)!.close),
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 IOSLogin()));
}
});
}
Future<void> kliknuti(String value, BuildContext context) async {
if (value == Languages.of(context)!.signOut) {
await showDialog<bool>(
context: context,
builder: (c) => CupertinoAlertDialog(
title: Text(Languages.of(context)!.warning),
content: Text(Languages.of(context)!.signOutWarn),
actions: [
CupertinoButton(
onPressed: () {
const storage = FlutterSecureStorage();
storage.deleteAll();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (c) => const IOSLogin()),
(route) => false);
},
child: Text(Languages.of(context)!.yes)),
CupertinoButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(Languages.of(context)!.no))
],
),
);
} else if (value == Languages.of(context)!.review) {
launchUrl(
Uri.parse("https://apps.apple.com/cz/app/opencanteen/id1621124445"),
mode: LaunchMode.externalApplication);
} else if (value == Languages.of(context)!.reportBugs) {
launchUrl(Uri.parse("https://forms.gle/jKN7QeFJwpaApSbC8"),
mode: LaunchMode.externalApplication);
} else if (value == Languages.of(context)!.about) {
var packageInfo = await PackageInfo.fromPlatform();
if (!mounted) return;
showAboutDialog(
context: context,
applicationName: "OpenCanteen",
applicationLegalese:
"${Languages.of(context)!.copyright}\n${Languages.of(context)!.license}",
applicationVersion: packageInfo.version,
children: [
CupertinoButton(
onPressed: (() => launchUrl(
Uri.parse("https://git.mnau.xyz/hernik/opencanteen"))),
child: Text(Languages.of(context)!.source))
]);
} else if (value == Languages.of(context)!.settings) {
Navigator.push(
context, MaterialPageRoute(builder: (c) => const IOSNastaveni()));
}
}
void nactiNastaveni() async {
var prefs = await SharedPreferences.getInstance();
_skipWeekend = prefs.getBool("skip") ?? false;
if (!mounted) return;
kontrolaTyden(context);
}
void ulozitDoOffline() async {
var prefs = await SharedPreferences.getInstance();
if (prefs.getBool("offline") ?? false) {
// vyčistit offline
Directory appDocDir = await getApplicationDocumentsDirectory();
for (var f in appDocDir.listSync()) {
if (f.path.contains("jidelnicek")) {
f.deleteSync();
}
}
// 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;
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));
}
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
nactiNastaveni();
ulozitDoOffline();
nactiJidlo();
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: drawerGenerator(context, widget.canteen, 1),
appBar: AppBar(
title: Text(Languages.of(context)!.menu),
actions: [
PopupMenuButton(
onSelected: ((String value) => kliknuti(value, context)),
itemBuilder: (BuildContext context) {
return {
Languages.of(context)!.reportBugs,
Languages.of(context)!.review,
Languages.of(context)!.settings,
Languages.of(context)!.about,
Languages.of(context)!.signOut
}.map((String choice) {
return PopupMenuItem<String>(
value: choice,
child: Text(choice),
);
}).toList();
},
),
],
),
body: RefreshIndicator(
onRefresh: nactiJidlo,
child: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width - 45,
child: Column(
children: [
const SizedBox(height: 10),
Text("${Languages.of(context)!.balance}$kredit"),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
IconButton(
onPressed: () {
setState(() {
den = den.subtract(const Duration(days: 1));
if (den.weekday == 7 && _skipWeekend) {
den = den.subtract(const Duration(days: 2));
}
nactiJidlo();
});
},
icon: const Icon(Icons.arrow_left)),
CupertinoButton(
onPressed: () async {
await showCupertinoModalPopup(
context: context,
builder: (c) => Container(
height: 216,
padding: const EdgeInsets.only(top: 6),
margin: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
color: CupertinoColors.systemBackground
.resolveFrom(context),
child: SafeArea(
top: false,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.date,
onDateTimeChanged: (t) {
den = t;
},
),
),
),
);
nactiJidlo();
},
child: Text(
"${den.day}. ${den.month}. ${den.year} - $denTydne")),
IconButton(
onPressed: () {
setState(() {
den = den.add(const Duration(days: 1));
if (den.weekday == 6 && _skipWeekend) {
den = den.add(const Duration(days: 2));
}
nactiJidlo();
});
},
icon: const Icon(Icons.arrow_right),
),
IconButton(
onPressed: () => setState(() {
den = DateTime.now();
nactiJidlo();
}),
icon: const Icon(Icons.today))
]),
SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: GestureDetector(
child: Container(
color: Theme.of(context)
.colorScheme
.onPrimary
.withOpacity(0),
height: MediaQuery.of(context).size.height / 1.3,
child: Column(children: obsah),
),
onHorizontalDragEnd: (details) {
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();
});
}
},
),
)
],
),
),
),
),
);
}
}

View File

@ -1,282 +0,0 @@
import 'dart:io';
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/okna/ios/welcome.dart';
import '../../lang/lang.dart';
import '../../loginmanager.dart';
import '../../main.dart';
import '../../util.dart';
import 'jidelnicek.dart';
import 'offline_jidelnicek.dart';
class IOSLogin extends StatefulWidget {
const IOSLogin({Key? key}) : super(key: key);
@override
State<IOSLogin> createState() => _IOSLoginState();
}
class _IOSLoginState extends State<IOSLogin> {
TextEditingController userControl = TextEditingController();
TextEditingController passControl = TextEditingController();
TextEditingController canteenControl = TextEditingController();
bool rememberMe = false;
bool _showUrl = false;
String dropdownUrl = instance.first["url"] ?? "";
@override
void initState() {
super.initState();
LoginManager.getDetails().then((r) async {
// žádat o oprávnění na iOS
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
if (r != null) {
// Automaticky přihlásit
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => Dialog(
child: SizedBox(
height: 100,
child: Row(children: [
const Padding(
padding: EdgeInsets.all(10),
child: CircularProgressIndicator(),
),
Text(Languages.of(context)!.loggingIn)
]),
),
));
var canteen = Canteen(r["url"]!);
try {
var l = await canteen.login(r["user"]!, r["pass"]!);
if (!l) {
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.loginFailed),
),
);
return;
}
const storage = FlutterSecureStorage();
var odsouhlasil = await storage.read(key: "oc_souhlas");
if (!mounted) return;
if (odsouhlasil == null || odsouhlasil != "ano") {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (c) => IOSWelcome(canteen: canteen),
),
(route) => false);
} else {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => IOSJidelnicek(canteen: canteen),
),
(route) => false);
}
} on PlatformException {
if (!mounted) return;
Navigator.of(context).pop();
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.corrupted),
),
);
} catch (_) {
if (!mounted) return;
Navigator.of(context).pop();
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.errorContacting),
),
);
goOffline();
}
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Languages.of(context)!.logIn),
automaticallyImplyLeading: false,
),
body: Center(
child: SingleChildScrollView(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
Languages.of(context)!.appName,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 40),
),
Text(
Languages.of(context)!.logIn,
textAlign: TextAlign.center,
),
CupertinoTextField(
controller: userControl,
autofillHints: const [AutofillHints.username],
prefix: Text(Languages.of(context)!.username),
),
CupertinoTextField(
autofillHints: const [AutofillHints.password],
prefix: Text(Languages.of(context)!.password),
controller: passControl,
obscureText: true,
),
const SizedBox(
height: 10,
),
DropdownButton(
isExpanded: true,
value: dropdownUrl,
items: instance.map<DropdownMenuItem<String>>((e) {
return DropdownMenuItem<String>(
value: e["url"],
child: Text(e["name"]!),
);
}).toList(),
onChanged: (String? value) {
setState(() {
if (value == "") {
_showUrl = true;
} else {
_showUrl = false;
}
dropdownUrl = value!;
});
},
),
AnimatedOpacity(
opacity: _showUrl ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: TextField(
autofillHints: const [AutofillHints.url],
decoration: InputDecoration(
labelText: Languages.of(context)!.iCanteenUrl),
keyboardType: TextInputType.url,
controller: canteenControl,
),
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
CupertinoSwitch(
value: rememberMe,
onChanged: (value) {
setState(() {
rememberMe = value;
});
}),
Text(Languages.of(context)!.rememberMe)
]),
CupertinoButton(
onPressed: () async {
var canteenUrl = (dropdownUrl == "")
? canteenControl.text
: dropdownUrl;
if (!canteenUrl.startsWith("https://") &&
!canteenUrl.startsWith("http://")) {
canteenUrl = "https://$canteenUrl";
}
var canteen = Canteen(canteenUrl);
try {
var l = await canteen.login(
userControl.text, passControl.text);
if (!l) {
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content:
Text(Languages.of(context)!.loginFailed),
),
);
return;
}
if (rememberMe) {
LoginManager.setDetails(
userControl.text, passControl.text, canteenUrl);
}
// souhlas
const storage = FlutterSecureStorage();
var odsouhlasil =
await storage.read(key: "oc_souhlas");
if (!mounted) return;
if (odsouhlasil == null || odsouhlasil != "ano") {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (c) => IOSWelcome(
canteen: canteen,
)),
(route) => false);
} else {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) =>
IOSJidelnicek(canteen: canteen)),
(route) => false);
}
} on PlatformException {
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.corrupted),
),
);
} on Exception catch (_) {
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content:
Text(Languages.of(context)!.errorContacting),
),
);
goOffline();
}
},
child: Text(Languages.of(context)!.logIn)),
],
),
),
),
));
}
/// Získá offline soubor a zobrazí údaje
void goOffline() async {
if (!mounted) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: ((context) => const IOSOfflineJidelnicek())),
(route) => false);
}
}

View File

@ -1,282 +0,0 @@
import 'dart:io';
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/cupertino.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';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:timezone/timezone.dart' as tz;
import '../../lang/lang.dart';
import '../../loginmanager.dart';
import '../../main.dart';
import '../../util.dart';
class IOSNastaveni extends StatefulWidget {
const IOSNastaveni({Key? key}) : super(key: key);
@override
State<IOSNastaveni> createState() => _IOSNastaveniState();
}
class _IOSNastaveniState extends State<IOSNastaveni> {
bool _ukladatOffline = false;
bool _preskakovatVikend = false;
bool _kontrolovatTyden = false;
bool _oznameniObed = false;
bool _zapamatovany = false;
TimeOfDay _oznameniCas = TimeOfDay.now();
final TextEditingController _countController =
TextEditingController(text: "1");
SharedPreferences? preferences;
void najitNastaveni() async {
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;
_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());
} else {
_oznameniCas = TimeOfDay.fromDateTime(DateTime.parse(casStr));
}
});
}
void zmenitNastaveni(String key, bool value) async {
preferences!.setBool(key, value);
}
@override
void initState() {
super.initState();
najitNastaveni();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Languages.of(context)!.settings),
),
body: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width / 1.1,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(Languages.of(context)!.saveOffline),
CupertinoSwitch(
activeColor: Colors.purple,
value: _ukladatOffline,
onChanged: (value) {
setState(() {
_ukladatOffline = value;
cistit(value);
zmenitNastaveni("offline", value);
});
})
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(Languages.of(context)!.saveCount),
SizedBox(
width: 35,
child: CupertinoTextField(
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: [
Text(Languages.of(context)!.skipWeekend),
CupertinoSwitch(
activeColor: Colors.purple,
value: _preskakovatVikend,
onChanged: (value) {
setState(() {
_preskakovatVikend = value;
zmenitNastaveni("skip", value);
});
})
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(Languages.of(context)!.checkOrdered)),
CupertinoSwitch(
activeColor: Colors.purple,
value: _kontrolovatTyden,
onChanged: (value) {
setState(() {
_kontrolovatTyden = value;
zmenitNastaveni("tyden", value);
});
})
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(Languages.of(context)!.notifyLunch)),
CupertinoSwitch(
activeColor: Colors.purple,
value: _oznameniObed,
onChanged: (value) {
if (!_zapamatovany) {
showDialog(
context: context,
builder: (bc) => CupertinoAlertDialog(
title: Text(Languages.of(context)!.error),
content:
Text(Languages.of(context)!.needRemember),
actions: [
CupertinoButton(
child: Text(Languages.of(context)!.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
} else {
setState(() {
_oznameniObed = value;
if (_oznameniObed) {
showDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title:
Text(Languages.of(context)!.warning),
content: Text(
Languages.of(context)!.notifyWarning),
actions: [
CupertinoButton(
child:
Text(Languages.of(context)!.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
vytvoritOznameni(casNaDate(_oznameniCas));
}
zmenitNastaveni("oznamit", value);
});
}
})
],
),
Text(Languages.of(context)!.notifyAt),
CupertinoButton(
onPressed: () async {
if (_oznameniObed) {
showCupertinoModalPopup(
context: context,
builder: (c) {
return Container(
height: 216,
padding: const EdgeInsets.only(top: 6),
margin: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
color: CupertinoColors.systemBackground
.resolveFrom(context),
child: SafeArea(
top: false,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.time,
onDateTimeChanged: (cas) {
setState(() {
_oznameniCas = TimeOfDay.fromDateTime(cas);
});
},
),
),
);
},
);
}
},
child: Text(
"${(_oznameniCas.hour < 10 ? "0" : "") + _oznameniCas.hour.toString()}:${(_oznameniCas.minute < 10 ? "0" : "") + _oznameniCas.minute.toString()}",
style: TextStyle(
color: (!_oznameniObed) ? Colors.grey : Colors.purple),
),
),
],
),
)),
);
}
void cistit(bool value) async {
if (!value) {
Directory appDocDir = await getApplicationDocumentsDirectory();
for (var f in appDocDir.listSync()) {
// Vymažeme obsah
if (f.path.contains("jidelnicek")) {
f.deleteSync();
}
}
}
}
void vytvoritOznameni(DateTime den) async {
await flutterLocalNotificationsPlugin.cancelAll();
var d = await LoginManager.getDetails(); // získat údaje
if (d != null) {
// Nové oznámení
var c = Canteen(d["url"]!);
if (await c.login(d["user"]!, d["pass"]!)) {
var jidla = await c.jidelnicekDen();
try {
var jidlo = jidla.jidla.singleWhere((element) => element.objednano);
var l =
tz.getLocation(await FlutterNativeTimezone.getLocalTimezone());
if (!mounted) return;
await flutterLocalNotificationsPlugin.zonedSchedule(
// Vytvoří nové oznámení pro daný čas a datum
0,
Languages.of(context)!.lunchNotif,
"${jidlo.varianta} - ${jidlo.nazev}",
tz.TZDateTime.from(den, l),
const NotificationDetails(),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
} on StateError catch (_) {
// nenalezeno
}
}
}
}
}

View File

@ -1,265 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/okna/ios/login.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';
class IOSOfflineJidelnicek extends StatefulWidget {
const IOSOfflineJidelnicek({Key? key}) : super(key: key);
@override
State<IOSOfflineJidelnicek> createState() => _IOSOfflineJidelnicekState();
}
class _IOSOfflineJidelnicekState extends State<IOSOfflineJidelnicek> {
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 {
var jidelnicek = data[jidloIndex];
den = jidelnicek[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 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,
),
),
Text((j.naBurze)
? Languages.of(context)!.inExchange
: "${j.cena}"),
Checkbox(
value: j.objednano,
fillColor: MaterialStateProperty.all(Colors.grey),
onChanged: (v) async {
return;
},
)
],
),
),
),
);
}
setState(() {});
}
void kliknuti(String value, BuildContext context) async {
if (value == Languages.of(context)!.signOut) {
const storage = FlutterSecureStorage();
storage.deleteAll();
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (c) => const IOSLogin()));
} else if (value == Languages.of(context)!.review) {
launchUrl(
Uri.parse("https://apps.apple.com/cz/app/opencanteen/id1621124445"),
mode: LaunchMode.externalApplication);
} else if (value == Languages.of(context)!.reportBugs) {
launchUrl(Uri.parse("https://forms.gle/jKN7QeFJwpaApSbC8"),
mode: LaunchMode.externalApplication);
} else if (value == Languages.of(context)!.about) {
var packageInfo = await PackageInfo.fromPlatform();
if (!mounted) return;
showAboutDialog(
context: context,
applicationName: "OpenCanteen",
applicationLegalese:
"${Languages.of(context)!.copyright}\n${Languages.of(context)!.license}",
applicationVersion: packageInfo.version,
children: [
CupertinoButton(
onPressed: (() => launchUrl(
Uri.parse("https://git.mnau.xyz/hernik/opencanteen"))),
child: Text(Languages.of(context)!.source))
]);
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
nactiNastaveni();
}
void nactiNastaveni() async {
var prefs = await SharedPreferences.getInstance();
_skipWeekend = prefs.getBool("skip") ?? false;
if (!mounted) return;
nactiZeSouboru();
}
@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)!.review,
Languages.of(context)!.about,
Languages.of(context)!.signOut
}.map((String choice) {
return PopupMenuItem<String>(
value: choice,
child: Text(choice),
);
}).toList();
},
),
],
),
body: RefreshIndicator(
child: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
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),
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)),
CupertinoButton(
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(
children: obsah,
),
),
],
),
),
),
onRefresh: () => Navigator.pushReplacement(context,
MaterialPageRoute(builder: ((context) => const IOSLogin()))),
),
);
}
}

591
lib/okna/jidelnicek.dart Normal file
View File

@ -0,0 +1,591 @@
import 'dart:convert';
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/main.dart';
import 'package:opencanteen/okna/login.dart';
import 'package:opencanteen/okna/nastaveni.dart';
import 'package:opencanteen/pw/platformbutton.dart';
import 'package:opencanteen/pw/platformdialog.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 'package:flutter_gen/gen_l10n/app_localizations.dart';
class MealView extends StatefulWidget {
const MealView({super.key, required this.canteen});
final Canteen canteen;
@override
State<MealView> createState() => _MealViewState();
}
class _MealViewState extends State<MealView> {
List<Widget> content = [const CircularProgressIndicator()];
DateTime day = DateTime.now();
String dayOWeek = "";
double balance = 0.0;
void checkWeek(BuildContext context) async {
if (settings.checkOrdered) {
// Check if user has ordered a meal in the next week
var pristi = day.add(const Duration(days: 6));
for (var i = 0; i < 5; i++) {
var jidelnicek = await widget.canteen
.jidelnicekDen(den: pristi.add(Duration(days: i + 1)));
if (jidelnicek.jidla.isNotEmpty &&
!jidelnicek.jidla.any((element) => element.objednano == true)) {
if (!mounted) break;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.noOrder),
duration: const Duration(seconds: 5),
action: SnackBarAction(
onPressed: () => setState(
() {
day = pristi.add(Duration(days: i + 1));
loadMeals();
},
),
label: AppLocalizations.of(context)!.jump,
),
),
);
break;
}
}
}
}
Future<void> loadMeals() async {
content = [const CircularProgressIndicator()];
switch (day.weekday) {
case 2:
dayOWeek = AppLocalizations.of(context)!.tuesday;
break;
case 3:
dayOWeek = AppLocalizations.of(context)!.wednesday;
break;
case 4:
dayOWeek = AppLocalizations.of(context)!.thursday;
break;
case 5:
dayOWeek = AppLocalizations.of(context)!.friday;
break;
case 6:
dayOWeek = AppLocalizations.of(context)!.saturday;
break;
case 7:
dayOWeek = AppLocalizations.of(context)!.sunday;
break;
default:
dayOWeek = AppLocalizations.of(context)!.monday;
}
Uzivatel uzivatel;
try {
uzivatel = await widget.canteen.ziskejUzivatele();
} catch (e) {
if (!widget.canteen.prihlasen && mounted) {
Navigator.pushReplacement(
context, platformRouter((c) => const LoginPage()));
}
return;
}
balance = uzivatel.kredit;
var jd = await widget.canteen.jidelnicekDen(den: day).catchError((_) {
showInfo(context, AppLocalizations.of(context)!.errorContacting);
return Jidelnicek(DateTime.now(), []);
});
setState(
() {
content = [];
if (jd.jidla.isEmpty) {
content.add(Text(
AppLocalizations.of(context)!.noFood,
style: const TextStyle(fontSize: 15),
));
} else {
for (var j in jd.jidla) {
content.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(
(settings.allergens && j.alergeny.isNotEmpty)
? "${j.nazev} (${j.alergeny.map<String>((e) => (e.kod != null) ? e.kod.toString() : e.nazev).join(', ')})"
: j.nazev,
),
),
Text((j.naBurze)
? AppLocalizations.of(context)!.inExchange
: "${j.cena}"),
Checkbox(
value: j.objednano,
side: BorderSide(
width: 2,
color: (j.lzeObjednat)
? (Colors.purple)
: (Colors.grey)),
fillColor: (j.lzeObjednat && j.objednano)
? MaterialStateProperty.all(Colors.purple)
: (j.objednano)
? MaterialStateProperty.all(Colors.grey)
: MaterialStateProperty.all(Colors.transparent),
onChanged: (v) async {
if (!j.lzeObjednat) {
showDialog(
context: context,
builder: (context) {
return PlatformDialog(
title: AppLocalizations.of(context)!
.errorOrdering,
content:
AppLocalizations.of(context)!.cannotOrder,
actions: [
PlatformButton(
text: AppLocalizations.of(context)!.ok,
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
},
);
} else {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => Dialog(
child: SizedBox(
height: 100,
child: Row(
children: [
const Padding(
padding: EdgeInsets.all(10),
child: CircularProgressIndicator(),
),
Text(AppLocalizations.of(context)!
.ordering)
],
),
),
),
);
widget.canteen.objednat(j).then((_) {
Navigator.of(context, rootNavigator: true).pop();
loadMeals();
}).catchError(
(o) {
Navigator.of(context, rootNavigator: true)
.pop();
showDialog(
context: context,
builder: (bc) => PlatformDialog(
title: AppLocalizations.of(context)!
.errorOrdering,
content: o.toString(),
actions: [
PlatformButton(
text:
AppLocalizations.of(context)!.close,
onPressed: () {
Navigator.pop(bc);
},
)
],
),
);
},
);
}
},
)
],
),
onTap: () async {
if (!j.lzeObjednat) {
showDialog(
context: context,
builder: (context) {
return PlatformDialog(
title: AppLocalizations.of(context)!.errorOrdering,
content: AppLocalizations.of(context)!.cannotOrder,
actions: [
PlatformButton(
text: AppLocalizations.of(context)!.ok,
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
},
);
} else {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => Dialog(
child: SizedBox(
height: 100,
child: Row(children: [
const Padding(
padding: EdgeInsets.all(10),
child: CircularProgressIndicator(),
),
Text(AppLocalizations.of(context)!.ordering)
]),
),
),
);
widget.canteen.objednat(j).then((_) {
Navigator.of(context, rootNavigator: true).pop();
loadMeals();
}).catchError(
(o) {
Navigator.of(context, rootNavigator: true).pop();
showDialog(
context: context,
builder: (bc) => PlatformDialog(
title:
AppLocalizations.of(context)!.errorOrdering,
content: o.toString(),
actions: [
PlatformButton(
text: AppLocalizations.of(context)!.close,
onPressed: () {
Navigator.pop(bc);
},
)
],
),
);
},
);
}
},
onLongPress: () async {
if (!j.objednano || j.burzaUrl == null) return;
if (!j.naBurze) {
// if not in exchange, we ask
var d = await showDialog(
context: context,
builder: (bc) => PlatformDialog(
title: AppLocalizations.of(context)!.verifyExchange,
actions: [
PlatformButton(
onPressed: () {
Navigator.pop(bc, true);
},
text: AppLocalizations.of(context)!.yes,
),
PlatformButton(
onPressed: () {
Navigator.pop(bc, false);
},
text: AppLocalizations.of(context)!.no,
),
],
),
);
if (d) {
widget.canteen
.doBurzy(j)
.then((_) => loadMeals())
.catchError((o) {
showDialog(
context: context,
builder: (bc) => PlatformDialog(
title:
AppLocalizations.of(context)!.exchangeError,
content: o.toString(),
actions: [
PlatformButton(
text: AppLocalizations.of(context)!.close,
onPressed: () {
Navigator.pop(bc);
},
)
],
),
);
});
}
} else {
// else no
widget.canteen.doBurzy(j).then((_) => loadMeals());
}
},
),
),
);
}
}
},
);
return;
}
Future<void> click(String value, BuildContext context) async {
if (value == AppLocalizations.of(context)!.signOut) {
await showDialog<bool>(
context: context,
builder: (c) => PlatformDialog(
title: AppLocalizations.of(context)!.warning,
content: AppLocalizations.of(context)!.signOutWarn,
actions: [
PlatformButton(
onPressed: () {
const storage = FlutterSecureStorage();
storage.deleteAll();
Navigator.pushAndRemoveUntil(
context,
platformRouter((c) => const LoginPage()),
(route) => false);
},
text: AppLocalizations.of(context)!.yes),
PlatformButton(
onPressed: () => Navigator.of(context).pop(),
text: AppLocalizations.of(context)!.no,
)
],
),
);
} else if (value == AppLocalizations.of(context)!.review) {
launchUrl(
Uri.parse((Platform.isAndroid)
? "market://details?id=cz.hernikplays.opencanteen"
: "https://apps.apple.com/cz/app/opencanteen/id1621124445"),
mode: LaunchMode.externalApplication);
} else if (value == AppLocalizations.of(context)!.reportBugs) {
launchUrl(Uri.parse("https://forms.gle/jKN7QeFJwpaApSbC8"),
mode: LaunchMode.externalApplication);
} else if (value == AppLocalizations.of(context)!.about) {
var packageInfo = await PackageInfo.fromPlatform();
if (!mounted) return;
showAboutDialog(
context: context,
applicationName: "OpenCanteen",
applicationLegalese:
"${AppLocalizations.of(context)!.copyright}\n${AppLocalizations.of(context)!.license}",
applicationVersion: packageInfo.version,
children: [
PlatformButton(
onPressed: (() => launchUrl(
Uri.parse("https://git.mnau.xyz/hernik/opencanteen"),
mode: LaunchMode.externalApplication)),
text: AppLocalizations.of(context)!.source,
)
]);
} else if (value == AppLocalizations.of(context)!.settings) {
Navigator.push(context, platformRouter((c) => const AndroidNastaveni()));
}
}
void loadSettings() async {
if (!mounted) return;
checkWeek(context);
}
void saveOffline() async {
if (!settings.saveOffline) return;
// clear offline storage
Directory appDocDir = await getApplicationDocumentsDirectory();
for (var f in appDocDir.listSync()) {
if (f.path.contains("jidelnicek")) {
f.deleteSync();
}
}
var prefs = await SharedPreferences.getInstance();
// save X meal lists
var pocet = prefs.getInt("offline_pocet") ?? 1;
if (pocet > 7) pocet = 7;
for (var i = 0; i < pocet; i++) {
var d = day.add(Duration(days: i));
Jidelnicek? j;
try {
j = await widget.canteen.jidelnicekDen(den: d);
} catch (e) {
if (!widget.canteen.prihlasen) {
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(AppLocalizations.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));
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
loadSettings();
saveOffline();
loadMeals();
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: drawerGenerator(context, widget.canteen, 1),
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.menu),
actions: [
PopupMenuButton(
onSelected: ((String value) => click(value, context)),
itemBuilder: (BuildContext context) {
return {
AppLocalizations.of(context)!.reportBugs,
AppLocalizations.of(context)!.review,
AppLocalizations.of(context)!.settings,
AppLocalizations.of(context)!.about,
AppLocalizations.of(context)!.signOut
}.map((String choice) {
return PopupMenuItem<String>(
value: choice,
child: Text(choice),
);
}).toList();
},
),
],
),
body: RefreshIndicator(
onRefresh: loadMeals,
child: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: Column(
children: [
const SizedBox(height: 10),
Text("${AppLocalizations.of(context)!.balance}$balance"),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () {
setState(() {
day = day.subtract(const Duration(days: 1));
if (day.weekday == 7 && settings.skipWeekend) {
day = day.subtract(const Duration(days: 2));
}
loadMeals();
});
},
icon: const Icon(Icons.arrow_left)),
PlatformButton(
onPressed: () async {
var datePicked = await showDatePicker(
context: context,
initialDate: day,
currentDate: day,
firstDate: DateTime(2019, 1, 1),
lastDate: DateTime(day.year + 1, 12, 31),
locale: Localizations.localeOf(context));
if (datePicked == null) return;
setState(() {
day = datePicked;
loadMeals();
});
},
text: "${day.day}. ${day.month}. ${day.year} - $dayOWeek",
),
IconButton(
onPressed: () {
setState(() {
day = day.add(const Duration(days: 1));
if (day.weekday == 6 && settings.skipWeekend) {
day = day.add(const Duration(days: 2));
}
loadMeals();
});
},
icon: const Icon(Icons.arrow_right),
),
Tooltip(
message: AppLocalizations.of(context)!.todayTooltip,
child: IconButton(
onPressed: () => setState(
() {
day = DateTime.now();
loadMeals();
},
),
icon: const Icon(Icons.today),
),
)
],
),
SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: GestureDetector(
child: Container(
padding: const EdgeInsets.only(left: 20, right: 20),
color: Theme.of(context)
.colorScheme
.onPrimary
.withOpacity(0),
height: MediaQuery.of(context).size.height / 1.3,
child: Column(children: content),
),
onHorizontalDragEnd: (details) {
if (details.primaryVelocity?.compareTo(0) == -1) {
setState(() {
day = day.add(const Duration(days: 1));
if (day.weekday == 6 && settings.skipWeekend) {
day = day.add(const Duration(days: 2));
}
loadMeals();
});
} else {
setState(
() {
day = day.subtract(const Duration(days: 1));
if (day.weekday == 7 && settings.skipWeekend) {
day = day.subtract(const Duration(days: 2));
}
loadMeals();
},
);
}
},
),
)
],
),
),
),
),
);
}
}

247
lib/okna/login.dart Normal file
View File

@ -0,0 +1,247 @@
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/okna/welcome.dart';
import 'package:opencanteen/pw/platformbutton.dart';
import 'package:opencanteen/pw/platformfield.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../loginmanager.dart';
import '../../util.dart';
import '../pw/platformswitch.dart';
import 'jidelnicek.dart';
import 'offline_jidelnicek.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
TextEditingController userControl = TextEditingController();
TextEditingController passControl = TextEditingController();
TextEditingController canteenControl = TextEditingController();
bool rememberMe = false;
bool _showUrl = false;
String dropdownUrl = instance.first["url"] ?? "";
@override
void initState() {
super.initState();
LoginManager.getDetails().then((r) async {
if (r != null) {
// Autologin
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => Dialog(
child: SizedBox(
height: 100,
child: Row(children: [
const Padding(
padding: EdgeInsets.all(10),
child: CircularProgressIndicator(),
),
Text(AppLocalizations.of(context)!.loggingIn)
]),
),
));
var canteen = Canteen(r["url"]!);
try {
var l = await canteen.login(r["user"]!, r["pass"]!);
if (!l) {
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.loginFailed),
),
);
return;
}
const storage = FlutterSecureStorage();
var odsouhlasil = await storage.read(key: "oc_souhlas");
if (!mounted) return;
if (odsouhlasil == null || odsouhlasil != "ano") {
Navigator.pushAndRemoveUntil(
context,
platformRouter(
(c) => WelcomePage(canteen: canteen),
),
(route) => false);
} else {
Navigator.pushAndRemoveUntil(
context,
platformRouter(
(context) => MealView(canteen: canteen),
),
(route) => false);
}
} on PlatformException {
if (!mounted) return;
Navigator.of(context).pop();
showInfo(context, AppLocalizations.of(context)!.corrupted);
} catch (_) {
if (!mounted) return;
Navigator.of(context).pop();
showInfo(context, AppLocalizations.of(context)!.errorContacting);
goOffline();
}
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.logIn),
automaticallyImplyLeading: false,
),
body: Center(
child: SingleChildScrollView(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
AppLocalizations.of(context)!.appName,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 40),
),
Text(
AppLocalizations.of(context)!.logIn,
textAlign: TextAlign.center,
),
PlatformField(
controller: userControl,
autofillHints: const [AutofillHints.username],
labelText: AppLocalizations.of(context)!.username,
),
PlatformField(
autofillHints: const [AutofillHints.password],
labelText: AppLocalizations.of(context)!.password,
controller: passControl,
obscureText: true,
),
const SizedBox(
height: 10,
),
DropdownButton(
isExpanded: true,
value: dropdownUrl,
items: instance.map<DropdownMenuItem<String>>((e) {
return DropdownMenuItem<String>(
value: e["url"],
child: Text(e["name"]!),
);
}).toList(),
onChanged: (String? value) {
setState(() {
if (value == "") {
_showUrl = true;
} else {
_showUrl = false;
}
dropdownUrl = value!;
});
},
),
AnimatedOpacity(
opacity: _showUrl ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: PlatformField(
autofillHints: const [AutofillHints.url],
labelText: AppLocalizations.of(context)!.iCanteenUrl,
keyboardType: TextInputType.url,
controller: canteenControl,
),
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
PlatformSwitch(
value: rememberMe,
onChanged: (value) {
setState(() {
rememberMe = value;
});
},
),
Text(AppLocalizations.of(context)!.rememberMe)
]),
PlatformButton(
onPressed: () async {
var canteenUrl = (dropdownUrl == "")
? canteenControl.text
: dropdownUrl;
if (!canteenUrl.startsWith("https://") &&
!canteenUrl.startsWith("http://")) {
canteenUrl = "https://$canteenUrl";
}
var canteen = Canteen(canteenUrl);
try {
var l = await canteen.login(
userControl.text, passControl.text);
if (!l) {
if (!mounted) return;
showInfo(context,
AppLocalizations.of(context)!.loginFailed);
return;
}
if (rememberMe) {
LoginManager.setDetails(
userControl.text, passControl.text, canteenUrl);
}
// souhlas
const storage = FlutterSecureStorage();
var odsouhlasil = await storage.read(key: "oc_souhlas");
if (!mounted) return;
if (odsouhlasil == null || odsouhlasil != "ano") {
Navigator.pushAndRemoveUntil(
context,
platformRouter(
(context) => WelcomePage(
canteen: canteen,
),
),
(route) => false);
} else {
Navigator.pushAndRemoveUntil(
context,
platformRouter(
(context) => MealView(
canteen: canteen,
),
),
(route) => false);
}
} on PlatformException {
if (!mounted) return;
showInfo(
context, AppLocalizations.of(context)!.corrupted);
} on Exception catch (_) {
if (!mounted) return;
showInfo(context,
AppLocalizations.of(context)!.errorContacting);
//goOffline();
}
},
text: AppLocalizations.of(context)!.logIn),
],
),
),
),
),
);
}
/// Switch to offline view
void goOffline() async {
if (!mounted) return;
Navigator.pushAndRemoveUntil(context,
platformRouter((context) => const OfflineMealView()), (route) => false);
}
}

325
lib/okna/nastaveni.dart Normal file
View File

@ -0,0 +1,325 @@
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:opencanteen/pw/platformbutton.dart';
import 'package:opencanteen/pw/platformdialog.dart';
import 'package:opencanteen/pw/platformfield.dart';
import 'package:path_provider/path_provider.dart';
import 'package:settings_ui/settings_ui.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../loginmanager.dart';
import '../../main.dart';
import '../../util.dart';
class AndroidNastaveni extends StatefulWidget {
const AndroidNastaveni({super.key});
@override
State<AndroidNastaveni> createState() => _AndroidNastaveniState();
}
class _AndroidNastaveniState extends State<AndroidNastaveni> {
bool _saveOffline = false;
bool _skipWeekend = false;
bool _checkWeek = false;
bool _notifyMeal = false;
bool _remember = false;
bool _allergens = false;
TimeOfDay _notifTime = TimeOfDay.now();
final TextEditingController _countController =
TextEditingController(text: "1");
SharedPreferences? preferences;
void loadSettings() async {
preferences = await SharedPreferences.getInstance();
_remember = await LoginManager.rememberme();
setState(
() {
_saveOffline = preferences!.getBool("offline") ?? false;
_skipWeekend = preferences!.getBool("skip") ?? false;
_checkWeek = preferences!.getBool("tyden") ?? false;
_notifyMeal = 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();
_notifTime = TimeOfDay.fromDateTime(
DateTime.now().add(const Duration(hours: 1)));
preferences!.setString("oznameni_cas", now.toString());
} else {
_notifTime = TimeOfDay.fromDateTime(DateTime.parse(casStr));
}
},
);
}
void changeSetting(String key, bool value) async {
preferences!.setBool(key, value);
}
@override
void initState() {
super.initState();
loadSettings();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.settings),
),
body: SettingsList(
platform: DevicePlatform.device,
sections: [
SettingsSection(
tiles: [
SettingsTile.switchTile(
initialValue: _allergens,
onToggle: (value) {
_allergens = value;
settings.allergens = value;
changeSetting("allergens", _allergens);
setState(() {});
},
title: Text(AppLocalizations.of(context)!.showAllergens),
),
SettingsTile.switchTile(
initialValue: _saveOffline,
onToggle: (value) {
_saveOffline = value;
settings.saveOffline = value;
changeSetting("offline", value);
setState(() {});
},
title: Text(AppLocalizations.of(context)!.saveOffline),
),
CustomSettingsTile(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.76,
child: Text(
AppLocalizations.of(context)!.saveCount,
softWrap: true,
style: TextStyle(
fontSize: (Platform.isAndroid) ? 18 : 17,
fontWeight:
(Platform.isAndroid) ? FontWeight.w400 : null,
),
),
),
const SizedBox(
width: 10,
),
SizedBox(
width: 35,
child: PlatformField(
controller: _countController,
enabled: _saveOffline,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly
],
onChanged: (c) {
var cislo = int.tryParse(c);
if (cislo != null) {
preferences!.setInt("offline_pocet", cislo);
}
},
),
)
],
),
),
SettingsTile.switchTile(
initialValue: _skipWeekend,
onToggle: (value) {
_skipWeekend = value;
settings.skipWeekend = value;
changeSetting("skip", value);
setState(() {});
},
title: Text(AppLocalizations.of(context)!.skipWeekend),
),
SettingsTile.switchTile(
initialValue: _checkWeek,
onToggle: (value) {
_checkWeek = value;
settings.checkOrdered = value;
changeSetting("tyden", value);
setState(() {});
},
title: Text(AppLocalizations.of(context)!.checkOrdered),
),
],
title: Text(AppLocalizations.of(context)!.settingsExperience),
),
SettingsSection(
tiles: [
SettingsTile.switchTile(
initialValue: _notifyMeal,
enabled: _remember,
onToggle: (value) {
if (!_remember) {
showDialog(
context: context,
builder: (bc) => PlatformDialog(
title: AppLocalizations.of(context)!.error,
content: AppLocalizations.of(context)!.needRemember,
actions: [
PlatformButton(
text: AppLocalizations.of(context)!.ok,
onPressed: () {
Navigator.of(bc).pop();
},
)
],
),
);
} else {
_notifyMeal = value;
if (_notifyMeal) {
showDialog(
context: context,
builder: (context) => PlatformDialog(
title: AppLocalizations.of(context)!.warning,
content: AppLocalizations.of(context)!.notifyWarning,
actions: [
PlatformButton(
text: AppLocalizations.of(context)!.ok,
onPressed: () {
Navigator.of(context).pop();
},
)
],
),
);
createNotif(timeToDate(_notifTime));
}
changeSetting("oznamit", value);
setState(() {});
}
},
title: Text(
AppLocalizations.of(context)!.notifyLunch,
),
),
CustomSettingsTile(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
AppLocalizations.of(context)!.notifyAt,
style: TextStyle(
fontSize: (Platform.isAndroid) ? 18 : 17,
fontWeight:
(Platform.isAndroid) ? FontWeight.w400 : null,
),
),
PlatformButton(
textStyle:
TextStyle(fontSize: (Platform.isAndroid ? 18 : 17)),
text: _notifTime.format(context),
onPressed: () async {
if (!_notifyMeal) return;
var cas = await showTimePicker(
context: context, initialTime: _notifTime);
if (cas == null) return;
var prefs = await SharedPreferences.getInstance();
prefs.setString(
"oznameni_cas",
timeToDate(cas)
.toString()); // aktualizovat vybraný čas
var den = timeToDate(cas);
debugPrint(den.isAfter(DateTime.now()).toString());
if (den.isAfter(DateTime.now())) {
// znovu vytvořit oznámení POUZE když je čas v budoucnosti
createNotif(den);
}
_notifTime = cas;
setState(() {});
},
)
],
),
)
],
title: Text(AppLocalizations.of(context)!.settingsFunctions),
)
],
),
);
}
void clear(bool value) async {
if (!value) {
Directory appDocDir = await getApplicationDocumentsDirectory();
for (var f in appDocDir.listSync()) {
// Vymažeme obsah
if (f.path.contains("jidelnicek")) {
f.deleteSync();
}
}
}
}
void createNotif(DateTime den) async {
await flutterLocalNotificationsPlugin.cancelAll();
var d = await LoginManager.getDetails(); // grab details
if (d != null) {
var c = Canteen(d["url"]!);
if (await c.login(d["user"]!, d["pass"]!)) {
var jidla = await c.jidelnicekDen();
try {
var jidlo = jidla.jidla.singleWhere((element) => element.objednano);
const AndroidNotificationDetails androidSpec =
AndroidNotificationDetails('opencanteen', 'predobjedem',
channelDescription: 'Oznámení o dnešním jídle',
importance: Importance.max,
priority: Priority.high,
ticker: 'today meal');
var l =
tz.getLocation(await FlutterNativeTimezone.getLocalTimezone());
var notifGranted = await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission() ??
true; // request android notification access
var granted = await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.requestExactAlarmsPermission() ??
true; // request android exact alarm permission
if (!granted || !notifGranted) return;
if (!mounted) return;
await flutterLocalNotificationsPlugin.zonedSchedule(
// schedules a notification
0,
AppLocalizations.of(context)!.lunchNotif,
"${jidlo.varianta} - ${jidlo.nazev}",
tz.TZDateTime.from(den, l),
const NotificationDetails(android: androidSpec),
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
} on StateError catch (_) {
// no meal found
}
}
}
}
}

View File

@ -0,0 +1,270 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/okna/login.dart';
import 'package:opencanteen/pw/platformbutton.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 'package:flutter_gen/gen_l10n/app_localizations.dart';
class OfflineMealView extends StatefulWidget {
const OfflineMealView({super.key});
@override
State<OfflineMealView> createState() => _OfflineMealViewState();
}
class _OfflineMealViewState extends State<OfflineMealView> {
List<Widget> content = [const CircularProgressIndicator()]; // view content
var _skipWeekend = false; // skip weekend setting
DateTime currentDay = DateTime.now(); // the day we are supposed to show
String dayOWeek = ""; // the name of the day (to show to user)
List<List<OfflineMeal>> data = []; // meal data
var mealIndex = 0; // index of the currently shown day
/// Loads the offline data from local storage
void loadFromFile() 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<OfflineMeal> jidla = [];
for (var j in r) {
jidla.add(OfflineMeal(
name: j["nazev"],
variant: j["varianta"],
ordered: j["objednano"],
price: j["cena"],
onExchange: j["naBurze"],
day: DateTime.parse(j["den"])));
}
data.add(jidla);
}
}
loadFood();
}
Future<void> loadFood() async {
var jidelnicek = data[mealIndex];
currentDay = jidelnicek[0].day;
switch (currentDay.weekday) {
case 2:
dayOWeek = AppLocalizations.of(context)!.tuesday;
break;
case 3:
dayOWeek = AppLocalizations.of(context)!.wednesday;
break;
case 4:
dayOWeek = AppLocalizations.of(context)!.thursday;
break;
case 5:
dayOWeek = AppLocalizations.of(context)!.friday;
break;
case 6:
dayOWeek = AppLocalizations.of(context)!.saturday;
break;
case 7:
dayOWeek = AppLocalizations.of(context)!.sunday;
break;
default:
dayOWeek = AppLocalizations.of(context)!.monday;
}
content = [];
for (OfflineMeal j in jidelnicek) {
content.add(
Padding(
padding: const EdgeInsets.only(top: 15),
child: InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(j.variant),
const SizedBox(width: 10),
Flexible(
child: Text(
j.name,
),
),
Text((j.onExchange)
? AppLocalizations.of(context)!.inExchange
: "${j.price}"),
Checkbox(
value: j.ordered,
fillColor: MaterialStateProperty.all(Colors.grey),
onChanged: (v) async {
return;
},
)
],
),
),
),
);
}
setState(() {});
}
void click(String value, BuildContext context) async {
if (value == AppLocalizations.of(context)!.signOut) {
const storage = FlutterSecureStorage();
storage.deleteAll();
Navigator.pushReplacement(
context, platformRouter((c) => const LoginPage()));
} else if (value == AppLocalizations.of(context)!.review) {
launchUrl(
Uri.parse((Platform.isAndroid)
? "market://details?id=cz.hernikplays.opencanteen"
: "https://apps.apple.com/cz/app/opencanteen/id1621124445"),
mode: LaunchMode.externalApplication);
} else if (value == AppLocalizations.of(context)!.reportBugs) {
launchUrl(Uri.parse("https://forms.gle/jKN7QeFJwpaApSbC8"),
mode: LaunchMode.externalApplication);
} else if (value == AppLocalizations.of(context)!.about) {
var packageInfo = await PackageInfo.fromPlatform();
if (!mounted) return;
showAboutDialog(
context: context,
applicationName: "OpenCanteen",
applicationLegalese:
"${AppLocalizations.of(context)!.copyright}\n${AppLocalizations.of(context)!.license}",
applicationVersion: packageInfo.version,
children: [
PlatformButton(
onPressed: (() => launchUrl(
Uri.parse("https://git.mnau.xyz/hernik/opencanteen"))),
text: AppLocalizations.of(context)!.source,
)
],
);
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
loadSettings();
}
void loadSettings() async {
var prefs = await SharedPreferences.getInstance();
_skipWeekend = prefs.getBool("skip") ?? false;
if (!mounted) return;
loadFromFile();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.menu),
automaticallyImplyLeading: false,
actions: [
PopupMenuButton(
onSelected: ((String value) => click(value, context)),
itemBuilder: (BuildContext context) {
return {
AppLocalizations.of(context)!.reportBugs,
AppLocalizations.of(context)!.review,
AppLocalizations.of(context)!.about,
AppLocalizations.of(context)!.signOut
}.map((String choice) {
return PopupMenuItem<String>(
value: choice,
child: Text(choice),
);
}).toList();
},
),
],
),
body: RefreshIndicator(
child: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
child: Column(
children: [
const SizedBox(height: 10),
Text(
AppLocalizations.of(context)!.offline,
style: const TextStyle(fontWeight: FontWeight.bold),
),
Text(AppLocalizations.of(context)!.mustLogout),
const SizedBox(height: 10),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
IconButton(
onPressed: () {
if (data.length <= 1) return;
content = [const CircularProgressIndicator()];
setState(() {
if (currentDay.weekday == 1 && _skipWeekend) {
// pokud je pondělí a chceme přeskočit víkend
if (mealIndex - 2 >= 0) {
mealIndex -= data.length - 3;
} else {
mealIndex = data.length - 1;
}
} else if (mealIndex == 0) {
mealIndex = data.length - 1;
} else {
mealIndex -= 1;
}
loadFood();
});
},
icon: const Icon(Icons.arrow_left)),
PlatformButton(
onPressed: () async {},
text:
"${currentDay.day}. ${currentDay.month}. ${currentDay.year} - $dayOWeek"),
IconButton(
onPressed: () {
if (data.length <= 1) return;
content = [const CircularProgressIndicator()];
setState(() {
if (currentDay.weekday == 5 && _skipWeekend) {
// pokud je pondělí a chceme přeskočit víkend
if (mealIndex + 2 <= data.length - 1) {
mealIndex += 2;
} else {
mealIndex = 0;
}
} else if (mealIndex == data.length) {
mealIndex = 0;
} else {
mealIndex += 1;
}
loadFood();
});
},
icon: const Icon(Icons.arrow_right),
),
IconButton(
onPressed: () {
mealIndex = 0;
},
icon: const Icon(Icons.today))
]),
SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
children: content,
),
),
],
),
),
),
onRefresh: () => Navigator.pushReplacement(
context, platformRouter((context) => const LoginPage())),
),
);
}
}

View File

@ -2,56 +2,57 @@ import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:introduction_screen/introduction_screen.dart';
import 'package:opencanteen/lang/lang.dart';
import 'package:opencanteen/okna/ios/jidelnicek.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:opencanteen/okna/jidelnicek.dart';
import 'package:opencanteen/util.dart';
class IOSWelcome extends StatefulWidget {
const IOSWelcome({Key? key, required this.canteen}) : super(key: key);
class WelcomePage extends StatefulWidget {
const WelcomePage({super.key, required this.canteen});
final Canteen canteen;
@override
State<IOSWelcome> createState() => _IOSWelcomeState();
State<WelcomePage> createState() => _WelcomePageState();
}
class _IOSWelcomeState extends State<IOSWelcome> {
class _WelcomePageState extends State<WelcomePage> {
@override
Widget build(BuildContext context) {
var listPagesViewModel = [
PageViewModel(
title: Languages.of(context)!.welcome,
body: Languages.of(context)!.appDesc,
title: AppLocalizations.of(context)!.welcome,
body: AppLocalizations.of(context)!.appDesc,
image: const Center(
child: Icon(Icons.waving_hand_outlined, size: 175),
),
),
PageViewModel(
title: Languages.of(context)!.aboutOrder,
body: Languages.of(context)!.howOrder,
title: AppLocalizations.of(context)!.aboutOrder,
body: AppLocalizations.of(context)!.howOrder,
image: Center(
child: Image.asset('assets/objednavam.png',
width: MediaQuery.of(context).size.width * 0.85),
),
),
PageViewModel(
title: Languages.of(context)!.aboutToExch,
body: Languages.of(context)!.howToExch,
title: AppLocalizations.of(context)!.aboutToExch,
body: AppLocalizations.of(context)!.howToExch,
image: Center(
child: Image.asset('assets/doburzy.png',
width: MediaQuery.of(context).size.width * 0.85),
),
),
PageViewModel(
title: Languages.of(context)!.aboutFromExch,
body: Languages.of(context)!.howFromExch,
title: AppLocalizations.of(context)!.aboutFromExch,
body: AppLocalizations.of(context)!.howFromExch,
image: Center(
child: Image.asset('assets/burza.png',
width: MediaQuery.of(context).size.width * 0.85),
),
),
PageViewModel(
title: Languages.of(context)!.warning,
body: Languages.of(context)!.notOfficial,
title: AppLocalizations.of(context)!.warning,
body: AppLocalizations.of(context)!.notOfficial,
image: const Center(
child: Icon(Icons.warning_amber_outlined, size: 175),
),
@ -60,16 +61,15 @@ class _IOSWelcomeState extends State<IOSWelcome> {
return Scaffold(
body: IntroductionScreen(
pages: listPagesViewModel,
next: Text(Languages.of(context)!.next),
done: Text(Languages.of(context)!.ok,
next: Text(AppLocalizations.of(context)!.next),
done: Text(AppLocalizations.of(context)!.ok,
style: const TextStyle(fontWeight: FontWeight.w600)),
onDone: () async {
const storage = FlutterSecureStorage();
await storage.write(key: "oc_souhlas", value: "ano");
if (!mounted) return;
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (c) => IOSJidelnicek(canteen: widget.canteen)),
platformRouter((c) => MealView(canteen: widget.canteen)),
(route) => false);
},
),

View File

@ -0,0 +1,27 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:opencanteen/pw/platformwidget.dart';
class PlatformButton extends PlatformWidget<TextButton, CupertinoButton> {
final String text;
final void Function()? onPressed;
final TextStyle? textStyle;
const PlatformButton(
{super.key, required this.text, required this.onPressed, this.textStyle});
@override
TextButton createAndroidWidget(BuildContext context) => TextButton(
onPressed: onPressed,
child: Text(
text,
style: textStyle,
),
);
@override
CupertinoButton createIosWidget(BuildContext context) => CupertinoButton(
onPressed: onPressed,
child: Text(text, style: textStyle),
);
}

View File

@ -0,0 +1,30 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:opencanteen/pw/platformwidget.dart';
class PlatformDialog extends PlatformWidget<AlertDialog, CupertinoAlertDialog> {
final String title;
final String? content;
final List<Widget> actions;
const PlatformDialog(
{super.key, required this.title, this.content, this.actions = const []});
@override
AlertDialog createAndroidWidget(BuildContext context) => AlertDialog(
title: Text(title),
content: (content != null)
? SingleChildScrollView(child: Text(content!))
: null,
actions: actions,
);
@override
CupertinoAlertDialog createIosWidget(BuildContext context) =>
CupertinoAlertDialog(
title: Text(title),
content: (content != null)
? SingleChildScrollView(child: Text(content!))
: null,
actions: actions,
);
}

54
lib/pw/platformfield.dart Normal file
View File

@ -0,0 +1,54 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:opencanteen/pw/platformwidget.dart';
class PlatformField extends PlatformWidget<TextField, CupertinoTextField> {
final TextEditingController? controller;
final bool enabled;
final bool obscureText;
final String? labelText;
final bool autocorrect;
final TextInputType? keyboardType;
final List<TextInputFormatter> inputFormatters;
final void Function(String)? onChanged;
final List<String>? autofillHints;
const PlatformField({
super.key,
this.controller,
this.labelText,
this.obscureText = false,
this.autocorrect = false,
this.keyboardType,
this.inputFormatters = const [],
this.onChanged,
this.enabled = true,
this.autofillHints,
});
@override
TextField createAndroidWidget(BuildContext context) => TextField(
controller: controller,
enabled: enabled,
obscureText: obscureText,
decoration: InputDecoration(labelText: labelText),
autocorrect: autocorrect,
keyboardType: keyboardType,
inputFormatters: inputFormatters,
onChanged: onChanged,
autofillHints: autofillHints,
);
@override
CupertinoTextField createIosWidget(BuildContext context) =>
CupertinoTextField(
controller: controller,
enabled: enabled,
obscureText: obscureText,
prefix: (labelText == null) ? null : Text(labelText!),
autocorrect: autocorrect,
keyboardType: keyboardType,
inputFormatters: inputFormatters,
onChanged: onChanged,
);
}

View File

@ -0,0 +1,28 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:opencanteen/pw/platformwidget.dart';
class PlatformSwitch extends PlatformWidget<Switch, CupertinoSwitch> {
final bool value;
final void Function(bool)? onChanged;
final Color? thumbColor;
const PlatformSwitch(
{super.key,
required this.value,
required this.onChanged,
this.thumbColor});
@override
Switch createAndroidWidget(BuildContext context) => Switch(
value: value,
onChanged: onChanged,
thumbColor: MaterialStateProperty.all(thumbColor),
);
@override
CupertinoSwitch createIosWidget(BuildContext context) => CupertinoSwitch(
value: value,
onChanged: onChanged,
thumbColor: thumbColor,
);
}

View File

@ -0,0 +1,22 @@
import 'dart:io';
import 'package:flutter/material.dart';
/// Abstract class used to create widgets for the respective platform UI library
abstract class PlatformWidget<A extends Widget, I extends Widget>
extends StatelessWidget {
const PlatformWidget({super.key});
@override
Widget build(BuildContext context) {
if (Platform.isAndroid) {
return createAndroidWidget(context);
} else {
return createIosWidget(context);
}
}
A createAndroidWidget(BuildContext context);
I createIosWidget(BuildContext context);
}

View File

@ -1,12 +1,12 @@
import 'dart:io';
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:opencanteen/okna/android/burza.dart';
import 'package:opencanteen/okna/android/jidelnicek.dart';
import 'package:opencanteen/okna/ios/burza.dart';
import 'package:opencanteen/okna/ios/jidelnicek.dart';
import 'lang/lang.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:opencanteen/okna/burza.dart';
import 'package:opencanteen/okna/jidelnicek.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
Drawer drawerGenerator(BuildContext context, Canteen canteen, int p) {
Drawer drawer = const Drawer();
@ -17,24 +17,20 @@ Drawer drawerGenerator(BuildContext context, Canteen canteen, int p) {
child: ListView(
children: [
DrawerHeader(
child: Text(Languages.of(context)!.appName),
child: Text(AppLocalizations.of(context)!.appName),
),
ListTile(
selected: true,
title: Text(Languages.of(context)!.home),
title: Text(AppLocalizations.of(context)!.home),
leading: const Icon(Icons.home),
onTap: () => Navigator.pop(context),
),
ListTile(
leading: const Icon(Icons.store),
title: Text(Languages.of(context)!.exchange),
title: Text(AppLocalizations.of(context)!.exchange),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => (Platform.isAndroid)
? AndroidBurza(canteen: canteen)
: IOSBurza(canteen: canteen),
),
platformRouter((context) => BurzaView(canteen: canteen)),
),
),
],
@ -47,22 +43,20 @@ Drawer drawerGenerator(BuildContext context, Canteen canteen, int p) {
child: ListView(
children: [
DrawerHeader(
child: Text(Languages.of(context)!.appName),
child: Text(AppLocalizations.of(context)!.appName),
),
ListTile(
leading: const Icon(Icons.home),
title: Text(Languages.of(context)!.home),
title: Text(AppLocalizations.of(context)!.home),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (c) => (Platform.isAndroid)
? AndroidJidelnicek(canteen: canteen)
: IOSJidelnicek(canteen: canteen))),
context,
platformRouter((c) => MealView(canteen: canteen)),
),
),
ListTile(
leading: const Icon(Icons.store),
selected: true,
title: Text(Languages.of(context)!.exchange),
title: Text(AppLocalizations.of(context)!.exchange),
onTap: () => Navigator.pop(context),
),
],
@ -72,31 +66,67 @@ Drawer drawerGenerator(BuildContext context, Canteen canteen, int p) {
return drawer;
}
class OfflineJidlo {
String nazev;
String varianta;
bool objednano;
double cena;
bool naBurze;
DateTime den;
class OfflineMeal {
String name;
String variant;
bool ordered;
double price;
bool onExchange;
DateTime day;
OfflineJidlo(
{required this.nazev,
required this.varianta,
required this.objednano,
required this.cena,
required this.naBurze,
required this.den});
OfflineMeal(
{required this.name,
required this.variant,
required this.ordered,
required this.price,
required this.onExchange,
required this.day});
}
/// Vytvoří [DateTime] z [TimeOfDay]
DateTime casNaDate(TimeOfDay c) {
/// Parses [DateTime] from [TimeOfDay]
DateTime timeToDate(TimeOfDay c) {
var now = DateTime.now();
return DateTime.parse(
"${now.year}-${(now.month < 10 ? "0" : "") + now.month.toString()}-${(now.day < 10 ? "0" : "") + now.day.toString()} ${(c.hour < 10 ? "0" : "") + c.hour.toString()}:${(c.minute < 10 ? "0" : "") + c.minute.toString()}:00");
}
/// List of instances to be used in the dropdown menu
List<Map<String, String>> instance = [
{"name": "SŠTE Brno, Olomoucká", "url": "https://stravovani.sstebrno.cz"},
{"name": "Jiné", "url": ""}
];
/// Used to display either a toas or a snackbar
void showInfo(BuildContext context, String message) {
if (Platform.isAndroid) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
duration: const Duration(seconds: 4),
),
);
} else {
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 3,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0,
);
}
}
Route platformRouter(Widget Function(BuildContext context) builder) =>
(Platform.isAndroid)
? MaterialPageRoute(builder: builder)
: CupertinoPageRoute(builder: builder);
class SettingsManager {
bool skipWeekend = false;
bool checkOrdered = false;
bool saveOffline = false;
bool allergens = false;
}

View File

@ -0,0 +1 @@
- Opravit chybu s přidáváním jídla na burzu

View File

@ -0,0 +1,3 @@
- Aktualizovány knihovny
- Vylepšen kódový základ
- Přidána podpora Material 3

View File

@ -0,0 +1 @@
- Fix issue with adding food to the exchange

View File

@ -0,0 +1,3 @@
- Updated libraries
- Polished codebase
- Added Material 3 support

View File

@ -1,12 +0,0 @@
{
"devDependencies": {
"@commitlint/cli": "^17.0.0",
"@commitlint/config-conventional": "^17.0.0",
"cz-conventional-changelog": "^3.3.0"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -5,147 +5,223 @@ packages:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
url: "https://pub.dev"
source: hosted
version: "3.3.4"
version: "3.4.9"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev"
source: hosted
version: "2.3.1"
version: "2.4.2"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.10.0"
version: "2.11.0"
canteenlib:
dependency: "direct main"
description:
name: canteenlib
url: "https://pub.dartlang.org"
sha256: "7051fd7ad1b2e4e5471b7f55dd092a6586972aad2c0c8f31edf7f0ee5a7e4ee9"
url: "https://pub.dev"
source: hosted
version: "1.1.0-alpha.1"
version: "3.0.6"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
version: "1.3.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "2.0.3"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7
url: "https://pub.dev"
source: hosted
version: "0.3.5"
version: "0.4.0"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.16.0"
version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.0.3"
csslib:
dependency: transitive
description:
name: csslib
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
dbus:
dependency: transitive
description:
name: dbus
url: "https://pub.dartlang.org"
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
url: "https://pub.dev"
source: hosted
version: "0.7.8"
version: "0.7.10"
dots_indicator:
dependency: transitive
description:
name: dots_indicator
url: "https://pub.dartlang.org"
sha256: f1599baa429936ba87f06ae5f2adc920a367b16d08f74db58c3d0f6e93bcdb5c
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.2"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "2.1.0"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
version: "7.0.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_keyboard_visibility:
dependency: transitive
description:
name: flutter_keyboard_visibility
sha256: "4983655c26ab5b959252ee204c2fffa4afeb4413cd030455194ec0caa3b8e7cb"
url: "https://pub.dev"
source: hosted
version: "5.4.1"
flutter_keyboard_visibility_linux:
dependency: transitive
description:
name: flutter_keyboard_visibility_linux
sha256: "6fba7cd9bb033b6ddd8c2beb4c99ad02d728f1e6e6d9b9446667398b2ac39f08"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
flutter_keyboard_visibility_macos:
dependency: transitive
description:
name: flutter_keyboard_visibility_macos
sha256: c5c49b16fff453dfdafdc16f26bdd8fb8d55812a1d50b0ce25fc8d9f2e53d086
url: "https://pub.dev"
source: hosted
version: "1.0.0"
flutter_keyboard_visibility_platform_interface:
dependency: transitive
description:
name: flutter_keyboard_visibility_platform_interface
sha256: e43a89845873f7be10cb3884345ceb9aebf00a659f479d1c8f4293fcb37022a4
url: "https://pub.dev"
source: hosted
version: "2.0.0"
flutter_keyboard_visibility_web:
dependency: transitive
description:
name: flutter_keyboard_visibility_web
sha256: d3771a2e752880c79203f8d80658401d0c998e4183edca05a149f5098ce6e3d1
url: "https://pub.dev"
source: hosted
version: "2.0.0"
flutter_keyboard_visibility_windows:
dependency: transitive
description:
name: flutter_keyboard_visibility_windows
sha256: fc4b0f0b6be9b93ae527f3d527fb56ee2d918cd88bbca438c478af7bcfd0ef73
url: "https://pub.dev"
source: hosted
version: "1.0.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
url: "https://pub.dartlang.org"
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
url: "https://pub.dev"
source: hosted
version: "0.11.0"
version: "0.13.1"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
url: "https://pub.dartlang.org"
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.1"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
url: "https://pub.dartlang.org"
sha256: bb5cd63ff7c91d6efe452e41d0d0ae6348925c82eafd10ce170ef585ea04776e
url: "https://pub.dev"
source: hosted
version: "12.0.3+1"
version: "16.2.0"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
url: "https://pub.dartlang.org"
sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "4.0.0+1"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
url: "https://pub.dartlang.org"
sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef"
url: "https://pub.dev"
source: hosted
version: "6.0.0"
version: "7.0.0+1"
flutter_localizations:
dependency: "direct main"
description: flutter
@ -155,315 +231,327 @@ packages:
dependency: "direct main"
description:
name: flutter_native_timezone
url: "https://pub.dartlang.org"
sha256: ed7bfb982f036243de1c068e269182a877100c994f05143c8b26a325e28c1b02
url: "https://pub.dev"
source: hosted
version: "2.0.0"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
url: "https://pub.dartlang.org"
sha256: ffdbb60130e4665d2af814a0267c481bcf522c41ae2e43caf69fa0146876d685
url: "https://pub.dev"
source: hosted
version: "5.0.2"
version: "9.0.0"
flutter_secure_storage_linux:
dependency: transitive
description:
name: flutter_secure_storage_linux
url: "https://pub.dartlang.org"
sha256: "3d5032e314774ee0e1a7d0a9f5e2793486f0dff2dd9ef5a23f4e3fb2a0ae6a9e"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.2.0"
flutter_secure_storage_macos:
dependency: transitive
description:
name: flutter_secure_storage_macos
url: "https://pub.dartlang.org"
sha256: bd33935b4b628abd0b86c8ca20655c5b36275c3a3f5194769a7b3f37c905369c
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "3.0.1"
flutter_secure_storage_platform_interface:
dependency: transitive
description:
name: flutter_secure_storage_platform_interface
url: "https://pub.dartlang.org"
sha256: "0d4d3a5dd4db28c96ae414d7ba3b8422fd735a8255642774803b2532c9a61d7e"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "1.0.2"
flutter_secure_storage_web:
dependency: transitive
description:
name: flutter_secure_storage_web
url: "https://pub.dartlang.org"
sha256: "30f84f102df9dcdaa2241866a958c2ec976902ebdaa8883fbfe525f1f2f3cf20"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
version: "1.1.2"
flutter_secure_storage_windows:
dependency: transitive
description:
name: flutter_secure_storage_windows
url: "https://pub.dartlang.org"
sha256: "5809c66f9dd3b4b93b0a6e2e8561539405322ee767ac2f64d084e2ab5429d108"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
version: "3.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
fluttertoast:
dependency: "direct main"
description:
name: fluttertoast
sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1
url: "https://pub.dev"
source: hosted
version: "8.2.4"
html:
dependency: transitive
description:
name: html
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
url: "https://pub.dev"
source: hosted
version: "0.15.4"
http:
dependency: transitive
description:
name: http
url: "https://pub.dartlang.org"
sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
url: "https://pub.dev"
source: hosted
version: "0.13.5"
version: "1.1.2"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271"
url: "https://pub.dev"
source: hosted
version: "3.2.2"
version: "4.1.3"
intl:
dependency: "direct main"
description:
name: intl
url: "https://pub.dartlang.org"
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
url: "https://pub.dev"
source: hosted
version: "0.17.0"
version: "0.18.1"
introduction_screen:
dependency: "direct main"
description:
name: introduction_screen
url: "https://pub.dartlang.org"
sha256: "72d25ceb71471773783f72783608e17585af93d4bc6474df577fcfe9e7842852"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.1.12"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.4"
version: "0.6.7"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
url: "https://pub.dev"
source: hosted
version: "4.7.0"
version: "4.8.1"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.0"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
url: "https://pub.dev"
source: hosted
version: "0.1.5"
version: "0.5.0"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev"
source: hosted
version: "1.8.0"
version: "1.10.0"
package_info_plus:
dependency: "direct main"
description:
name: package_info_plus
url: "https://pub.dartlang.org"
sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79"
url: "https://pub.dev"
source: hosted
version: "1.4.3+1"
package_info_plus_linux:
dependency: transitive
description:
name: package_info_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
package_info_plus_macos:
dependency: transitive
description:
name: package_info_plus_macos
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "5.0.1"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
url: "https://pub.dartlang.org"
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
package_info_plus_web:
dependency: transitive
description:
name: package_info_plus_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.6"
package_info_plus_windows:
dependency: transitive
description:
name: package_info_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.0.1"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
url: "https://pub.dev"
source: hosted
version: "1.8.2"
version: "1.8.3"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
url: "https://pub.dev"
source: hosted
version: "2.0.11"
version: "2.1.1"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
url: "https://pub.dev"
source: hosted
version: "2.0.21"
path_provider_ios:
version: "2.2.1"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.dartlang.org"
name: path_provider_foundation
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
url: "https://pub.dev"
source: hosted
version: "2.0.11"
version: "2.3.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.1.7"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6"
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
url: "https://pub.dev"
source: hosted
version: "2.0.5"
version: "2.1.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
version: "2.2.1"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6
url: "https://pub.dev"
source: hosted
version: "5.1.0"
version: "6.0.1"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.3"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
url: "https://pub.dev"
source: hosted
version: "2.1.3"
version: "2.1.7"
pointycastle:
dependency: transitive
description:
name: pointycastle
url: "https://pub.dartlang.org"
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
url: "https://pub.dev"
source: hosted
version: "3.6.2"
process:
dependency: transitive
version: "3.7.3"
settings_ui:
dependency: "direct main"
description:
name: process
url: "https://pub.dartlang.org"
name: settings_ui
sha256: d9838037cb554b24b4218b2d07666fbada3478882edefae375ee892b6c820ef3
url: "https://pub.dev"
source: hosted
version: "4.2.4"
version: "2.0.2"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
url: "https://pub.dev"
source: hosted
version: "2.0.15"
version: "2.2.2"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
url: "https://pub.dartlang.org"
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
url: "https://pub.dev"
source: hosted
version: "2.0.14"
shared_preferences_ios:
version: "2.2.1"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_ios
url: "https://pub.dartlang.org"
name: shared_preferences_foundation
sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.3.4"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
version: "2.3.2"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
url: "https://pub.dartlang.org"
sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.3.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
url: "https://pub.dartlang.org"
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
version: "2.2.2"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.3.2"
sky_engine:
dependency: transitive
description: flutter
@ -473,128 +561,154 @@ packages:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
version: "1.10.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
timezone:
dependency: "direct main"
description:
name: timezone
url: "https://pub.dartlang.org"
sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0"
url: "https://pub.dev"
source: hosted
version: "0.9.0"
version: "0.9.2"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.3.2"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
url: "https://pub.dartlang.org"
sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba
url: "https://pub.dev"
source: hosted
version: "6.1.6"
version: "6.2.1"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
url: "https://pub.dartlang.org"
sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def"
url: "https://pub.dev"
source: hosted
version: "6.0.21"
version: "6.2.0"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
url: "https://pub.dartlang.org"
sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3
url: "https://pub.dev"
source: hosted
version: "6.0.17"
version: "6.2.1"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
url: "https://pub.dartlang.org"
sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.1.0"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
url: "https://pub.dartlang.org"
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.1.0"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
url: "https://pub.dartlang.org"
sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.2.0"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
url: "https://pub.dartlang.org"
sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7"
url: "https://pub.dev"
source: hosted
version: "2.0.13"
version: "2.2.1"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
url: "https://pub.dartlang.org"
sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.1.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.4"
web:
dependency: transitive
description:
name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev"
source: hosted
version: "0.3.0"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
version: "5.1.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
url: "https://pub.dev"
source: hosted
version: "0.2.0+2"
version: "1.0.3"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556
url: "https://pub.dev"
source: hosted
version: "6.1.0"
version: "6.4.2"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
version: "3.1.2"
sdks:
dart: ">=2.18.0 <3.0.0"
flutter: ">=3.0.0"
dart: ">=3.2.0 <4.0.0"
flutter: ">=3.16.0"

View File

@ -6,31 +6,33 @@ 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.6.0+25
version: 1.10.0+32
environment:
sdk: ">=2.16.1 <3.0.0"
sdk: ">=2.18.2 <3.0.0"
dependencies:
canteenlib: ^3.0.0
flutter:
sdk: flutter
flutter_localizations:
flutter_local_notifications: ^16.0.0
flutter_localizations:
sdk: flutter
canteenlib: ^1.1.0-alpha.1
flutter_secure_storage: 5.0.2
url_launcher: ^6.0.20
path_provider: ^2.0.9
shared_preferences: ^2.0.13
introduction_screen: ^3.0.1
flutter_local_notifications: ^12.0.3+1
timezone: ^0.9.0
flutter_native_timezone: ^2.0.0
intl: ^0.17.0
package_info_plus: ^1.4.3+1
flutter_secure_storage: ^9.0.0
fluttertoast: ^8.1.2
intl: ^0.18.0
introduction_screen: ^3.0.1
package_info_plus: ^5.0.0
path_provider: ^2.0.9
settings_ui: ^2.0.2
shared_preferences: ^2.0.13
timezone: ^0.9.0
url_launcher: ^6.0.20
dev_dependencies:
flutter_lints: ^2.0.1
flutter_launcher_icons: ^0.11.0
flutter_launcher_icons: ^0.13.0
flutter_lints: ^3.0.0
flutter_icons:
android: true
@ -41,14 +43,13 @@ flutter_icons:
flutter:
uses-material-design: true
generate: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a

8
renovate.json Normal file
View File

@ -0,0 +1,8 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"baseBranches": ["dev"],
"enabledManagers":["pub"]
}