Compare commits

...

50 commits

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
Matyáš Caras ca3d0a81c6 Merge pull request 'feat: opravit iOS bílou obrazovku a upravit UI' (#32) from ios-ui into main
Reviewed-on: #32
2022-12-12 17:36:56 +00:00
Matyáš Caras e9173a6acd fix: optimalizovat a upravit barvy 2022-12-12 17:52:33 +01:00
Matyáš Caras 365976a853 feat: opravit a rozdelit iOS 2022-12-12 16:48:19 +01:00
Matyáš Caras dfb5a091aa docs: odstranit FOSSA odznak a přidat odkazy na přispění 2022-12-08 20:20:51 +00:00
Matyáš Caras c2ea5a1a11
fix: podpora pro apk split + aktualizace knihovny
Merge pull request #35 from hernikplays/split
2022-12-08 20:34:22 +01:00
Matyáš Caras ff5ec1b7de fix: podpora pro apk split + aktualizace knihovny 2022-12-08 20:30:37 +01:00
Matyáš Caras 0dfb3c1f6a
feat: vylepšit offline užívání
Merge pull request #34 from hernikplays/lepsi-offline
2022-11-21 20:30:55 +01:00
Matyáš Caras c913e3bb95 feat: umožnit ukládat více dnů offline 2022-11-21 20:26:55 +01:00
Matyáš Caras db84284eca chore: přesunout screenshoty do správné složky 2022-11-21 20:26:37 +01:00
Matyáš Caras 881e8d7afd chore(flutter): přejít na flutter wrapper 2022-11-21 17:27:09 +01:00
Matyáš Caras e1a7de0400 chore: update gradle 2022-11-17 16:33:13 +01:00
Matyáš Caras dcc681078c
chore(metadata): vytvořit F-Droid metadata
Merge pull request #32 from hernikplays/fdroid
2022-11-17 14:38:07 +01:00
Matyáš Caras 6f73d000c4 chore(metadata): vytvořit F-Droid metadata 2022-11-17 13:59:34 +01:00
Matyáš Caras 799887eae3
fix: vylepšit podporu pro android 13
Merge pull request #31 from hernikplays/a13
2022-11-17 12:26:05 +01:00
Matyáš Caras ae5984eb4c fix: vylepšit podporu pro android 13
Adaptivní + themed ikona, zeptání na oprávnění oznámení
2022-11-17 12:19:38 +01:00
99 changed files with 2485 additions and 3975 deletions

View file

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

1
.flutter Submodule

@ -0,0 +1 @@
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."

4
.gitmodules vendored Normal file
View file

@ -0,0 +1,4 @@
[submodule ".flutter"]
path = .flutter
url = https://github.com/flutter/flutter.git
branch = stable

View file

@ -1,10 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
# This file should be version controlled.
version:
revision: 097d3313d8e2c7f901932d63e537c1acefb87800
revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
- platform: android
create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
- platform: ios
create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
- platform: linux
create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
- platform: macos
create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
- platform: web
create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
- platform: windows
create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

7
.vscode/settings.json vendored Normal file
View file

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

View file

@ -1,3 +1,42 @@
# 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
- upravit vzhled
# 1.5.1
- aktualizovat knihovnu canteenlib
- přidat podporu pro splitování APK podle ABI
# 1.5.0
- umožnit ukládat více dnů offline
- chyba při ukládání offline vás nyní již nevyhodí ale zobrazí pouze zprávu
- "Přihlašování" pop-up zmizí, když není přihlášení úspěšné
# 1.4.2
- aktualizace knihovny flutter_local_notifications
- lepší podpora pro Android 13
- změna na adaptivní ikony na Androidu
# 1.4.1
- aktualizovat knihovnu canteenlib
- změnit odkaz na odeslání zpětné vazby
@ -49,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

@ -2,7 +2,9 @@
Open-Source **neoficiální** aplikace pro přístup do iCanteen
[![wakatime](https://wakatime.com/badge/user/17178fab-a33c-430f-a764-7b3f26c7b966/project/e3ff9994-0026-4041-a529-1cb2041bdf4b.svg)](https://wakatime.com/badge/user/17178fab-a33c-430f-a764-7b3f26c7b966/project/e3ff9994-0026-4041-a529-1cb2041bdf4b)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fhernikplays%2Fopencanteen.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fhernikplays%2Fopencanteen?ref=badge_shield) [![Codemagic build status](https://api.codemagic.io/apps/62863e4c96304ce0518a1694/62863e4c96304ce0518a1693/status_badge.svg)](https://codemagic.io/apps/62863e4c96304ce0518a1694/62863e4c96304ce0518a1693/latest_build) [![Commit Style: Conventional Commits](https://img.shields.io/badge/commit%20style-conventional%20commits-pink)](https://www.conventionalcommits.org/en/v1.0.0/)
[![Codemagic build status](https://api.codemagic.io/apps/62863e4c96304ce0518a1694/62863e4c96304ce0518a1693/status_badge.svg)](https://codemagic.io/apps/62863e4c96304ce0518a1694/62863e4c96304ce0518a1693/latest_build) [![Commit Style: Conventional Commits](https://img.shields.io/badge/commit%20style-conventional%20commits-pink)](https://www.conventionalcommits.org/en/v1.0.0/)
### Upstream repozitář je nyní na adrese [https://git.mnau.xyz/hernik/opencanteen](https://git.mnau.xyz/hernik/opencanteen), všechny problémy hlaste tam.
## Co umí
Aplikace vás přihlásí do vaší iCanteen ***pokud ji podporuje [canteenlib](https://github.com/hernikplays/canteenlib/blob/main/COMPATIBILITY.md), jinak experimentálně***, a umožní vám zobrazit, objednávat obědy, objednávat nebo přidávat jídlo na burzu.
@ -25,9 +27,23 @@ Jednoduchý návod k použití naleznete [zde](https://github.com/hernikplays/op
<img src="https://repobeats.axiom.co/api/embed/ce91f6018ce5523dc8d655df771f4f504c2c6664.svg" alt="repobeats stats">
</div>
## Licence
[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B12838%2Fgithub.com%2Fhernikplays%2Fopencanteen.svg?type=large)](https://app.fossa.com/projects/custom%2B12838%2Fgithub.com%2Fhernikplays%2Fopencanteen?ref=badge_large)
## Přispět
Přispět na vývoj můžete skrz
- 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é**
Tento program je svobodný software. Kde není uvedeno jinak je tento program a zdrojový kód šířen pod licencí GNU General Public License verze 3 nebo pozdější. Všechny detaily licence najdete [zde](https://github.com/hernikplays/opencanteen/blob/main/LICENSE)

View file

@ -31,9 +31,12 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
}
android {
compileSdkVersion flutter.compileSdkVersion
compileSdkVersion 33
compileOptions {
// Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true
// Sets Java compatibility to Java 8
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
@ -48,8 +51,8 @@ android {
defaultConfig {
applicationId "cz.hernikplays.opencanteen"
minSdkVersion 18
targetSdkVersion flutter.targetSdkVersion
minSdkVersion 21
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
@ -78,4 +81,16 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:multidex:1.0.3'
implementation 'androidx.appcompat:appcompat:1.3.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
}
ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3]
import com.android.build.OutputFile
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
if (abiVersionCode != null) {
output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
}
}
}

View file

@ -1,10 +1,11 @@
<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}"
android:icon="@mipmap/launcher_icon"
android:icon="@mipmap/ic_launcher"
android:allowBackup="false"
android:fullBackupContent="false">
<activity

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#4f4685</color>
</resources>

View file

@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.9.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View file

@ -1,6 +1,5 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip

BIN
assets/fore.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View file

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

113
flutterw Executable file
View file

@ -0,0 +1,113 @@
#!/usr/bin/env sh
##############################################################################
##
## Flutter start up script for UN*X
## Version: v1.3.1
## Date: 2022-11-21 17:25:38
##
## Use this flutter wrapper to bundle Flutter within your project to make
## sure everybody builds with the same version.
##
## Read about the install and uninstall process in the README on GitHub
## https://github.com/passsy/flutter_wrapper
##
## Inspired by gradle-wrapper.
##
##############################################################################
echoerr() { echo "$@" 1>&2; }
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ]; do
ls=$(ls -ld "$PRG")
link=$(expr "$ls" : '.*-> \(.*\)$')
if expr "$link" : '/.*' >/dev/null; then
PRG="$link"
else
PRG=$(dirname "$PRG")"/$link"
fi
done
SAVED="$(pwd)"
cd "$(dirname "$PRG")/" >/dev/null
APP_HOME="$(pwd -P)"
cd "$SAVED" >/dev/null
FLUTTER_SUBMODULE_NAME='.flutter'
GIT_HOME=$(git -C "${APP_HOME}" rev-parse --show-toplevel)
FLUTTER_DIR="${GIT_HOME}/${FLUTTER_SUBMODULE_NAME}"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
# Fix not initialized flutter submodule
if [ ! -f "${FLUTTER_DIR}/bin/flutter" ]; then
echoerr "$FLUTTER_SUBMODULE_NAME submodule not initialized. Initializing..."
git submodule update --init "${FLUTTER_DIR}"
fi
# Detect detach HEAD and fix it. commands like upgrade expect a valid branch, not a detached HEAD
FLUTTER_SYMBOLIC_REF=$(git -C "${FLUTTER_DIR}" symbolic-ref -q HEAD)
if [ -z "${FLUTTER_SYMBOLIC_REF}" ]; then
FLUTTER_REV=$(git -C "${FLUTTER_DIR}" rev-parse HEAD)
FLUTTER_CHANNEL=$(git -C "${GIT_HOME}" config -f .gitmodules submodule.${FLUTTER_SUBMODULE_NAME}.branch)
if [ -z "${FLUTTER_CHANNEL}" ]; then
echoerr "Warning: Submodule '$FLUTTER_SUBMODULE_NAME' doesn't point to an official Flutter channel \
(one of stable|beta|dev|master). './flutterw upgrade' will fail without a channel."
echoerr "Fix this by adding a specific channel with:"
echoerr " - './flutterw channel <channel>' or"
echoerr " - Add 'branch = <channel>' to '$FLUTTER_SUBMODULE_NAME' submodule in .gitmodules"
else
echoerr "Fixing detached HEAD: '$FLUTTER_SUBMODULE_NAME' submodule points to a specific commit $FLUTTER_REV, not channel '$FLUTTER_CHANNEL' (as defined in .gitmodules)."
# Make sure channel is fetched
# Remove old channel branch because it might be moved to an unrelated commit where fast-forward pull isn't possible
git -C "${FLUTTER_DIR}" branch -q -D "${FLUTTER_CHANNEL}" 2> /dev/null || true
git -C "${FLUTTER_DIR}" fetch -q origin
# bind current HEAD to channel defined in .gitmodules
git -C "${FLUTTER_DIR}" checkout -q -b "${FLUTTER_CHANNEL}" "${FLUTTER_REV}"
git -C "${FLUTTER_DIR}" branch -q -u "origin/${FLUTTER_CHANNEL}" "${FLUTTER_CHANNEL}"
echoerr "Fixed! Migrated to channel '$FLUTTER_CHANNEL' while staying at commit $FLUTTER_REV. './flutterw upgrade' now works without problems!"
git -C "${FLUTTER_DIR}" status -bs
fi
fi
# Wrapper tasks done, call flutter binary with all args
set -e
"$FLUTTER_DIR/bin/flutter" "$@"
set +e
# Post flutterw tasks. exit code from /bin/flutterw will be used as final exit
FLUTTER_EXIT_STATUS=$?
if [ ${FLUTTER_EXIT_STATUS} -eq 0 ]; then
# ./flutterw channel CHANNEL
if echo "$@" | grep -q "channel"; then
if [ -n "$2" ]; then
# make sure .gitmodules is updated as well
CHANNEL=${2} # second arg
git config -f "${GIT_HOME}/.gitmodules" "submodule.${FLUTTER_SUBMODULE_NAME}.branch" "${CHANNEL}"
# makes sure nobody forgets to do commit all changed files
git add "${GIT_HOME}/.gitmodules"
git add "${FLUTTER_DIR}"
fi
fi
# ./flutterw upgrade
if echo "$@" | grep -q "upgrade"; then
# makes sure nobody forgets to do commit the changed submodule
git add "${FLUTTER_DIR}"
# flutter packages get runs automatically. Stage those changes as well
if [ -f pubspec.lock ]; then
git add pubspec.lock
fi
fi
fi
exit ${FLUTTER_EXIT_STATUS}

View file

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
<string>11.0</string>
</dict>
</plist>

View file

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View file

@ -1,53 +1,79 @@
PODS:
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
- Flutter (1.0.0)
- flutter_secure_storage (3.3.1):
- flutter_keyboard_visibility (0.0.1):
- Flutter
- path_provider_ios (0.0.1):
- flutter_local_notifications (0.0.1):
- Flutter
- ReachabilitySwift (5.0.0)
- shared_preferences_ios (0.0.1):
- flutter_native_timezone (0.0.1):
- Flutter
- flutter_secure_storage (6.0.0):
- Flutter
- fluttertoast (0.0.2):
- Flutter
- Toast
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- Toast (4.0.0)
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- 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`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/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:
- ReachabilitySwift
- Toast
EXTERNAL SOURCES:
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
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"
path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios"
shared_preferences_ios:
:path: ".symlinks/plugins/shared_preferences_ios/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/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:
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
flutter_native_timezone: 5f05b2de06c9776b4cc70e1839f03de178394d22
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: aafe91acc616949ddb318b77800a7f51bffa2a4c
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 = (
);
@ -340,7 +343,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -417,7 +420,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -466,7 +469,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;

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

@ -1,5 +1,6 @@
import UIKit
import Flutter
import flutter_local_notifications
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
@ -7,6 +8,14 @@ import Flutter
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// This is required to make any communication available in the action isolate.
FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
GeneratedPluginRegistrant.register(with: registry)
}
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
}
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

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,171 +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 skipWeekend;
String get checkOrdered;
String get notifyLunch;
String get notifyAt;
String get notifyWarning;
// Offline
String get offline;
String get mustLogout;
// Oznámit před obědem
String get lunchNotif;
String get error;
String get needRemember;
}

View file

@ -1,240 +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";
}

View file

@ -1,238 +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";
}

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,28 +1,21 @@
import 'dart:convert';
import 'dart:io';
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_localizations/flutter_localizations.dart';
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/lang/lang_cz.dart';
import 'package:opencanteen/loginmanager.dart';
import 'package:canteenlib/canteenlib.dart';
import 'package:opencanteen/okna/offline_jidelnicek.dart';
import 'package:opencanteen/okna/welcome.dart';
import 'package:opencanteen/okna/login.dart';
import 'package:opencanteen/util.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:intl/intl.dart';
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'lang/lang.dart';
import 'lang/lang_en.dart';
import 'okna/jidelnicek.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é
@ -42,59 +35,60 @@ 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',
importance: Importance.max,
priority: Priority.high,
styleInformation: BigTextStyleInformation(''),
ticker: 'today meal');
const IOSNotificationDetails iOSpec =
IOSNotificationDetails(presentAlert: true, presentBadge: true);
// naplánovat
// plan through lib
await flutterLocalNotificationsPlugin.zonedSchedule(
0,
title,
"${jidlo.varianta} - ${jidlo.nazev}",
tz.TZDateTime.from(cas, l),
const NotificationDetails(android: androidSpec, iOS: iOSpec),
androidAllowWhileIdle: true,
const NotificationDetails(android: androidSpec),
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
} on StateError catch (_) {
// nenalezeno
// no ordered meal found
}
}
// }
@ -109,372 +103,68 @@ 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');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings(onDidReceiveLocalNotification: (
int id,
String? title,
String? body,
String? payload,
) async {
debugPrint(body);
});
const ios = DarwinInitializationSettings();
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: (String? payload) async {
if (payload != null) {
debugPrint('notification payload: $payload');
}
});
const InitializationSettings initializationSettings =
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: const LoginPage(),
);
}
}
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: 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 (Platform.isIOS) {
// žá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 (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,
),
);
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) => WelcomeScreen(
canteen: canteen, n: flutterLocalNotificationsPlugin),
),
(route) => false);
} else {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => JidelnicekPage(
canteen: canteen, n: flutterLocalNotificationsPlugin),
),
(route) => false);
}
} on PlatformException {
if (!mounted) return;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.corrupted),
home: LoginPage(),
),
);
} catch (_) {
if (!mounted) return;
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) => WelcomeScreen(
canteen: canteen,
n: flutterLocalNotificationsPlugin)),
(route) => false);
} else {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => JidelnicekPage(
canteen: canteen,
n: flutterLocalNotificationsPlugin)),
(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 {
Directory appDocDir = await getApplicationDocumentsDirectory();
var den = DateTime.now();
var soubor = File(
"${appDocDir.path}/jidelnicek_${den.year}-${den.month}-${den.day}.json");
if (soubor.existsSync()) {
// načteme offline jídelníček
var input = await soubor.readAsString();
var r = jsonDecode(input);
List<OfflineJidlo> jidla = [];
for (var j in r) {
jidla.add(OfflineJidlo(
nazev: j["nazev"],
varianta: j["varianta"],
objednano: j["objednano"],
cena: j["cena"],
naBurze: j["naBurze"],
den: DateTime.parse(j["den"])));
}
if (!mounted) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: ((context) => OfflineJidelnicek(jidla: jidla))),
(route) => false);
}
}
}
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,123 +1,126 @@
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.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 '../lang/lang.dart';
import '../main.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class BurzaPage extends StatefulWidget {
const BurzaPage({Key? key, required this.canteen, required this.n})
: super(key: key);
class BurzaView extends StatefulWidget {
const BurzaView({super.key, required this.canteen});
final Canteen canteen;
final FlutterLocalNotificationsPlugin n;
@override
State<BurzaPage> createState() => _BurzaPageState();
State<BurzaView> createState() => _BurzaViewState();
}
class _BurzaPageState extends State<BurzaPage> {
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) {
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, MaterialPageRoute(builder: (c) => const LoginPage()));
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();
nactiBurzu(context);
loadExchange(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: drawerGenerator(context, widget.canteen, 3, widget.n),
drawer: drawerGenerator(context, widget.canteen, 3),
appBar: AppBar(
title: Text(Languages.of(context)!.exchange),
title: Text(AppLocalizations.of(context)!.exchange),
),
body: RefreshIndicator(
child: Center(
@ -126,20 +129,20 @@ class _BurzaPageState extends State<BurzaPage> {
child: Column(
children: [
const SizedBox(height: 10),
Text("${Languages.of(context)!.balance}$kredit"),
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: obsah),
child: Column(children: content),
),
)
],
),
),
),
onRefresh: () => nactiBurzu(context)),
onRefresh: () => loadExchange(context)),
);
}
}

View file

@ -3,39 +3,37 @@ import 'dart:io';
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.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 '../lang/lang.dart';
import '../main.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class JidelnicekPage extends StatefulWidget {
const JidelnicekPage({Key? key, required this.canteen, required this.n})
: super(key: key);
class MealView extends StatefulWidget {
const MealView({super.key, required this.canteen});
final Canteen canteen;
final FlutterLocalNotificationsPlugin n;
@override
State<JidelnicekPage> createState() => _JidelnicekPageState();
State<MealView> createState() => _MealViewState();
}
class _JidelnicekPageState extends State<JidelnicekPage> {
List<Widget> obsah = [const CircularProgressIndicator()];
DateTime den = DateTime.now();
String denTydne = "";
double kredit = 0.0;
bool _skipWeekend = false;
class _MealViewState extends State<MealView> {
List<Widget> content = [const CircularProgressIndicator()];
DateTime day = DateTime.now();
String dayOWeek = "";
double balance = 0.0;
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));
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)));
@ -45,16 +43,16 @@ class _JidelnicekPageState extends State<JidelnicekPage> {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Languages.of(context)!.noOrder),
content: Text(AppLocalizations.of(context)!.noOrder),
duration: const Duration(seconds: 5),
action: SnackBarAction(
onPressed: () => setState(
() {
den = pristi.add(Duration(days: i + 1));
nactiJidlo();
day = pristi.add(Duration(days: i + 1));
loadMeals();
},
),
label: Languages.of(context)!.jump,
label: AppLocalizations.of(context)!.jump,
),
),
);
@ -64,359 +62,384 @@ class _JidelnicekPageState extends State<JidelnicekPage> {
}
}
Future<void> nactiJidlo() async {
obsah = [const CircularProgressIndicator()];
switch (den.weekday) {
Future<void> loadMeals() async {
content = [const CircularProgressIndicator()];
switch (day.weekday) {
case 2:
denTydne = Languages.of(context)!.tuesday;
dayOWeek = AppLocalizations.of(context)!.tuesday;
break;
case 3:
denTydne = Languages.of(context)!.wednesday;
dayOWeek = AppLocalizations.of(context)!.wednesday;
break;
case 4:
denTydne = Languages.of(context)!.thursday;
dayOWeek = AppLocalizations.of(context)!.thursday;
break;
case 5:
denTydne = Languages.of(context)!.friday;
dayOWeek = AppLocalizations.of(context)!.friday;
break;
case 6:
denTydne = Languages.of(context)!.saturday;
dayOWeek = AppLocalizations.of(context)!.saturday;
break;
case 7:
denTydne = Languages.of(context)!.sunday;
dayOWeek = AppLocalizations.of(context)!.sunday;
break;
default:
denTydne = Languages.of(context)!.monday;
dayOWeek = AppLocalizations.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,
),
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)
? Languages.of(context)!.inExchange
: "${j.cena}"),
Checkbox(
value: j.objednano,
fillColor: (j.lzeObjednat)
? MaterialStateProperty.all(Colors.blue)
: 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: [
),
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(Languages.of(context)!.ordering)
]),
Text(AppLocalizations.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()),
),
),
);
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: [
TextButton(
child:
Text(Languages.of(context)!.close),
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);
},
)
],
),
);
});
}
},
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());
}
},
),
} else {
// else no
widget.canteen.doBurzy(j).then((_) => loadMeals());
}
},
),
);
}
),
);
}
});
});
}).catchError((o) {
if (!widget.canteen.prihlasen) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (c) => const LoginPage()));
}
});
}
},
);
return;
}
Future<void> kliknuti(String value, BuildContext context,
FlutterLocalNotificationsPlugin n) async {
if (value == Languages.of(context)!.signOut) {
Future<void> click(String value, BuildContext context) async {
if (value == AppLocalizations.of(context)!.signOut) {
await showDialog<bool>(
context: context,
builder: (c) => AlertDialog(
title: Text(Languages.of(context)!.warning),
content: Text(Languages.of(context)!.signOutWarn),
builder: (c) => PlatformDialog(
title: AppLocalizations.of(context)!.warning,
content: AppLocalizations.of(context)!.signOutWarn,
actions: [
TextButton(
PlatformButton(
onPressed: () {
const storage = FlutterSecureStorage();
storage.deleteAll();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (c) => const LoginPage()),
platformRouter((c) => const LoginPage()),
(route) => false);
},
child: Text(Languages.of(context)!.yes)),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(Languages.of(context)!.no))
text: AppLocalizations.of(context)!.yes),
PlatformButton(
onPressed: () => Navigator.of(context).pop(),
text: AppLocalizations.of(context)!.no,
)
],
),
);
} else if (value == Languages.of(context)!.review) {
(Platform.isAndroid)
? launchUrl(
Uri.parse("market://details?id=cz.hernikplays.opencanteen"),
mode: LaunchMode.externalApplication)
: launchUrl(
Uri.parse(
"https://apps.apple.com/cz/app/opencanteen/id1621124445"),
mode: LaunchMode.externalApplication);
} else if (value == Languages.of(context)!.reportBugs) {
} 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 == Languages.of(context)!.about) {
} else if (value == AppLocalizations.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}",
"${AppLocalizations.of(context)!.copyright}\n${AppLocalizations.of(context)!.license}",
applicationVersion: packageInfo.version,
children: [
TextButton(
onPressed: (() => launchUrl(
Uri.parse("https://github.com/hernikplays/opencanteen"))),
child: Text(Languages.of(context)!.source))
PlatformButton(
onPressed: (() => launchUrl(
Uri.parse("https://git.mnau.xyz/hernik/opencanteen"),
mode: LaunchMode.externalApplication)),
text: AppLocalizations.of(context)!.source,
)
]);
} else if (value == Languages.of(context)!.settings) {
Navigator.push(
context, MaterialPageRoute(builder: (c) => Nastaveni(n: n)));
} else if (value == AppLocalizations.of(context)!.settings) {
Navigator.push(context, platformRouter((c) => const AndroidNastaveni()));
}
}
void nactiNastaveni() async {
var prefs = await SharedPreferences.getInstance();
_skipWeekend = prefs.getBool("skip") ?? false;
void loadSettings() async {
if (!mounted) return;
kontrolaTyden(context);
checkWeek(context);
}
/// uložení jídelníčku pro dnešek offline
void ulozitDnesekOffline() async {
var prefs = await SharedPreferences.getInstance();
if (prefs.getBool("offline") != null && prefs.getBool("offline")!) {
Directory appDocDir = await getApplicationDocumentsDirectory();
for (var f in appDocDir.listSync()) {
// Vymažeme obsah
if (f.path.contains("jidelnicek")) {
f.deleteSync();
}
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();
}
// Uložíme nová data
Jidelnicek j = Jidelnicek(DateTime.now(), []);
}
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();
j = await widget.canteen.jidelnicekDen(den: d);
} catch (e) {
if (!widget.canteen.prihlasen) {
if (!mounted) return; // ! Přidat chybu, pokud není mounted
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (c) => const LoginPage()));
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_${den.year}-${den.month}-${den.day}.json");
"${appDocDir.path}/jidelnicek_${d.year}-${d.month}-${d.day}.json");
soubor.createSync();
var jidla = [];
for (var jidlo in j.jidla) {
for (var jidlo in j!.jidla) {
jidla.add({
"nazev": jidlo.nazev,
"varianta": jidlo.varianta,
"objednano": jidlo.objednano,
"cena": jidlo.cena,
"naBurze": jidlo.naBurze,
"den": den.toString()
"den": d.toString()
});
}
await soubor.writeAsString(json.encode(jidla));
@ -426,27 +449,27 @@ class _JidelnicekPageState extends State<JidelnicekPage> {
@override
void didChangeDependencies() {
super.didChangeDependencies();
nactiNastaveni();
ulozitDnesekOffline();
nactiJidlo();
loadSettings();
saveOffline();
loadMeals();
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: drawerGenerator(context, widget.canteen, 1, widget.n),
drawer: drawerGenerator(context, widget.canteen, 1),
appBar: AppBar(
title: Text(Languages.of(context)!.menu),
title: Text(AppLocalizations.of(context)!.menu),
actions: [
PopupMenuButton(
onSelected: ((String value) => kliknuti(value, context, widget.n)),
onSelected: ((String value) => click(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
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,
@ -458,90 +481,102 @@ class _JidelnicekPageState extends State<JidelnicekPage> {
],
),
body: RefreshIndicator(
onRefresh: nactiJidlo,
onRefresh: loadMeals,
child: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width - 50,
width: MediaQuery.of(context).size.width,
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(
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: den,
currentDate: den,
initialDate: day,
currentDate: day,
firstDate: DateTime(2019, 1, 1),
lastDate: DateTime(den.year + 1, 12, 31),
lastDate: DateTime(day.year + 1, 12, 31),
locale: Localizations.localeOf(context));
if (datePicked == null) return;
setState(() {
den = datePicked;
nactiJidlo();
day = datePicked;
loadMeals();
});
},
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))
]),
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: obsah),
child: Column(children: content),
),
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));
day = day.add(const Duration(days: 1));
if (day.weekday == 6 && settings.skipWeekend) {
day = day.add(const Duration(days: 2));
}
nactiJidlo();
loadMeals();
});
} else {
setState(() {
den = den.subtract(const Duration(days: 1));
if (den.weekday == 7 && _skipWeekend) {
den = den.subtract(const Duration(days: 2));
}
nactiJidlo();
});
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);
}
}

View file

@ -2,208 +2,267 @@ 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 '../lang/lang.dart';
import '../loginmanager.dart';
import '../util.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../loginmanager.dart';
import '../../main.dart';
import '../../util.dart';
class Nastaveni extends StatefulWidget {
const Nastaveni({Key? key, required this.n}) : super(key: key);
final FlutterLocalNotificationsPlugin n;
class AndroidNastaveni extends StatefulWidget {
const AndroidNastaveni({super.key});
@override
State<Nastaveni> createState() => _NastaveniState();
State<AndroidNastaveni> createState() => _AndroidNastaveniState();
}
class _NastaveniState extends State<Nastaveni> {
bool _ukladatOffline = false;
bool _preskakovatVikend = false;
bool _kontrolovatTyden = false;
bool _oznameniObed = false;
bool _zapamatovany = false;
TimeOfDay _oznameniCas = TimeOfDay.now();
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 najitNastaveni() async {
var preferences = await SharedPreferences.getInstance();
_zapamatovany = await LoginManager.zapamatovat();
setState(() {
_ukladatOffline = preferences.getBool("offline") ?? false;
_preskakovatVikend = preferences.getBool("skip") ?? false;
_kontrolovatTyden = preferences.getBool("tyden") ?? false;
_oznameniObed = preferences.getBool("oznamit") ?? false;
var casStr = preferences.getString("oznameni_cas");
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 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 zmenitNastaveni(String key, bool value) async {
var preferences = await SharedPreferences.getInstance();
preferences.setBool(key, value);
void changeSetting(String key, bool value) async {
preferences!.setBool(key, value);
}
@override
void initState() {
super.initState();
najitNastaveni();
loadSettings();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Languages.of(context)!.settings),
title: Text(AppLocalizations.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,
onChanged: (value) {
setState(() {
_ukladatOffline = value;
cistit(value);
zmenitNastaveni("offline", value);
});
})
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(Languages.of(context)!.skipWeekend),
Switch(
value: _preskakovatVikend,
onChanged: (value) {
setState(() {
_preskakovatVikend = value;
zmenitNastaveni("skip", value);
});
})
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(Languages.of(context)!.checkOrdered)),
Switch(
value: _kontrolovatTyden,
onChanged: (value) {
setState(() {
_kontrolovatTyden = value;
zmenitNastaveni("tyden", value);
});
})
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(Languages.of(context)!.notifyLunch)),
Switch(
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),
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 cistit(bool value) async {
void clear(bool value) async {
if (!value) {
Directory appDocDir = await getApplicationDocumentsDirectory();
for (var f in appDocDir.listSync()) {
@ -215,11 +274,10 @@ class _NastaveniState extends State<Nastaveni> {
}
}
void vytvoritOznameni(DateTime den) async {
await widget.n.cancelAll();
var d = await LoginManager.getDetails(); // získat údaje
void createNotif(DateTime den) async {
await flutterLocalNotificationsPlugin.cancelAll();
var d = await LoginManager.getDetails(); // grab details
if (d != null) {
// Nové oznámení
var c = Canteen(d["url"]!);
if (await c.login(d["user"]!, d["pass"]!)) {
var jidla = await c.jidelnicekDen();
@ -232,23 +290,34 @@ class _NastaveniState extends State<Nastaveni> {
importance: Importance.max,
priority: Priority.high,
ticker: 'today meal');
const IOSNotificationDetails iOSpec =
IOSNotificationDetails(presentAlert: true, presentBadge: true);
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 widget.n.zonedSchedule(
// Vytvoří nové oznámení pro daný čas a datum
await flutterLocalNotificationsPlugin.zonedSchedule(
// schedules a notification
0,
Languages.of(context)!.lunchNotif,
AppLocalizations.of(context)!.lunchNotif,
"${jidlo.varianta} - ${jidlo.nazev}",
tz.TZDateTime.from(den, l),
const NotificationDetails(android: androidSpec, iOS: iOSpec),
androidAllowWhileIdle: true,
const NotificationDetails(android: androidSpec),
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
} on StateError catch (_) {
// nenalezeno
// no meal found
}
}
}

View file

@ -1,139 +1,179 @@
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 '../lang/lang.dart';
import '../main.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class OfflineJidelnicek extends StatefulWidget {
const OfflineJidelnicek({Key? key, required this.jidla}) : super(key: key);
final List<OfflineJidlo> jidla;
class OfflineMealView extends StatefulWidget {
const OfflineMealView({super.key});
@override
State<OfflineJidelnicek> createState() => _OfflineJidelnicekState();
State<OfflineMealView> createState() => _OfflineMealViewState();
}
class _OfflineJidelnicekState extends State<OfflineJidelnicek> {
List<Widget> obsah = [const CircularProgressIndicator()];
DateTime den = DateTime.now();
String denTydne = "";
Future<void> nactiJidlo() async {
den = widget.jidla[0].den;
switch (den.weekday) {
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:
denTydne = Languages.of(context)!.tuesday;
dayOWeek = AppLocalizations.of(context)!.tuesday;
break;
case 3:
denTydne = Languages.of(context)!.wednesday;
dayOWeek = AppLocalizations.of(context)!.wednesday;
break;
case 4:
denTydne = Languages.of(context)!.thursday;
dayOWeek = AppLocalizations.of(context)!.thursday;
break;
case 5:
denTydne = Languages.of(context)!.friday;
dayOWeek = AppLocalizations.of(context)!.friday;
break;
case 6:
denTydne = Languages.of(context)!.saturday;
dayOWeek = AppLocalizations.of(context)!.saturday;
break;
case 7:
denTydne = Languages.of(context)!.sunday;
dayOWeek = AppLocalizations.of(context)!.sunday;
break;
default:
denTydne = Languages.of(context)!.monday;
dayOWeek = AppLocalizations.of(context)!.monday;
}
obsah = [];
for (OfflineJidlo j in widget.jidla) {
obsah.add(
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.varianta),
const SizedBox(width: 10),
Flexible(
child: Text(
j.nazev,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(j.variant),
const SizedBox(width: 10),
Flexible(
child: Text(
j.name,
),
),
),
Text((j.naBurze)
? Languages.of(context)!.inExchange
: "${j.cena}"),
Checkbox(
value: j.objednano,
Text((j.onExchange)
? AppLocalizations.of(context)!.inExchange
: "${j.price}"),
Checkbox(
value: j.ordered,
fillColor: MaterialStateProperty.all(Colors.grey),
onChanged: (v) async {
return;
})
],
)),
},
)
],
),
),
),
);
}
setState(() {});
}
void kliknuti(String value, BuildContext context) async {
if (value == Languages.of(context)!.signOut) {
void click(String value, BuildContext context) async {
if (value == AppLocalizations.of(context)!.signOut) {
const storage = FlutterSecureStorage();
storage.deleteAll();
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (c) => const LoginPage()));
} else if (value == Languages.of(context)!.review) {
(Platform.isAndroid)
? launchUrl(
Uri.parse("market://details?id=cz.hernikplays.opencanteen"),
mode: LaunchMode.externalApplication)
: launchUrl(
Uri.parse(
"https://apps.apple.com/cz/app/opencanteen/id1621124445"),
mode: LaunchMode.externalApplication);
} else if (value == Languages.of(context)!.reportBugs) {
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 == Languages.of(context)!.about) {
} else if (value == AppLocalizations.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://github.com/hernikplays/opencanteen"))),
child: Text(Languages.of(context)!.source))
]);
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();
nactiJidlo();
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(Languages.of(context)!.menu),
title: Text(AppLocalizations.of(context)!.menu),
automaticallyImplyLeading: false,
actions: [
PopupMenuButton(
onSelected: ((String value) => kliknuti(value, context)),
onSelected: ((String value) => click(value, context)),
itemBuilder: (BuildContext context) {
return {
Languages.of(context)!.reportBugs,
Languages.of(context)!.review,
Languages.of(context)!.about,
Languages.of(context)!.signOut
AppLocalizations.of(context)!.reportBugs,
AppLocalizations.of(context)!.review,
AppLocalizations.of(context)!.about,
AppLocalizations.of(context)!.signOut
}.map((String choice) {
return PopupMenuItem<String>(
value: choice,
@ -152,28 +192,78 @@ class _OfflineJidelnicekState extends State<OfflineJidelnicek> {
children: [
const SizedBox(height: 10),
Text(
Languages.of(context)!.offline,
AppLocalizations.of(context)!.offline,
style: const TextStyle(fontWeight: FontWeight.bold),
),
Text(Languages.of(context)!.mustLogout),
Text(AppLocalizations.of(context)!.mustLogout),
const SizedBox(height: 10),
TextButton(
child:
Text("${den.day}. ${den.month}. ${den.year} - $denTydne"),
onPressed: () => {},
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
IconButton(
onPressed: () {
if (data.length <= 1) return;
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: obsah,
children: content,
),
),
],
),
),
),
onRefresh: () => Navigator.pushReplacement(context,
MaterialPageRoute(builder: ((context) => const LoginPage()))),
onRefresh: () => Navigator.pushReplacement(
context, platformRouter((context) => const LoginPage())),
),
);
}

View file

@ -1,60 +1,58 @@
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.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:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:opencanteen/okna/jidelnicek.dart';
import 'package:opencanteen/util.dart';
class WelcomeScreen extends StatefulWidget {
const WelcomeScreen({Key? key, required this.canteen, required this.n})
: super(key: key);
class WelcomePage extends StatefulWidget {
const WelcomePage({super.key, required this.canteen});
final Canteen canteen;
final FlutterLocalNotificationsPlugin n;
@override
State<WelcomeScreen> createState() => _WelcomeScreenState();
State<WelcomePage> createState() => _WelcomePageState();
}
class _WelcomeScreenState extends State<WelcomeScreen> {
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),
),
@ -63,17 +61,15 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
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) =>
JidelnicekPage(canteen: widget.canteen, n: widget.n)),
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,13 +1,14 @@
import 'dart:io';
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.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';
import 'lang/lang.dart';
import 'okna/jidelnicek.dart';
Drawer drawerGenerator(BuildContext context, Canteen canteen, int p,
FlutterLocalNotificationsPlugin n) {
Drawer drawerGenerator(BuildContext context, Canteen canteen, int p) {
Drawer drawer = const Drawer();
switch (p) {
case 1:
@ -16,22 +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) => BurzaPage(canteen: canteen, n: n),
),
platformRouter((context) => BurzaView(canteen: canteen)),
),
),
],
@ -44,20 +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) => JidelnicekPage(canteen: canteen, n: n))),
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),
),
],
@ -67,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,2 @@
- lepší podpora pro Android 13
- změna na adaptivní ikony na Androidu

View file

@ -0,0 +1,3 @@
- Umožnit ukládat více dnů offline
- Pop-up "Přihlašování" nyní zmizí v případě neúspěšného přihlášení
- chyba při ukládání offline vás nyní již nevyhodí ale zobrazí pouze zprávu

View file

@ -0,0 +1,2 @@
- aktualizace knihovny canteenlib
- splitování APK

View file

@ -0,0 +1,2 @@
- Optimalizace ze strany kodu
- Úpravy vzhledu

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,9 @@
OpenCanteen je open-source alternativní klient pro obědový systém iCanteen
Aplikace nyní umožňuje:
- prohlížet jídelníček
- objednávat či rušit obědy
- přidávat do nebo objednávat z burzy
- zobrazit oznámení ve vybraný čas obsahující objednané jídlo v daný den
Aplikace nemusí na všech instancích fungovat, v případě problémů vytvořte issue v repu nebo zanechte zpětnou vazbu skrz tlačítko v aplikaci.

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

View file

@ -0,0 +1 @@
Neoficiální klient pro systém iCanteen

View file

@ -0,0 +1,2 @@
- better support for Android 13
- changed to adaptive + themed icons

View file

@ -0,0 +1,3 @@
- Support downloading multiple days of meal lists offline
- "Signing in" pop-up disappears when not signed in succesfully
- Error while saving menu offline no longer throws you to the log-in screen, but shows just an error message

View file

@ -0,0 +1,2 @@
- updated canteenlib library
- added support for APK splitting

View file

@ -0,0 +1,2 @@
- Code optimization
- Theme edits

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

@ -0,0 +1,9 @@
OpenCanteen is an alternative free open-source client for the iCanteen food ordering system.
Using this app, you can:
- browse the menu of your iCanteen instance
- order or cancel meals
- place food into or the exchange
- have a notification displayed at a defined time with info about the ordered meal
The app may not work on every iCanteen instance, so in case of any bugs, make sure to open an issue in the repo.

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

View file

@ -0,0 +1 @@
An unofficial client for the iCanteen food system

1
metadata/en-US/title.txt Normal file
View file

@ -0,0 +1 @@
OpenCanteen

View file

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

File diff suppressed because it is too large Load diff

View file

@ -5,126 +5,223 @@ packages:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
url: "https://pub.dev"
source: hosted
version: "3.3.1"
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.9.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.0.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
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted
version: "2.0.3"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7
url: "https://pub.dev"
source: hosted
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
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.9.3"
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: "9.5.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: "0.4.2"
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: "5.0.0"
version: "7.0.0+1"
flutter_localizations:
dependency: "direct main"
description: flutter
@ -134,301 +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.1"
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.0"
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
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
url: "https://pub.dev"
source: hosted
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.0"
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.5"
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.20"
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.4"
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.0.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.2"
process:
version: "2.1.7"
pointycastle:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
name: pointycastle
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
version: "3.7.3"
settings_ui:
dependency: "direct main"
description:
name: settings_ui
sha256: d9838037cb554b24b4218b2d07666fbada3478882edefae375ee892b6c820ef3
url: "https://pub.dev"
source: hosted
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.12"
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
@ -438,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.1.1"
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.8.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.5"
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.17"
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.0"
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.0.0"
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.17.0 <3.0.0"
flutter: ">=3.0.0"
dart: ">=3.2.0 <4.0.0"
flutter: ">=3.16.0"

View file

@ -6,47 +6,50 @@ publish_to: 'none'
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
version: 1.4.1+20
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.0.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: 9.5.3+1
timezone: ^0.8.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.9.2"
flutter_launcher_icons: ^0.13.0
flutter_lints: ^3.0.0
flutter_icons:
android: "launcher_icon"
android: true
ios: true
image_path: "assets/icon.jpg"
adaptive_icon_background: "#4f4685"
adaptive_icon_foreground: "assets/fore.png"
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"]
}