Compare commits
78 Commits
4.0.4
...
4.1.1-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d9f99a955 | ||
|
|
2c5939fab4 | ||
|
|
95ed503e53 | ||
|
|
51e2c63134 | ||
|
|
50bc03f403 | ||
|
|
f5bc16ba42 | ||
|
|
f4501ce251 | ||
|
|
3317472773 | ||
|
|
acdd47a49a | ||
|
|
fa8be89aaf | ||
|
|
5034af2fb4 | ||
|
|
87842de421 | ||
|
|
1f46a0509f | ||
|
|
8e9713e436 | ||
|
|
fe0a7d81ae | ||
|
|
f9e14349b7 | ||
|
|
a1f087758f | ||
|
|
4068abdb95 | ||
|
|
9314c613bc | ||
|
|
458e93e19f | ||
|
|
ba8d26d250 | ||
|
|
33e3495d9f | ||
|
|
f71e72e443 | ||
|
|
c615a33bd2 | ||
|
|
7ad0ea26e2 | ||
|
|
0ad663beb3 | ||
|
|
1366984c15 | ||
|
|
62d3895373 | ||
|
|
3579c4e821 | ||
|
|
5c39865d40 | ||
|
|
551b2849fe | ||
|
|
ce1c5eb0d8 | ||
|
|
d929c804df | ||
|
|
19c128eecd | ||
|
|
57cf764804 | ||
|
|
ea812e0b67 | ||
|
|
1c517a99f2 | ||
|
|
2687cb146b | ||
|
|
1a9080dcc2 | ||
|
|
580c92b13d | ||
|
|
ae4c4aa89c | ||
|
|
ded7c51f44 | ||
|
|
485e85ddaa | ||
|
|
3c0082a786 | ||
|
|
c474512088 | ||
|
|
f78a542be2 | ||
|
|
131454b99d | ||
|
|
e255182b93 | ||
|
|
3857896d6c | ||
|
|
301e8cb638 | ||
|
|
ae7c724f65 | ||
|
|
751cd04ce2 | ||
|
|
a88ccfa3fc | ||
|
|
9e914974b7 | ||
|
|
9cfa8296b8 | ||
|
|
27ef942723 | ||
|
|
fe03554fbf | ||
|
|
8c2227df73 | ||
|
|
0274c2f070 | ||
|
|
8f85c6a33b | ||
|
|
cf81ca8207 | ||
|
|
07bbafe7dd | ||
|
|
3eee2c7a55 | ||
|
|
1cdde3b6ce | ||
|
|
67aea46c06 | ||
|
|
7adec7dfa5 | ||
|
|
95fa819ed2 | ||
|
|
1e87d344e2 | ||
|
|
9223375304 | ||
|
|
bb862f15bd | ||
|
|
261c94e9bb | ||
|
|
06016514e5 | ||
|
|
cead24b65a | ||
|
|
6eaab57468 | ||
|
|
5852fc233d | ||
|
|
d7e21f6332 | ||
|
|
c78b8d3b97 | ||
|
|
940e3d8ca1 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -49,4 +49,4 @@ filcnaplo/linux/flutter/generated_plugin_registrant.h
|
||||
filcnaplo/linux/flutter/generated_plugins.cmake
|
||||
filcnaplo/macos/Flutter/GeneratedPluginRegistrant.swift
|
||||
.vscode/
|
||||
|
||||
key.properties
|
||||
60
.idea/naplo.iml
generated
60
.idea/naplo.iml
generated
@@ -2,7 +2,65 @@
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_desktop_ui/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_desktop_ui/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_desktop_ui/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_premium/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_premium/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_premium/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_kreta_api/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_kreta_api/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_kreta_api/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_mobile_ui/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_mobile_ui/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_mobile_ui/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/.dart_tool" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
|
||||
@@ -1,39 +1,36 @@
|
||||
|
||||
# Contributing Guide
|
||||
# Contribution guide
|
||||
Köszönjük, ha programozással segíted a munkánkat!
|
||||
A folytatáshoz szükséged lesz egy Linuxot vagy Windowst futtató számítógépre, minimális programozási tapasztalatra és egy kis angoltudásra.
|
||||
Segít, ha nem csak kicsit tudsz programozni, és ha ismered a Gitet és a GitHubot ;)
|
||||
A folytatáshoz szükséged lesz egy Linux-ot vagy Windows-t futtató számítógépre, minimális programozási tapasztalatra és egy kis angoltudásra.
|
||||
Segít, ha már gyakorlottabb vagy a programozásban, és ha ismered a [Git](https://git-scm.com/) és a [GitHub](https://github.com/) működését. ;)
|
||||
|
||||
## Miben segítsek?
|
||||
Kérünk, **olyan dologgal járulj hozzá** a reFilchez, ami valószínűleg **sok embernek hasznos lesz** majd. Szeretnénk egy minél teljeskörűbb iskolai asszisztenst létrehozni, de az iskolaspecifikus, vagy külön neked hasznos funkciók helye inkább legyen a saját forkod.
|
||||
Kérünk, **olyan dologgal járulj hozzá** a **reFilc**hez, ami valószínűleg **sok embernek hasznos lehet**. Szeretnénk egy minél teljeskörűbb iskolai asszisztenst létrehozni, de az iskolaspecifikus, vagy külön neked hasznos funkciók helye inkább legyen a saját Fork-od.
|
||||
|
||||
Fontos, hogy **mielőtt egy nagy volumenű projektbe belekezdesz, futtasd meg ötletedet a [Discord szerverünkön](https://dc.refilc.hu/),** ahol még azelőtt tudunk tanácsot adni, mielőtt sok-sok órát beleöltél volna egy esetleg felesleges dologba.
|
||||
Fontos, hogy **mielőtt egy nagyobb méretű projektbe belekezdenél, futtasd meg ötletedet a [Discord szerverünkön](https://dc.refilc.hu/)**, ahol még azelőtt tudunk tanácsot adni, hogy sok-sok órát beleöltél volna egy esetleg felesleges dologba.
|
||||
|
||||
A legjobban annak örülünk, ha az [Issues](https://github.com/refilc/filcnaplo/issues) oldalról szemezgetsz, **ha lehet, a [priority taggel megjelöltekkel kezdd](https://github.com/refilc/filcnaplo/issues?q=is%3Aissue+is%3Aopen+label%3Apriority),** vagy ha új vagy a Flutterhez, ajánljuk figyelmedbe [ezeket a viszonylag könnyen javítható hibákat](https://github.com/refilc/filcnaplo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) (ha épp van ilyen).
|
||||
A legjobban annak örülünk, ha az [Issues](https://github.com/refilc/naplo/issues) oldalról szemezgetsz. Ha még új vagy a Flutterben, ajánljuk figyelmedbe ezeket a [viszonylag könnyen javítható hibákat](https://github.com/refilc/naplo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22), ha éppen van ilyen.
|
||||
|
||||
## Hogyan segítsek?
|
||||
Nem ígérhetünk itt sem programozás-, sem Git-kurzust, de a projektspecifikus dolgokat leírjuk, és segítünk a Flutter telepítésében.
|
||||
|
||||
Nem ígérhetünk itt sem programozás-, sem git-kurzust, de a projektspecifikus dolgokat leírjuk, és segítünk a Flutter feltelepítésében.
|
||||
A **reFilc** a Google által pár éve létrehozott **[Flutter](https://flutter.dev/)** keretrendszert használja, aminek nyelve a **[Dart](https://dart.dev/)**. Ha ismered a C#, Java, C++, vagy egyéb hasonló programnyelvek működését, **nem fog nagy gondot okozni a használata.** A felhasználói felület létrehozásában az is segíthet, ha foglalkoztál már korábban weboldalakkal vagy alkalmazásfejlesztéssel.
|
||||
Ha még nem használtad a Flutter-t, mindenképp böngészd át a [YouTube csatornájukat](https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw).
|
||||
Kód vagy UI teszteléséhez Flutter telepítése nélkül is használhatod a [DartPad](https://dartpad.dev/)-et.
|
||||
|
||||
A reFilc a Google által pár éve létrehozott **[Fluttert](https://flutter.dev/)** használja, aminek nyelve a **[Dart](https://dart.dev/)**. Ha ismered a C#-ot, Javát, C++t, vagy egyéb hasonló nyelvet, **nem fog gondot okozni a használata.** A felhasználói felület létrehozásában az is segíthet, ha foglalkoztál már korábban weboldalakkal.
|
||||
Ha még nem használtál Fluttert, mindenképp böngészd át a [YouTube csatornájukat](https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw).
|
||||
Könnyen tudsz kódot, vagy akár UI-t is tesztelni a [DartPad](https://dartpad.dev/) oldalon.
|
||||
#### [Segítség a Flutter telepítéséhez](https://docs.flutter.dev/get-started/install)
|
||||
**Használd a Flutter stable verzióját!** Írd be a terminálba: `flutter channel stable`
|
||||
|
||||
#### [Segítség a Flutter telepítéséhez és a forráskód futtatásához](/.github/SETUP.md)
|
||||
Fontos: **Legyél a flutter beta verzióján!** Írd be: `flutter channel beta`
|
||||
Ha nem értessz a Git-hez vagy a GitHub-hoz, ajánljuk figyelmedbe [ezt a cikket](https://medium.com/envienta-magyarorsz%C3%A1g/git-%C3%A9s-github-gyorstalpal%C3%B3-f2d78a732deb), viszont arra kérünk, hogy a használatukat ne a **reFilc**en próbáld ki először. Hozz létre egy saját Repo-t és abban tesztelgess. Ha már nagyjából kitapasztaltad, várjuk hozzájárulásodat.
|
||||
|
||||
|
||||
|
||||
Ha nem értesz a Githez, ajánljuk figyelmedbe [ezt a cikket](https://medium.com/envienta-magyarorsz%C3%A1g/git-%C3%A9s-github-gyorstalpal%C3%B3-f2d78a732deb). Viszont arra kérünk, a Git használatát ne a reFilcen próbáld ki először, hozz létre előbb egy saját Repót, és abba tesztelgess. Ha már nagyjából kitapasztaltad, várjuk hozzájárulásodat.
|
||||
|
||||
Készíts egy forkot a saját fiókod alá.
|
||||
A reFilc legfrissebb, épp fejlesztés alatt álló verzióját a [dev brancen](https://github.com/refilc/filcnaplo/tree/dev) találod, kérjük ide commitolj, és ide célozd a forkodból a Pull Requested. Írd le benne, mit változtattál, és ha lehet, csatolj képernyőképet is.
|
||||
Minél gyakrabban készíts minél részletesebben elnevezett commitokat, hogy el tudjunk tájékozódni az általad beküldött kódon.
|
||||
Készíts egy Fork-ot a saját GitHub fiókod alá.
|
||||
A **reFilc** legfrissebb, **épp fejlesztés alatt álló verzióját a [master branch](https://github.com/refilc/naplo/tree/master)-en találod**. Kérjük ide Commit-olj és ide célozd a Fork-odból a Pull Request-edet. Írd le benne, hogy mit változtattál és ha lehet, csatolj képernyőképet is.
|
||||
Minél gyakrabban készíts minél részletesebben elnevezett Commit-okat, hogy mások is el tudjanak igazodni az általad beküldött kódban.
|
||||
|
||||
---
|
||||
|
||||
Az általad fejlesztett funkciók mellé a changelogban odakerül GitHub felhasználóneved.
|
||||
Ha jelentős és rendszeres hozzájáruló vagy, Discordon megkapod a `DEV` rangot.
|
||||
Az általad fejlesztett funkciók mellé a Changelog-ba odakerül a GitHub felhasználóneved.
|
||||
Ha jelentős és rendszeres hozzájáruló vagy, Discord-on megkaphatod a `DEV` rangot.
|
||||
|
||||
Ha bárhol elakadtál, keress minket Discordon.
|
||||
Jó fejlesztést kívánunk!
|
||||
Ha bárhol elakadtál vagy kérdésed van, keress bátran Discordon!
|
||||
**Jó fejlesztést kívánunk!**
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#### Nem hivatalos e-napló alkalmazás az eKRÉTA rendszerhez - tanulóktól, tanulóknak.
|
||||
|
||||
[](https://github.com/refilc/naplo/releases) [](http://dc.refilc.hu)
|
||||
[](https://github.com/refilc/naplo/releases) [](https://dc.refilc.hu)
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -29,11 +29,13 @@ flutter run
|
||||
|
||||
### Contribution
|
||||
|
||||
**Nézd meg a [Contribution guide](CONTRIBUTING.md)-ot!**
|
||||
|
||||
Az összes (ugyan azon verzióhoz tartozó) contribution meg fog jelenni a release-nél. Kérjük, írd le a Discord nevedet a Description-be, hogy adhassunk rangot.
|
||||
|
||||
-------
|
||||
|
||||
# Kudo
|
||||
# Developers
|
||||
|
||||
**annon:** a Filc napló készítője (ez az app a Filcen alapul)
|
||||
|
||||
|
||||
1
filcnaplo/.gitignore
vendored
1
filcnaplo/.gitignore
vendored
@@ -44,3 +44,4 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
key.properties
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" package="hu.refilc.naplo">
|
||||
<application android:label="reFilc" tools:replace="android:label" android:icon="@mipmap/ic_launcher"
|
||||
<application android:name="${applicationName}" android:label="reFilc" tools:replace="android:label" android:icon="@mipmap/ic_launcher"
|
||||
android:requestLegacyExternalStorage="true">
|
||||
<activity android:exported="true" android:name=".MainActivity"
|
||||
android:launchMode="singleTop" android:theme="@style/LaunchTheme"
|
||||
@@ -62,9 +62,10 @@
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
package hu.refilc.naplo;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity;
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||
|
||||
import io.flutter.embedding.engine.FlutterEngine;
|
||||
|
||||
public class MainActivity extends FlutterActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
GeneratedPluginRegistrant.registerWith(new FlutterEngine(this));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BIN
filcnaplo/android/app/src/main/res/drawable/ic_notification.png
Normal file
BIN
filcnaplo/android/app/src/main/res/drawable/ic_notification.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
filcnaplo/assets/images/card_border.png
Normal file
BIN
filcnaplo/assets/images/card_border.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -1,3 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
flutter clean
|
||||
dart pub get
|
||||
flutter doctor -v
|
||||
|
||||
flutter build ipa --release --dart-define=APPVER=$(cat pubspec.yaml | grep version: | cut -d' ' -f2 | cut -d+ -f1) --no-tree-shake-icons
|
||||
|
||||
15
filcnaplo/ios/Flutter/Generated 3.xcconfig
Normal file
15
filcnaplo/ios/Flutter/Generated 3.xcconfig
Normal file
@@ -0,0 +1,15 @@
|
||||
// This is a generated file; do not edit or check into version control.
|
||||
FLUTTER_ROOT=/Users/kima/development/flutter
|
||||
FLUTTER_APPLICATION_PATH=/Users/kima/Documents/refilc/app/naplo/filcnaplo
|
||||
COCOAPODS_PARALLEL_CODE_SIGN=true
|
||||
FLUTTER_TARGET=/Users/kima/Documents/refilc/app/naplo/filcnaplo/lib/main.dart
|
||||
FLUTTER_BUILD_DIR=build
|
||||
FLUTTER_BUILD_NAME=4.1.0
|
||||
FLUTTER_BUILD_NUMBER=213
|
||||
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386
|
||||
EXCLUDED_ARCHS[sdk=iphoneos*]=armv7
|
||||
DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9XRUJfQ0FOVkFTS0lUX1VSTD1odHRwczovL3d3dy5nc3RhdGljLmNvbS9mbHV0dGVyLWNhbnZhc2tpdC8yYTM0MDFjOWJiYjVhOWE5YWVjNzRkNGY3MzVkMThhOWRkM2ViZjJkLw==
|
||||
DART_OBFUSCATION=false
|
||||
TRACK_WIDGET_CREATION=true
|
||||
TREE_SHAKE_ICONS=false
|
||||
PACKAGE_CONFIG=/Users/kima/Documents/refilc/app/naplo/filcnaplo/.dart_tool/package_config.json
|
||||
14
filcnaplo/ios/Flutter/flutter_export_environment 2.sh
Executable file
14
filcnaplo/ios/Flutter/flutter_export_environment 2.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
export "FLUTTER_ROOT=/Users/kima/development/flutter"
|
||||
export "FLUTTER_APPLICATION_PATH=/Users/kima/Documents/refilc/app/naplo/filcnaplo"
|
||||
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
|
||||
export "FLUTTER_TARGET=/Users/kima/Documents/refilc/app/naplo/filcnaplo/lib/main.dart"
|
||||
export "FLUTTER_BUILD_DIR=build"
|
||||
export "FLUTTER_BUILD_NAME=4.1.0"
|
||||
export "FLUTTER_BUILD_NUMBER=213"
|
||||
export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9XRUJfQ0FOVkFTS0lUX1VSTD1odHRwczovL3d3dy5nc3RhdGljLmNvbS9mbHV0dGVyLWNhbnZhc2tpdC8yYTM0MDFjOWJiYjVhOWE5YWVjNzRkNGY3MzVkMThhOWRkM2ViZjJkLw=="
|
||||
export "DART_OBFUSCATION=false"
|
||||
export "TRACK_WIDGET_CREATION=true"
|
||||
export "TREE_SHAKE_ICONS=false"
|
||||
export "PACKAGE_CONFIG=/Users/kima/Documents/refilc/app/naplo/filcnaplo/.dart_tool/package_config.json"
|
||||
@@ -204,7 +204,7 @@ SPEC CHECKSUMS:
|
||||
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
|
||||
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
|
||||
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||
file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95
|
||||
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89
|
||||
flutter_image_compress: 5a5e9aee05b6553048b8df1c3bc456d0afaac433
|
||||
@@ -218,14 +218,14 @@ SPEC CHECKSUMS:
|
||||
live_activities: 9ff56a06a2d43ecd68f56deeed13b18a8304789c
|
||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||
open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
|
||||
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
||||
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
||||
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
|
||||
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
|
||||
quick_actions_ios: 9e80dcfadfbc5d47d9cf8f47bcf428b11cf383d4
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866
|
||||
SDWebImageWebPCoder: 18503de6621dd2c420d680e33d46bf8e1d5169b0
|
||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028
|
||||
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
||||
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
||||
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
|
||||
|
||||
@@ -488,7 +488,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.6.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@@ -526,7 +526,7 @@
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo.livecardpro;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo.livecardpro;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -567,7 +567,7 @@
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo.livecardpro;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo.livecardpro;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -607,7 +607,7 @@
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo.livecardpro;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo.livecardpro;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -746,7 +746,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.6.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
@@ -774,7 +774,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.6.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:filcnaplo/models/release.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/models/supporter.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/school.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
|
||||
@@ -68,7 +69,9 @@ class FilcAPI {
|
||||
http.Response res = await http.get(Uri.parse(config), headers: headers);
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
print(jsonDecode(res.body));
|
||||
if (kDebugMode) {
|
||||
print(jsonDecode(res.body));
|
||||
}
|
||||
return Config.fromJson(jsonDecode(res.body));
|
||||
} else if (res.statusCode == 429) {
|
||||
res = await http.get(Uri.parse(config));
|
||||
|
||||
@@ -49,22 +49,22 @@ Future loginApi({
|
||||
}) async {
|
||||
Provider.of<KretaClient>(context, listen: false).userAgent =
|
||||
Provider.of<SettingsProvider>(context, listen: false).config.userAgent;
|
||||
|
||||
|
||||
Map<String, String> headers = {
|
||||
"content-type": "application/x-www-form-urlencoded",
|
||||
};
|
||||
|
||||
|
||||
String nonceStr = await Provider.of<KretaClient>(context, listen: false)
|
||||
.getAPI(KretaAPI.nonce, json: false);
|
||||
|
||||
Nonce nonce = getNonce(nonceStr, username.replaceAll(' ', '') + ' ', instituteCode);
|
||||
|
||||
Nonce nonce = getNonce(nonceStr, username, instituteCode);
|
||||
headers.addAll(nonce.header());
|
||||
|
||||
Map? res = await Provider.of<KretaClient>(context, listen: false)
|
||||
.postAPI(KretaAPI.login,
|
||||
headers: headers,
|
||||
body: User.loginBody(
|
||||
username: username.replaceAll(' ', '') + ' ',
|
||||
username: username,
|
||||
password: password,
|
||||
instituteCode: instituteCode,
|
||||
));
|
||||
@@ -83,7 +83,7 @@ Future loginApi({
|
||||
.getAPI(KretaAPI.student(instituteCode));
|
||||
Student student = Student.fromJson(studentJson!);
|
||||
var user = User(
|
||||
username: username.replaceAll(' ', '') + ' ',
|
||||
username: username,
|
||||
password: password,
|
||||
instituteCode: instituteCode,
|
||||
name: student.name,
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'dart:io';
|
||||
|
||||
import 'package:filcnaplo/database/query.dart';
|
||||
import 'package:filcnaplo/database/store.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
// ignore: depend_on_referenced_packages
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@ enum LiveCardState {
|
||||
duringBreak,
|
||||
morning,
|
||||
afternoon,
|
||||
night
|
||||
night,
|
||||
summary
|
||||
}
|
||||
|
||||
class LiveCardProvider extends ChangeNotifier {
|
||||
@@ -39,6 +40,8 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
String? _latestActivityId;
|
||||
Map<String, String> _lastActivity = {};
|
||||
|
||||
bool _hasCheckedTimetable = false;
|
||||
|
||||
LiveCardProvider({
|
||||
required TimetableProvider timetable,
|
||||
required SettingsProvider settings,
|
||||
@@ -47,7 +50,9 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
// Check if live card is enabled .areActivitiesEnabled()
|
||||
_liveActivitiesPlugin.areActivitiesEnabled().then((value) {
|
||||
// Console log
|
||||
print("Live card enabled: $value");
|
||||
if (kDebugMode) {
|
||||
print("Live card enabled: $value");
|
||||
}
|
||||
|
||||
if (value) {
|
||||
_liveActivitiesPlugin.init(appGroupId: "group.refilc.livecard");
|
||||
@@ -195,7 +200,8 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
|
||||
List<Lesson> today = _today(_timetable);
|
||||
|
||||
if (today.isEmpty) {
|
||||
if (today.isEmpty && !_hasCheckedTimetable) {
|
||||
_hasCheckedTimetable = true;
|
||||
await _timetable.fetch(week: Week.current());
|
||||
today = _today(_timetable);
|
||||
}
|
||||
@@ -249,7 +255,10 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
if (currentLesson != null) {
|
||||
if (now.isBefore(DateTime(now.year, DateTime.august, 31)) &&
|
||||
now.isAfter(DateTime(now.year, DateTime.june, 14))) {
|
||||
currentState = LiveCardState.summary;
|
||||
} else if (currentLesson != null) {
|
||||
currentState = LiveCardState.duringLesson;
|
||||
} else if (nextLesson != null && prevLesson != null) {
|
||||
currentState = LiveCardState.duringBreak;
|
||||
|
||||
@@ -5,28 +5,36 @@ import 'dart:io';
|
||||
import 'package:filcnaplo/api/providers/database_provider.dart';
|
||||
import 'package:filcnaplo/database/struct.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
// ignore: depend_on_referenced_packages
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
|
||||
const settingsDB = DatabaseStruct("settings", {
|
||||
"language": String, "start_page": int, "rounding": int, "theme": int, "accent_color": int, "news": int, "news_state": int, "developer_mode": int,
|
||||
"update_channel": int, "config": String, "custom_accent_color": int, "custom_background_color": int, "custom_highlight_color": int, // general
|
||||
"grade_color1": int, "grade_color2": int, "grade_color3": int, "grade_color4": int, "grade_color5": int, // grade colors
|
||||
"language": String, "start_page": int, "rounding": int, "theme": int,
|
||||
"accent_color": int, "news": int, "news_state": int, "developer_mode": int,
|
||||
"update_channel": int, "config": String, "custom_accent_color": int,
|
||||
"custom_background_color": int, "custom_highlight_color": int, // general
|
||||
"grade_color1": int, "grade_color2": int, "grade_color3": int,
|
||||
"grade_color4": int, "grade_color5": int, // grade colors
|
||||
"vibration_strength": int, "ab_weeks": int, "swap_ab_weeks": int,
|
||||
"notifications": int, "notifications_bitfield": int, "notification_poll_interval": int, // notifications
|
||||
"x_filc_id": String, "graph_class_avg": int, "presentation_mode": int, "bell_delay": int, "bell_delay_enabled": int,
|
||||
"grade_opening_fun": int, "icon_pack": String, "premium_scopes": String, "premium_token": String, "premium_login": String,
|
||||
"notifications": int, "notifications_bitfield": int,
|
||||
"notification_poll_interval": int, // notifications
|
||||
"x_filc_id": String, "graph_class_avg": int, "presentation_mode": int,
|
||||
"bell_delay": int, "bell_delay_enabled": int,
|
||||
"grade_opening_fun": int, "icon_pack": String, "premium_scopes": String,
|
||||
"premium_token": String, "premium_login": String,
|
||||
"last_account_id": String, "renamed_subjects_enabled": int,
|
||||
"renamed_subjects_italics": int,
|
||||
});
|
||||
// DON'T FORGET TO UPDATE DEFAULT VALUES IN `initDB` MIGRATION OR ELSE PARENTS WILL COMPLAIN ABOUT THEIR CHILDREN MISSING
|
||||
// YOU'VE BEEN WARNED!!!
|
||||
const usersDB = DatabaseStruct("users", {
|
||||
"id": String, "name": String, "username": String, "password": String, "institute_code": String, "student": String, "role": int,
|
||||
"id": String, "name": String, "username": String, "password": String,
|
||||
"institute_code": String, "student": String, "role": int,
|
||||
"nickname": String, "picture": String // premium only
|
||||
});
|
||||
const userDataDB = DatabaseStruct("user_data", {
|
||||
"id": String, "grades": String, "timetable": String, "exams": String, "homework": String, "messages": String, "notes": String,
|
||||
"id": String, "grades": String, "timetable": String, "exams": String,
|
||||
"homework": String, "messages": String, "notes": String,
|
||||
"events": String, "absences": String, "group_averages": String,
|
||||
// renamed subjects // non kreta data
|
||||
"renamed_subjects": String,
|
||||
@@ -34,7 +42,8 @@ const userDataDB = DatabaseStruct("user_data", {
|
||||
"last_seen_grade": int,
|
||||
});
|
||||
|
||||
Future<void> createTable(Database db, DatabaseStruct struct) => db.execute("CREATE TABLE IF NOT EXISTS ${struct.table} ($struct)");
|
||||
Future<void> createTable(Database db, DatabaseStruct struct) =>
|
||||
db.execute("CREATE TABLE IF NOT EXISTS ${struct.table} ($struct)");
|
||||
|
||||
Future<Database> initDB(DatabaseProvider database) async {
|
||||
Database db;
|
||||
@@ -50,9 +59,11 @@ Future<Database> initDB(DatabaseProvider database) async {
|
||||
await createTable(db, usersDB);
|
||||
await createTable(db, userDataDB);
|
||||
|
||||
if ((await db.rawQuery("SELECT COUNT(*) FROM settings"))[0].values.first == 0) {
|
||||
if ((await db.rawQuery("SELECT COUNT(*) FROM settings"))[0].values.first ==
|
||||
0) {
|
||||
// Set default values for table Settings
|
||||
await db.insert("settings", SettingsProvider.defaultSettings(database: database).toMap());
|
||||
await db.insert("settings",
|
||||
SettingsProvider.defaultSettings(database: database).toMap());
|
||||
}
|
||||
|
||||
// Migrate Databases
|
||||
@@ -60,7 +71,8 @@ Future<Database> initDB(DatabaseProvider database) async {
|
||||
await migrateDB(
|
||||
db,
|
||||
struct: settingsDB,
|
||||
defaultValues: SettingsProvider.defaultSettings(database: database).toMap(),
|
||||
defaultValues:
|
||||
SettingsProvider.defaultSettings(database: database).toMap(),
|
||||
);
|
||||
await migrateDB(
|
||||
db,
|
||||
@@ -68,7 +80,8 @@ Future<Database> initDB(DatabaseProvider database) async {
|
||||
defaultValues: {"role": 0, "nickname": "", "picture": ""},
|
||||
);
|
||||
await migrateDB(db, struct: userDataDB, defaultValues: {
|
||||
"grades": "[]", "timetable": "[]", "exams": "[]", "homework": "[]", "messages": "[]", "notes": "[]", "events": "[]", "absences": "[]",
|
||||
"grades": "[]", "timetable": "[]", "exams": "[]", "homework": "[]",
|
||||
"messages": "[]", "notes": "[]", "events": "[]", "absences": "[]",
|
||||
"group_averages": "[]",
|
||||
// renamed subjects // non kreta data
|
||||
"renamed_subjects": "{}",
|
||||
@@ -99,7 +112,8 @@ Future<void> migrateDB(
|
||||
|
||||
// go through each row and add missing keys or delete non existing keys
|
||||
await Future.forEach<Map<String, Object?>>(originalRows, (original) async {
|
||||
bool migrationRequired = struct.struct.keys.any((key) => !original.containsKey(key) || original[key] == null) ||
|
||||
bool migrationRequired = struct.struct.keys.any(
|
||||
(key) => !original.containsKey(key) || original[key] == null) ||
|
||||
original.keys.any((key) => !struct.struct.containsKey(key));
|
||||
|
||||
if (migrationRequired) {
|
||||
|
||||
76
filcnaplo/lib/helpers/notification_helper.dart
Normal file
76
filcnaplo/lib/helpers/notification_helper.dart
Normal file
@@ -0,0 +1,76 @@
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:filcnaplo/api/providers/database_provider.dart';
|
||||
import 'package:filcnaplo/api/providers/status_provider.dart';
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/helpers/notification_helper.i18n.dart';
|
||||
import 'package:filcnaplo_kreta_api/client/client.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
|
||||
class NotificationsHelper {
|
||||
@pragma('vm:entry-point')
|
||||
void backgroundJob() async {
|
||||
// initialize providers
|
||||
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
DatabaseProvider database = DatabaseProvider();
|
||||
await database.init();
|
||||
SettingsProvider settingsProvider =
|
||||
await database.query.getSettings(database);
|
||||
UserProvider userProvider = await database.query.getUsers(settingsProvider);
|
||||
|
||||
if (userProvider.id != null && settingsProvider.notificationsEnabled) {
|
||||
// refresh grades
|
||||
final status = StatusProvider();
|
||||
final kretaClient = KretaClient(
|
||||
user: userProvider, settings: settingsProvider, status: status);
|
||||
kretaClient.refreshLogin();
|
||||
GradeProvider gradeProvider = GradeProvider(
|
||||
settings: settingsProvider,
|
||||
user: userProvider,
|
||||
database: database,
|
||||
kreta: kretaClient);
|
||||
gradeProvider.fetch();
|
||||
List<Grade> grades =
|
||||
await database.userQuery.getGrades(userId: userProvider.id ?? "");
|
||||
DateTime lastSeenGrade =
|
||||
await database.userQuery.lastSeenGrade(userId: userProvider.id ?? "");
|
||||
|
||||
// loop through grades and see which hasn't been seen yet
|
||||
for (Grade grade in grades) {
|
||||
// if the grade was added over a week ago, don't show it to avoid notification spam
|
||||
if (grade.seenDate.isAfter(lastSeenGrade) &&
|
||||
grade.date.difference(DateTime.now()).inDays * -1 < 7) {
|
||||
// send notificiation about new grade
|
||||
const AndroidNotificationDetails androidNotificationDetails =
|
||||
AndroidNotificationDetails('GRADES', 'Jegyek',
|
||||
channelDescription: 'Értesítés jegyek beírásakor',
|
||||
importance: Importance.max,
|
||||
priority: Priority.max,
|
||||
color: Color(0xFF3D7BF4),
|
||||
ticker: 'Jegyek');
|
||||
const NotificationDetails notificationDetails =
|
||||
NotificationDetails(android: androidNotificationDetails);
|
||||
await flutterLocalNotificationsPlugin.show(
|
||||
// probably shouldn't use a random int
|
||||
Random().nextInt(432234 * 2),
|
||||
"title".i18n,
|
||||
"body".i18n.fill([
|
||||
grade.value.value.toString(),
|
||||
grade.subject.isRenamed &&
|
||||
settingsProvider.renamedSubjectsEnabled
|
||||
? grade.subject.renamedTo!
|
||||
: grade.subject.name
|
||||
]),
|
||||
notificationDetails);
|
||||
}
|
||||
}
|
||||
// set grade seen status
|
||||
gradeProvider.seenAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
filcnaplo/lib/helpers/notification_helper.i18n.dart
Normal file
24
filcnaplo/lib/helpers/notification_helper.i18n.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"title": "New grade",
|
||||
"body": "You got a %s in %s"
|
||||
},
|
||||
"hu_hu": {
|
||||
"title": "Új jegy",
|
||||
"body": "%s-st kaptál %s tantárgyból"
|
||||
},
|
||||
"de_de": {
|
||||
"title": "Neue Note",
|
||||
"body": "Du hast eine %s in %s"
|
||||
},
|
||||
};
|
||||
|
||||
String get i18n => localize(this, _t);
|
||||
String fill(List<Object> params) => localizeFill(this, params);
|
||||
String plural(int value) => localizePlural(value, this, _t);
|
||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
||||
}
|
||||
@@ -19,7 +19,7 @@ extension UpdateHelper on Release {
|
||||
updateCallback!(-1, UpdateState.preparing);
|
||||
|
||||
String downloads = await StorageHelper.downloadsPath();
|
||||
File apk = File("$downloads/refilc-$version.apk");
|
||||
File apk = File("$downloads/refilc-v$version.apk");
|
||||
|
||||
if (!await apk.exists()) {
|
||||
updateCallback(-1, UpdateState.downloading);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:background_fetch/background_fetch.dart';
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo/api/providers/database_provider.dart';
|
||||
import 'package:filcnaplo/database/init.dart';
|
||||
import 'package:filcnaplo/helpers/notification_helper.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -9,6 +12,7 @@ import 'package:filcnaplo/app.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:filcnaplo_mobile_ui/screens/error_screen.dart';
|
||||
import 'package:filcnaplo_mobile_ui/screens/error_report_screen.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
|
||||
void main() async {
|
||||
// Initalize
|
||||
@@ -43,6 +47,57 @@ class Startup {
|
||||
await database.init();
|
||||
settings = await database.query.getSettings(database);
|
||||
user = await database.query.getUsers(settings);
|
||||
|
||||
// Notifications setup
|
||||
initPlatformState();
|
||||
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
|
||||
// Get permission to show notifications
|
||||
if (Platform.isAndroid) {
|
||||
await flutterLocalNotificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin>()!
|
||||
.requestPermission();
|
||||
} else if (Platform.isIOS) {
|
||||
await flutterLocalNotificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<
|
||||
IOSFlutterLocalNotificationsPlugin>()
|
||||
?.requestPermissions(
|
||||
alert: false,
|
||||
badge: true,
|
||||
sound: true,
|
||||
);
|
||||
} else if (Platform.isMacOS) {
|
||||
await flutterLocalNotificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<
|
||||
MacOSFlutterLocalNotificationsPlugin>()
|
||||
?.requestPermissions(
|
||||
alert: false,
|
||||
badge: true,
|
||||
sound: true,
|
||||
);
|
||||
}
|
||||
|
||||
// Platform specific settings
|
||||
const DarwinInitializationSettings initializationSettingsDarwin =
|
||||
DarwinInitializationSettings(
|
||||
requestSoundPermission: true,
|
||||
requestBadgePermission: true,
|
||||
requestAlertPermission: false,
|
||||
);
|
||||
const AndroidInitializationSettings initializationSettingsAndroid =
|
||||
AndroidInitializationSettings('ic_notification');
|
||||
const InitializationSettings initializationSettings =
|
||||
InitializationSettings(
|
||||
android: initializationSettingsAndroid,
|
||||
iOS: initializationSettingsDarwin,
|
||||
macOS: initializationSettingsDarwin);
|
||||
|
||||
// Initialize notifications
|
||||
await flutterLocalNotificationsPlugin.initialize(
|
||||
initializationSettings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +127,58 @@ Widget errorBuilder(FlutterErrorDetails details) {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> initPlatformState() async {
|
||||
// Configure BackgroundFetch.
|
||||
int status = await BackgroundFetch.configure(
|
||||
BackgroundFetchConfig(
|
||||
minimumFetchInterval: 15,
|
||||
stopOnTerminate: false,
|
||||
enableHeadless: true,
|
||||
requiresBatteryNotLow: false,
|
||||
requiresCharging: false,
|
||||
requiresStorageNotLow: false,
|
||||
requiresDeviceIdle: false,
|
||||
requiredNetworkType: NetworkType.ANY,
|
||||
startOnBoot: true), (String taskId) async {
|
||||
// <-- Event handler
|
||||
if (kDebugMode) {
|
||||
print("[BackgroundFetch] Event received $taskId");
|
||||
}
|
||||
NotificationsHelper().backgroundJob();
|
||||
BackgroundFetch.finish(taskId);
|
||||
}, (String taskId) async {
|
||||
// <-- Task timeout handler.
|
||||
if (kDebugMode) {
|
||||
print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
|
||||
}
|
||||
BackgroundFetch.finish(taskId);
|
||||
});
|
||||
if (kDebugMode) {
|
||||
print('[BackgroundFetch] configure success: $status');
|
||||
}
|
||||
BackgroundFetch.scheduleTask(TaskConfig(
|
||||
taskId: "com.transistorsoft.refilcnotification",
|
||||
delay: 900000, // 15 minutes
|
||||
periodic: true,
|
||||
forceAlarmManager: true,
|
||||
stopOnTerminate: false,
|
||||
enableHeadless: true));
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void backgroundHeadlessTask(HeadlessTask task) {
|
||||
print('[BackgroundFetch] Headless event received.');
|
||||
String taskId = task.taskId;
|
||||
bool isTimeout = task.timeout;
|
||||
if (isTimeout) {
|
||||
if (kDebugMode) {
|
||||
print("[BackgroundFetch] Headless task timed-out: $taskId");
|
||||
}
|
||||
BackgroundFetch.finish(taskId);
|
||||
return;
|
||||
}
|
||||
if (kDebugMode) {
|
||||
print('[BackgroundFetch] Headless event received.');
|
||||
}
|
||||
NotificationsHelper().backgroundJob();
|
||||
BackgroundFetch.finish(task.taskId);
|
||||
}
|
||||
|
||||
21
filcnaplo/lib/models/personality.dart
Normal file
21
filcnaplo/lib/models/personality.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
class Personality {
|
||||
PersonalityType type;
|
||||
|
||||
Personality({
|
||||
this.type = PersonalityType.npc,
|
||||
});
|
||||
}
|
||||
|
||||
enum PersonalityType {
|
||||
geek,
|
||||
sick,
|
||||
late,
|
||||
quitter,
|
||||
healthy,
|
||||
acceptable,
|
||||
fallible,
|
||||
average,
|
||||
diligent,
|
||||
cheater,
|
||||
npc
|
||||
}
|
||||
@@ -68,6 +68,7 @@ class SettingsProvider extends ChangeNotifier {
|
||||
String _premiumLogin;
|
||||
String _lastAccountId;
|
||||
bool _renamedSubjectsEnabled;
|
||||
bool _renamedSubjectsItalics;
|
||||
|
||||
SettingsProvider({
|
||||
DatabaseProvider? database,
|
||||
@@ -104,6 +105,7 @@ class SettingsProvider extends ChangeNotifier {
|
||||
required String premiumLogin,
|
||||
required String lastAccountId,
|
||||
required bool renameSubjectsEnabled,
|
||||
required bool renameSubjectsItalics,
|
||||
}) : _database = database,
|
||||
_language = language,
|
||||
_startPage = startPage,
|
||||
@@ -137,7 +139,8 @@ class SettingsProvider extends ChangeNotifier {
|
||||
_premiumAccessToken = premiumAccessToken,
|
||||
_premiumLogin = premiumLogin,
|
||||
_lastAccountId = lastAccountId,
|
||||
_renamedSubjectsEnabled = renameSubjectsEnabled;
|
||||
_renamedSubjectsEnabled = renameSubjectsEnabled,
|
||||
_renamedSubjectsItalics = renameSubjectsItalics;
|
||||
|
||||
factory SettingsProvider.fromMap(Map map,
|
||||
{required DatabaseProvider database}) {
|
||||
@@ -191,6 +194,7 @@ class SettingsProvider extends ChangeNotifier {
|
||||
premiumLogin: map["premium_login"],
|
||||
lastAccountId: map["last_account_id"],
|
||||
renameSubjectsEnabled: map["renamed_subjects_enabled"] == 1,
|
||||
renameSubjectsItalics: map["renamed_subjects_italics"] == 1,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -231,7 +235,8 @@ class SettingsProvider extends ChangeNotifier {
|
||||
"premium_token": _premiumAccessToken,
|
||||
"premium_login": _premiumLogin,
|
||||
"last_account_id": _lastAccountId,
|
||||
"renamed_subjects_enabled": _renamedSubjectsEnabled ? 1 : 0
|
||||
"renamed_subjects_enabled": _renamedSubjectsEnabled ? 1 : 0,
|
||||
"renamed_subjects_italics": _renamedSubjectsItalics ? 1 : 0
|
||||
};
|
||||
}
|
||||
|
||||
@@ -277,6 +282,7 @@ class SettingsProvider extends ChangeNotifier {
|
||||
premiumLogin: "igen",
|
||||
lastAccountId: "",
|
||||
renameSubjectsEnabled: false,
|
||||
renameSubjectsItalics: false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -317,6 +323,7 @@ class SettingsProvider extends ChangeNotifier {
|
||||
String get premiumLogin => _premiumLogin;
|
||||
String get lastAccountId => _lastAccountId;
|
||||
bool get renamedSubjectsEnabled => _renamedSubjectsEnabled;
|
||||
bool get renamedSubjectsItalics => _renamedSubjectsItalics;
|
||||
|
||||
Future<void> update({
|
||||
bool store = true,
|
||||
@@ -353,69 +360,94 @@ class SettingsProvider extends ChangeNotifier {
|
||||
String? premiumLogin,
|
||||
String? lastAccountId,
|
||||
bool? renamedSubjectsEnabled,
|
||||
bool? renamedSubjectsItalics,
|
||||
}) async {
|
||||
if (language != null && language != _language) _language = language;
|
||||
if (startPage != null && startPage != _startPage) _startPage = startPage;
|
||||
if (rounding != null && rounding != _rounding) _rounding = rounding;
|
||||
if (theme != null && theme != _theme) _theme = theme;
|
||||
if (accentColor != null && accentColor != _accentColor)
|
||||
if (accentColor != null && accentColor != _accentColor) {
|
||||
_accentColor = accentColor;
|
||||
if (gradeColors != null && gradeColors != _gradeColors)
|
||||
}
|
||||
if (gradeColors != null && gradeColors != _gradeColors) {
|
||||
_gradeColors = gradeColors;
|
||||
if (newsEnabled != null && newsEnabled != _newsEnabled)
|
||||
}
|
||||
if (newsEnabled != null && newsEnabled != _newsEnabled) {
|
||||
_newsEnabled = newsEnabled;
|
||||
}
|
||||
if (newsState != null && newsState != _newsState) _newsState = newsState;
|
||||
if (notificationsEnabled != null &&
|
||||
notificationsEnabled != _notificationsEnabled)
|
||||
notificationsEnabled != _notificationsEnabled) {
|
||||
_notificationsEnabled = notificationsEnabled;
|
||||
}
|
||||
if (notificationsBitfield != null &&
|
||||
notificationsBitfield != _notificationsBitfield)
|
||||
notificationsBitfield != _notificationsBitfield) {
|
||||
_notificationsBitfield = notificationsBitfield;
|
||||
if (developerMode != null && developerMode != _developerMode)
|
||||
}
|
||||
if (developerMode != null && developerMode != _developerMode) {
|
||||
_developerMode = developerMode;
|
||||
}
|
||||
if (notificationPollInterval != null &&
|
||||
notificationPollInterval != _notificationPollInterval) {
|
||||
_notificationPollInterval = notificationPollInterval;
|
||||
}
|
||||
if (vibrate != null && vibrate != _vibrate) _vibrate = vibrate;
|
||||
if (abWeeks != null && abWeeks != _abWeeks) _abWeeks = abWeeks;
|
||||
if (swapABweeks != null && swapABweeks != _swapABweeks)
|
||||
if (swapABweeks != null && swapABweeks != _swapABweeks) {
|
||||
_swapABweeks = swapABweeks;
|
||||
if (updateChannel != null && updateChannel != _updateChannel)
|
||||
}
|
||||
if (updateChannel != null && updateChannel != _updateChannel) {
|
||||
_updateChannel = updateChannel;
|
||||
}
|
||||
if (config != null && config != _config) _config = config;
|
||||
if (xFilcId != null && xFilcId != _xFilcId) _xFilcId = xFilcId;
|
||||
if (graphClassAvg != null && graphClassAvg != _graphClassAvg)
|
||||
if (graphClassAvg != null && graphClassAvg != _graphClassAvg) {
|
||||
_graphClassAvg = graphClassAvg;
|
||||
}
|
||||
if (goodStudent != null) _goodStudent = goodStudent;
|
||||
if (presentationMode != null && presentationMode != _presentationMode)
|
||||
if (presentationMode != null && presentationMode != _presentationMode) {
|
||||
_presentationMode = presentationMode;
|
||||
}
|
||||
if (bellDelay != null && bellDelay != _bellDelay) _bellDelay = bellDelay;
|
||||
if (bellDelayEnabled != null && bellDelayEnabled != _bellDelayEnabled)
|
||||
if (bellDelayEnabled != null && bellDelayEnabled != _bellDelayEnabled) {
|
||||
_bellDelayEnabled = bellDelayEnabled;
|
||||
if (gradeOpeningFun != null && gradeOpeningFun != _gradeOpeningFun)
|
||||
}
|
||||
if (gradeOpeningFun != null && gradeOpeningFun != _gradeOpeningFun) {
|
||||
_gradeOpeningFun = gradeOpeningFun;
|
||||
}
|
||||
if (iconPack != null && iconPack != _iconPack) _iconPack = iconPack;
|
||||
if (customAccentColor != null && customAccentColor != _customAccentColor)
|
||||
if (customAccentColor != null && customAccentColor != _customAccentColor) {
|
||||
_customAccentColor = customAccentColor;
|
||||
}
|
||||
if (customBackgroundColor != null &&
|
||||
customBackgroundColor != _customBackgroundColor)
|
||||
customBackgroundColor != _customBackgroundColor) {
|
||||
_customBackgroundColor = customBackgroundColor;
|
||||
}
|
||||
if (customHighlightColor != null &&
|
||||
customHighlightColor != _customHighlightColor)
|
||||
customHighlightColor != _customHighlightColor) {
|
||||
_customHighlightColor = customHighlightColor;
|
||||
if (premiumScopes != null && premiumScopes != _premiumScopes)
|
||||
}
|
||||
if (premiumScopes != null && premiumScopes != _premiumScopes) {
|
||||
_premiumScopes = premiumScopes;
|
||||
if (premiumAccessToken != null && premiumAccessToken != _premiumAccessToken)
|
||||
}
|
||||
if (premiumAccessToken != null &&
|
||||
premiumAccessToken != _premiumAccessToken) {
|
||||
_premiumAccessToken = premiumAccessToken;
|
||||
if (premiumLogin != null && premiumLogin != _premiumLogin)
|
||||
}
|
||||
if (premiumLogin != null && premiumLogin != _premiumLogin) {
|
||||
_premiumLogin = premiumLogin;
|
||||
if (lastAccountId != null && lastAccountId != _lastAccountId)
|
||||
}
|
||||
if (lastAccountId != null && lastAccountId != _lastAccountId) {
|
||||
_lastAccountId = lastAccountId;
|
||||
}
|
||||
if (renamedSubjectsEnabled != null &&
|
||||
renamedSubjectsEnabled != _renamedSubjectsEnabled)
|
||||
renamedSubjectsEnabled != _renamedSubjectsEnabled) {
|
||||
_renamedSubjectsEnabled = renamedSubjectsEnabled;
|
||||
|
||||
}
|
||||
if (renamedSubjectsItalics != null &&
|
||||
renamedSubjectsItalics != _renamedSubjectsItalics) {
|
||||
_renamedSubjectsItalics = renamedSubjectsItalics;
|
||||
}
|
||||
if (store) await _database?.store.storeSettings(this);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ class DarkMobileAppColors implements ThemeAppColors {
|
||||
final gradeTwo = const Color(0xFFAE3DF4);
|
||||
@override
|
||||
final gradeOne = const Color(0xFFF43DAB);
|
||||
@override
|
||||
final purple = const Color(0xffBF5AF2);
|
||||
@override
|
||||
final pink = const Color(0xffFF375F);
|
||||
|
||||
@@ -89,6 +89,7 @@ Future<List<DateWidget>> getFilterWidgets(FilterType activeData,
|
||||
|
||||
// Grades
|
||||
case FilterType.grades:
|
||||
gradeProvider.seenAll();
|
||||
items = grade_filter.getWidgets(
|
||||
gradeProvider.grades, gradeProvider.lastSeenDate);
|
||||
if (settingsProvider.gradeOpeningFun) {
|
||||
|
||||
@@ -23,6 +23,8 @@ class GradeTile extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
String title;
|
||||
String subtitle;
|
||||
bool isTitleItalic = false;
|
||||
bool isSubtitleItalic = false;
|
||||
EdgeInsets leadingPadding = EdgeInsets.zero;
|
||||
bool isSubjectView = SubjectGradesContainer.of(context) != null;
|
||||
String subjectName =
|
||||
@@ -32,7 +34,8 @@ class GradeTile extends StatelessWidget {
|
||||
|
||||
GradeCalculatorProvider calculatorProvider =
|
||||
Provider.of<GradeCalculatorProvider>(context, listen: false);
|
||||
|
||||
SettingsProvider settingsProvider =
|
||||
Provider.of<SettingsProvider>(context);
|
||||
// Test order:
|
||||
// description
|
||||
// mode
|
||||
@@ -47,6 +50,7 @@ class GradeTile extends StatelessWidget {
|
||||
}
|
||||
} else {
|
||||
title = subjectName;
|
||||
isTitleItalic = grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics;
|
||||
}
|
||||
|
||||
// Test order:
|
||||
@@ -58,6 +62,7 @@ class GradeTile extends StatelessWidget {
|
||||
? modeDescription
|
||||
: ""
|
||||
: subjectName;
|
||||
isSubtitleItalic = isSubjectView ? false : grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics;
|
||||
} else {
|
||||
subtitle = grade.value.valueName.split("(")[0];
|
||||
}
|
||||
@@ -122,7 +127,7 @@ class GradeTile extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontStyle: grade.subject.isRenamed && title == subjectName
|
||||
fontStyle: isTitleItalic
|
||||
? FontStyle.italic
|
||||
: null),
|
||||
),
|
||||
@@ -144,7 +149,7 @@ class GradeTile extends StatelessWidget {
|
||||
subtitle,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: isSubtitleItalic ? FontStyle.italic : null),
|
||||
)
|
||||
: null,
|
||||
trailing: isSubjectView
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/exam_provider.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/homework_provider.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
@@ -30,6 +31,8 @@ class LessonTile extends StatelessWidget {
|
||||
bool fill = false;
|
||||
bool fillLeading = false;
|
||||
String lessonIndexTrailing = "";
|
||||
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
|
||||
// Only put a trailing . if its a digit
|
||||
if (RegExp(r'\d').hasMatch(lesson.lessonIndex)) lessonIndexTrailing = ".";
|
||||
@@ -159,7 +162,7 @@ class LessonTile extends StatelessWidget {
|
||||
.text
|
||||
.withOpacity(!lesson.isEmpty ? 1.0 : 0.5),
|
||||
fontStyle:
|
||||
lesson.subject.isRenamed ? FontStyle.italic : null),
|
||||
lesson.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: description != ""
|
||||
? Text(
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <dynamic_color/dynamic_color_plugin.h>
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <flutter_acrylic/flutter_acrylic_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
@@ -14,6 +15,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
|
||||
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
|
||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) flutter_acrylic_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterAcrylicPlugin");
|
||||
flutter_acrylic_plugin_register_with_registrar(flutter_acrylic_registrar);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
dynamic_color
|
||||
file_selector_linux
|
||||
flutter_acrylic
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
@@ -7,19 +7,21 @@ import Foundation
|
||||
|
||||
import connectivity_plus
|
||||
import dynamic_color
|
||||
import flutter_acrylic
|
||||
import file_selector_macos
|
||||
import flutter_local_notifications
|
||||
import macos_window_utils
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import share_plus_macos
|
||||
import share_plus
|
||||
import sqflite
|
||||
import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
||||
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||
FlutterAcrylicPlugin.register(with: registry.registrar(forPlugin: "FlutterAcrylicPlugin"))
|
||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||
MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin"))
|
||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
|
||||
@@ -3,7 +3,7 @@ description: "Nem hivatalos e-napló alkalmazás az e-Kréta rendszerhez"
|
||||
homepage: https://refilc.hu
|
||||
publish_to: "none"
|
||||
|
||||
version: 4.0.4+212
|
||||
version: 4.1.1+215
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
@@ -38,7 +38,7 @@ dependencies:
|
||||
ref: master
|
||||
path_provider: ^2.0.2
|
||||
permission_handler: ^10.2.0
|
||||
share_plus: ^5.0.0
|
||||
share_plus: ^7.0.2
|
||||
connectivity_plus: ^4.0.1
|
||||
flutter_displaymode: ^0.6.0
|
||||
quick_actions: ^1.0.1
|
||||
@@ -48,17 +48,14 @@ dependencies:
|
||||
crypto: ^3.0.2
|
||||
elegant_notification: ^1.6.1
|
||||
flutter_feather_icons: ^2.0.0+1
|
||||
live_activities: ^1.0.0
|
||||
live_activities: ^1.7.4
|
||||
animated_flip_counter: ^0.2.5
|
||||
lottie: ^1.4.3
|
||||
rive: ^0.9.1
|
||||
animated_background: ^2.0.0
|
||||
dropdown_button2: ^1.8.9
|
||||
home_widget: ^0.1.6
|
||||
flutter_expandable_fab:
|
||||
git:
|
||||
url: https://github.com/filc/flutter_expandable_fab
|
||||
ref: master
|
||||
flutter_expandable_fab: ^1.8.1
|
||||
uni_links: ^0.5.1
|
||||
url_launcher: ^6.1.6
|
||||
workmanager: ^0.5.1
|
||||
@@ -67,7 +64,8 @@ dependencies:
|
||||
animations: ^2.0.1
|
||||
background_fetch: ^1.1.5
|
||||
flutter_local_notifications: ^14.1.0
|
||||
package_info_plus: ^3.1.2
|
||||
package_info_plus: ^4.0.2
|
||||
screenshot: ^2.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^2.0.1
|
||||
|
||||
@@ -16,13 +16,15 @@ import 'package:filcnaplo_kreta_api/client/client.dart';
|
||||
class NavigationScreen extends StatefulWidget {
|
||||
const NavigationScreen({Key? key}) : super(key: key);
|
||||
|
||||
static NavigationScreenState? of(BuildContext context) => context.findAncestorStateOfType<NavigationScreenState>();
|
||||
static NavigationScreenState? of(BuildContext context) =>
|
||||
context.findAncestorStateOfType<NavigationScreenState>();
|
||||
|
||||
@override
|
||||
State<NavigationScreen> createState() => NavigationScreenState();
|
||||
}
|
||||
|
||||
class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingObserver {
|
||||
class NavigationScreenState extends State<NavigationScreen>
|
||||
with WidgetsBindingObserver {
|
||||
final _navigatorState = GlobalKey<NavigatorState>();
|
||||
late NavigationRoute selected;
|
||||
late SettingsProvider settings;
|
||||
@@ -40,7 +42,8 @@ class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingO
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
// set client User-Agent
|
||||
Provider.of<KretaClient>(context, listen: false).userAgent = settings.config.userAgent;
|
||||
Provider.of<KretaClient>(context, listen: false).userAgent =
|
||||
settings.config.userAgent;
|
||||
|
||||
// Get news
|
||||
newsProvider = Provider.of<NewsProvider>(context, listen: false);
|
||||
@@ -54,7 +57,11 @@ class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingO
|
||||
await Window.initialize();
|
||||
} catch (_) {}
|
||||
// Transparent sidebar
|
||||
await Window.setEffect(effect: WindowEffect.acrylic, color: Platform.isMacOS ? Colors.transparent : const Color.fromARGB(27, 27, 27, 27));
|
||||
await Window.setEffect(
|
||||
effect: WindowEffect.acrylic,
|
||||
color: Platform.isMacOS
|
||||
? Colors.transparent
|
||||
: const Color.fromARGB(27, 27, 27, 27));
|
||||
|
||||
// todo: do for windows
|
||||
if (Platform.isMacOS) {
|
||||
@@ -75,8 +82,12 @@ class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingO
|
||||
@override
|
||||
void didChangePlatformBrightness() {
|
||||
if (settings.theme == ThemeMode.system) {
|
||||
Brightness? brightness = WidgetsBinding.instance.window.platformBrightness;
|
||||
Provider.of<ThemeModeObserver>(context, listen: false).changeTheme(brightness == Brightness.light ? ThemeMode.light : ThemeMode.dark);
|
||||
// ignore: deprecated_member_use
|
||||
Brightness? brightness =
|
||||
// ignore: deprecated_member_use
|
||||
WidgetsBinding.instance.window.platformBrightness;
|
||||
Provider.of<ThemeModeObserver>(context, listen: false).changeTheme(
|
||||
brightness == Brightness.light ? ThemeMode.light : ThemeMode.dark);
|
||||
}
|
||||
super.didChangePlatformBrightness();
|
||||
}
|
||||
@@ -103,8 +114,12 @@ class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingO
|
||||
if (_navigatorState.currentState != null)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor.withOpacity(.5),
|
||||
border: Border(right: BorderSide(color: AppColors.of(context).shadow.withOpacity(.7), width: 1.0)),
|
||||
color:
|
||||
Theme.of(context).scaffoldBackgroundColor.withOpacity(.5),
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: AppColors.of(context).shadow.withOpacity(.7),
|
||||
width: 1.0)),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: topInset),
|
||||
@@ -125,7 +140,8 @@ class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingO
|
||||
child: Navigator(
|
||||
key: _navigatorState,
|
||||
initialRoute: selected.name,
|
||||
onGenerateRoute: (settings) => navigationRouteHandler(settings),
|
||||
onGenerateRoute: (settings) =>
|
||||
navigationRouteHandler(settings),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -23,10 +23,7 @@ dependencies:
|
||||
animations: ^2.0.1
|
||||
confetti: ^0.6.0
|
||||
auto_size_text: ^3.0.0
|
||||
flutter_acrylic:
|
||||
git:
|
||||
url: https://github.com/filc/flutter_acrylic
|
||||
ref: master
|
||||
flutter_acrylic: ^1.1.3
|
||||
elegant_notification: ^1.6.1
|
||||
|
||||
dev_dependencies:
|
||||
|
||||
@@ -169,7 +169,7 @@ class KretaClient {
|
||||
Map? loginRes = await postAPI(KretaAPI.login,
|
||||
headers: headers,
|
||||
body: User.loginBody(
|
||||
username: loginUser.username.replaceAll(' ', '') + ' ',
|
||||
username: loginUser.username,
|
||||
password: loginUser.password,
|
||||
instituteCode: loginUser.instituteCode,
|
||||
));
|
||||
|
||||
@@ -51,7 +51,6 @@ class GradeProvider with ChangeNotifier {
|
||||
final userStore = _database.userStore;
|
||||
userStore.storeLastSeenGrade(DateTime.now(), userId: userId);
|
||||
_lastSeen = DateTime.now();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +97,11 @@ class GradeProvider with ChangeNotifier {
|
||||
.i18n;
|
||||
grade.value.shortName = _settings.goodStudent
|
||||
? "Jeles".i18n
|
||||
: '${grade.json!["SzovegesErtekelesRovidNev"]}' != "null" && '${grade.json!["SzovegesErtekelesRovidNev"]}' != "-" && '${grade.json!["SzovegesErtekelesRovidNev"]}'.replaceAll(RegExp(r'[0123456789]+[%]?'), '') != ""
|
||||
: '${grade.json!["SzovegesErtekelesRovidNev"]}' != "null" &&
|
||||
'${grade.json!["SzovegesErtekelesRovidNev"]}' != "-" &&
|
||||
'${grade.json!["SzovegesErtekelesRovidNev"]}'
|
||||
.replaceAll(RegExp(r'[0123456789]+[%]?'), '') !=
|
||||
""
|
||||
? '${grade.json!["SzovegesErtekelesRovidNev"]}'.i18n
|
||||
: grade.value.valueName;
|
||||
}
|
||||
@@ -119,14 +122,16 @@ class GradeProvider with ChangeNotifier {
|
||||
if (grades.isNotEmpty || _grades.isNotEmpty) await store(grades);
|
||||
|
||||
List? groupsJson = await _kreta.getAPI(KretaAPI.groups(iss));
|
||||
if (groupsJson == null || groupsJson.isEmpty)
|
||||
if (groupsJson == null || groupsJson.isEmpty) {
|
||||
throw "Cannot fetch Groups for User ${user.id}";
|
||||
}
|
||||
_groups = (groupsJson[0]["OktatasNevelesiFeladat"] ?? {})["Uid"] ?? "";
|
||||
|
||||
List? groupAvgJson =
|
||||
await _kreta.getAPI(KretaAPI.groupAverages(iss, _groups));
|
||||
if (groupAvgJson == null)
|
||||
if (groupAvgJson == null) {
|
||||
throw "Cannot fetch Class Averages for User ${user.id}";
|
||||
}
|
||||
final groupAvgs =
|
||||
groupAvgJson.map((e) => GroupAverage.fromJson(e)).toList();
|
||||
await storeGroupAvg(groupAvgs);
|
||||
|
||||
@@ -37,10 +37,14 @@ class TimetableProvider with ChangeNotifier {
|
||||
// for renamed subjects
|
||||
Future<void> convertBySettings() async {
|
||||
Map<String, String> renamedSubjects =
|
||||
(await _database.query.getSettings(_database)).renamedSubjectsEnabled ? await _database.userQuery.renamedSubjects(userId: _user.id!) : {};
|
||||
(await _database.query.getSettings(_database)).renamedSubjectsEnabled
|
||||
? await _database.userQuery.renamedSubjects(userId: _user.id!)
|
||||
: {};
|
||||
|
||||
for (Lesson lesson in _lessons.values.expand((e) => e)) {
|
||||
lesson.subject.renamedTo = renamedSubjects.isNotEmpty ? renamedSubjects[lesson.subject.id] : null;
|
||||
lesson.subject.renamedTo = renamedSubjects.isNotEmpty
|
||||
? renamedSubjects[lesson.subject.id]
|
||||
: null;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
@@ -54,7 +58,8 @@ class TimetableProvider with ChangeNotifier {
|
||||
User? user = _user.user;
|
||||
if (user == null) throw "Cannot fetch Lessons for User null";
|
||||
String iss = user.instituteCode;
|
||||
List? lessonsJson = await _kreta.getAPI(KretaAPI.timetable(iss, start: week.start, end: week.end));
|
||||
List? lessonsJson = await _kreta
|
||||
.getAPI(KretaAPI.timetable(iss, start: week.start, end: week.end));
|
||||
if (lessonsJson == null) throw "Cannot fetch Lessons for User ${user.id}";
|
||||
List<Lesson> lessons = lessonsJson.map((e) => Lesson.fromJson(e)).toList();
|
||||
|
||||
@@ -72,7 +77,7 @@ class TimetableProvider with ChangeNotifier {
|
||||
if (user == null) throw "Cannot store Lessons for User null";
|
||||
String userId = user.id;
|
||||
|
||||
// TODO: clear indexes with weeks outside of the current school year
|
||||
// -TODO: clear indexes with weeks outside of the current school year
|
||||
await _database.userStore.storeLessons(_lessons, userId: userId);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,7 @@ dependencies:
|
||||
path: ../filcnaplo/
|
||||
http: ^0.13.3
|
||||
provider: ^5.0.0
|
||||
file_picker:
|
||||
git:
|
||||
url: https://github.com/filc/flutter_file_picker.git
|
||||
ref: master
|
||||
file_picker: ^5.3.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^1.0.0
|
||||
|
||||
@@ -23,7 +23,6 @@ class Empty extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// make the face randomness a bit more constant (to avoid strokes)
|
||||
int index = Random(DateTime.now().minute).nextInt(faces.length);
|
||||
|
||||
return Center(
|
||||
@@ -32,9 +31,19 @@ class Empty extends StatelessWidget {
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: faces[index],
|
||||
style: TextStyle(fontSize: 32.0, fontWeight: FontWeight.w500, color: AppColors.of(context).text.withOpacity(.75)),
|
||||
style: TextStyle(
|
||||
fontSize: 32.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.of(context).text.withOpacity(.75)),
|
||||
children: subtitle != null
|
||||
? [TextSpan(text: "\n" + subtitle!, style: TextStyle(fontSize: 18.0, height: 2.0, color: AppColors.of(context).text.withOpacity(.5)))]
|
||||
? [
|
||||
TextSpan(
|
||||
text: "\n" + subtitle!,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
height: 2.0,
|
||||
color: AppColors.of(context).text.withOpacity(.5)))
|
||||
]
|
||||
: [],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class EmptyCard extends StatefulWidget {
|
||||
const EmptyCard({
|
||||
Key? key,
|
||||
required this.text,
|
||||
}) : super(key: key);
|
||||
|
||||
final String text;
|
||||
|
||||
@override
|
||||
State<EmptyCard> createState() => _EmptyCardState();
|
||||
}
|
||||
|
||||
class _EmptyCardState extends State<EmptyCard> {
|
||||
bool hold = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onLongPressDown: (_) => setState(() => hold = true),
|
||||
onLongPressEnd: (_) => setState(() => hold = false),
|
||||
onLongPressCancel: () => setState(() => hold = false),
|
||||
child: AnimatedScale(
|
||||
scale: hold ? 1.018 : 1.0,
|
||||
curve: Curves.easeInOutBack,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: Container(
|
||||
height: 444,
|
||||
padding:
|
||||
const EdgeInsets.only(top: 12, bottom: 12, left: 12, right: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0x280008FF),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.08),
|
||||
offset: const Offset(0, 5),
|
||||
blurRadius: 20,
|
||||
spreadRadius: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: DottedBorder(
|
||||
color: Colors.black.withOpacity(0.9),
|
||||
dashPattern: const [12, 12],
|
||||
padding:
|
||||
const EdgeInsets.only(top: 20, bottom: 20, left: 20, right: 20),
|
||||
child: Center(
|
||||
child: Text(
|
||||
widget.text,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo/helpers/average_helper.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/models/personality.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/subject.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/week.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'personality_card.i18n.dart';
|
||||
|
||||
class PersonalityCard extends StatefulWidget {
|
||||
const PersonalityCard({
|
||||
Key? key,
|
||||
required this.user,
|
||||
}) : super(key: key);
|
||||
|
||||
final UserProvider user;
|
||||
|
||||
@override
|
||||
State<PersonalityCard> createState() => _PersonalityCardState();
|
||||
}
|
||||
|
||||
class _PersonalityCardState extends State<PersonalityCard> {
|
||||
late GradeProvider gradeProvider;
|
||||
late AbsenceProvider absenceProvider;
|
||||
late TimetableProvider timetableProvider;
|
||||
late SettingsProvider settings;
|
||||
|
||||
late List<int> subjectAvgsList = [];
|
||||
late Map<Subject, double> subjectAvgs = {};
|
||||
late double subjectAvg;
|
||||
late List<Grade> classWorkGrades;
|
||||
late Map<int, int> mostCommonGrade;
|
||||
late List<Absence> absences = [];
|
||||
final Map<Subject, Lesson> _lessonCount = {};
|
||||
late int totalDelays;
|
||||
late int unexcusedAbsences;
|
||||
|
||||
late PersonalityType finalPersonality;
|
||||
|
||||
bool hold = false;
|
||||
|
||||
List<Grade> getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider
|
||||
.grades
|
||||
.where((e) =>
|
||||
e.subject == subject &&
|
||||
e.type == GradeType.midYear &&
|
||||
(days == 0 ||
|
||||
e.date.isBefore(DateTime.now().subtract(Duration(days: days)))))
|
||||
.toList();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
gradeProvider = Provider.of<GradeProvider>(context, listen: false);
|
||||
absenceProvider = Provider.of<AbsenceProvider>(context, listen: false);
|
||||
timetableProvider = Provider.of<TimetableProvider>(context, listen: false);
|
||||
settings = Provider.of<SettingsProvider>(context, listen: false);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
for (final lesson in timetableProvider.getWeek(Week.current()) ?? []) {
|
||||
if (!lesson.isEmpty &&
|
||||
lesson.subject.id != '' &&
|
||||
lesson.lessonYearIndex != null) {
|
||||
_lessonCount.update(
|
||||
lesson.subject,
|
||||
(value) {
|
||||
if (lesson.lessonYearIndex! > value.lessonYearIndex!) {
|
||||
return lesson;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
ifAbsent: () => lesson,
|
||||
);
|
||||
}
|
||||
}
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
void getGrades() {
|
||||
List<Subject> subjects = gradeProvider.grades
|
||||
.map((e) => e.subject)
|
||||
.toSet()
|
||||
.toList()
|
||||
..sort((a, b) => a.name.compareTo(b.name));
|
||||
|
||||
for (Subject subject in subjects) {
|
||||
List<Grade> subjectGrades = getSubjectGrades(subject);
|
||||
|
||||
double avg = AverageHelper.averageEvals(subjectGrades);
|
||||
if (avg != 0) subjectAvgs[subject] = avg;
|
||||
|
||||
subjectAvgsList.add(avg.round());
|
||||
}
|
||||
|
||||
subjectAvg = subjectAvgs.isNotEmpty
|
||||
? subjectAvgs.values.fold(0.0, (double a, double b) => a + b) /
|
||||
subjectAvgs.length
|
||||
: 0.0;
|
||||
|
||||
classWorkGrades =
|
||||
gradeProvider.grades.where((a) => a.value.weight <= 75).toList();
|
||||
}
|
||||
|
||||
void getMostCommonGrade() {
|
||||
Map<int, int> counts = {};
|
||||
|
||||
subjectAvgsList.map((e) {
|
||||
if (counts.containsKey(e)) {
|
||||
counts.update(e, (value) => value++);
|
||||
} else {
|
||||
counts[e] = 1;
|
||||
}
|
||||
});
|
||||
|
||||
var maxValue = 0;
|
||||
var maxKey = 0;
|
||||
|
||||
counts.forEach((k, v) {
|
||||
if (v > maxValue) {
|
||||
maxValue = v;
|
||||
maxKey = k;
|
||||
}
|
||||
});
|
||||
|
||||
mostCommonGrade = {maxKey: maxValue};
|
||||
}
|
||||
|
||||
void getAbsences() {
|
||||
absences = absenceProvider.absences.where((a) => a.delay == 0).toList();
|
||||
|
||||
unexcusedAbsences = absences
|
||||
.where((a) => a.state == Justification.unexcused && a.delay == 0)
|
||||
.length;
|
||||
}
|
||||
|
||||
void getAndSortDelays() {
|
||||
Iterable<int> unexcusedDelays = absences
|
||||
.where((a) => a.state == Justification.unexcused && a.delay > 0)
|
||||
.map((e) => e.delay);
|
||||
totalDelays = unexcusedDelays.isNotEmpty
|
||||
? unexcusedDelays.reduce((a, b) => a + b)
|
||||
: 0;
|
||||
}
|
||||
|
||||
void doEverything() {
|
||||
getGrades();
|
||||
getMostCommonGrade();
|
||||
getAbsences();
|
||||
getAndSortDelays();
|
||||
}
|
||||
|
||||
void getPersonality() {
|
||||
if (settings.goodStudent) {
|
||||
finalPersonality = PersonalityType.cheater;
|
||||
} else if (subjectAvg > 4.7) {
|
||||
finalPersonality = PersonalityType.geek;
|
||||
} else if (mostCommonGrade.keys.toList()[0] == 1 &&
|
||||
mostCommonGrade.values.toList()[0] > 1) {
|
||||
finalPersonality = PersonalityType.fallible;
|
||||
} else if (absences.length < 10) {
|
||||
finalPersonality = PersonalityType.healthy;
|
||||
} else if (unexcusedAbsences >= 10) {
|
||||
finalPersonality = PersonalityType.quitter;
|
||||
} else if (totalDelays > 50) {
|
||||
finalPersonality = PersonalityType.late;
|
||||
} else if (absences.length >= 100) {
|
||||
finalPersonality = PersonalityType.sick;
|
||||
} else if (mostCommonGrade.keys.toList()[0] == 2) {
|
||||
finalPersonality = PersonalityType.acceptable;
|
||||
} else if (mostCommonGrade.keys.toList()[0] == 3) {
|
||||
finalPersonality = PersonalityType.average;
|
||||
} else if (classWorkGrades.length >= 5) {
|
||||
finalPersonality = PersonalityType.diligent;
|
||||
} else {
|
||||
finalPersonality = PersonalityType.npc;
|
||||
}
|
||||
}
|
||||
|
||||
Widget cardInnerBuilder() {
|
||||
Map<PersonalityType, Map<String, String>> personality = {
|
||||
PersonalityType.geek: {
|
||||
'emoji': '🤓',
|
||||
'title': 't_geek',
|
||||
'description': 'd_geek',
|
||||
'subtitle': 's_geek',
|
||||
'subvalue': subjectAvg.toStringAsFixed(2),
|
||||
},
|
||||
PersonalityType.sick: {
|
||||
'emoji': '🤒',
|
||||
'title': 't_sick',
|
||||
'description': 'd_sick',
|
||||
'subtitle': 's_sick',
|
||||
'subvalue': absences.length.toString(),
|
||||
},
|
||||
PersonalityType.late: {
|
||||
'emoji': '⌛',
|
||||
'title': 't_late',
|
||||
'description': 'd_late',
|
||||
'subtitle': 's_late',
|
||||
'subvalue': totalDelays.toString(),
|
||||
},
|
||||
PersonalityType.quitter: {
|
||||
'emoji': '❓',
|
||||
'title': 't_quitter',
|
||||
'description': 'd_quitter',
|
||||
'subtitle': 's_quitter',
|
||||
'subvalue': unexcusedAbsences.toString(),
|
||||
},
|
||||
PersonalityType.healthy: {
|
||||
'emoji': '😷',
|
||||
'title': 't_healthy',
|
||||
'description': 'd_healthy',
|
||||
'subtitle': 's_healthy',
|
||||
'subvalue': absences.length.toString(),
|
||||
},
|
||||
PersonalityType.acceptable: {
|
||||
'emoji': '🤏',
|
||||
'title': 't_acceptable',
|
||||
'description': 'd_acceptable',
|
||||
'subtitle': 's_acceptable',
|
||||
'subvalue': mostCommonGrade.values.toList()[0].toString(),
|
||||
},
|
||||
PersonalityType.fallible: {
|
||||
'emoji': '📉',
|
||||
'title': 't_fallible',
|
||||
'description': 'd_fallible',
|
||||
'subtitle': 's_fallible',
|
||||
'subvalue': mostCommonGrade.values.toList()[0].toString(),
|
||||
},
|
||||
PersonalityType.average: {
|
||||
'emoji': '👌',
|
||||
'title': 't_average',
|
||||
'description': 'd_average',
|
||||
'subtitle': 's_average',
|
||||
'subvalue': mostCommonGrade.values.toList()[0].toString(),
|
||||
},
|
||||
PersonalityType.diligent: {
|
||||
'emoji': '💫',
|
||||
'title': 't_diligent',
|
||||
'description': 'd_diligent',
|
||||
'subtitle': 's_diligent',
|
||||
'subvalue': classWorkGrades.length.toString(),
|
||||
},
|
||||
PersonalityType.cheater: {
|
||||
'emoji': '🧑💻',
|
||||
'title': 't_cheater',
|
||||
'description': 'd_cheater',
|
||||
'subtitle': 's_cheater',
|
||||
'subvalue': '0',
|
||||
},
|
||||
PersonalityType.npc: {
|
||||
'emoji': '⛰️',
|
||||
'title': 't_npc',
|
||||
'description': 'd_npc',
|
||||
'subtitle': 's_npc',
|
||||
'subvalue': '69420',
|
||||
}
|
||||
};
|
||||
|
||||
Map<PersonalityType, Widget> personalityWidgets = {};
|
||||
|
||||
for (var i in personality.keys) {
|
||||
Widget w = Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
personality[i]?['emoji'] ?? '❓',
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 128.0,
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
(personality[i]?['title'] ?? 'unknown').i18n,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 38.0,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
(personality[i]?['description'] ?? 'unknown_personality').i18n,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
height: 1.2,
|
||||
color: Colors.white.withOpacity(0.8),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Text(
|
||||
(personality[i]?['subtitle'] ?? 'unknown').i18n,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 20.0,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
personality[i]?['subvalue'] ?? '0',
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 69.0,
|
||||
height: 1.15,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
personalityWidgets.addAll({i: w});
|
||||
}
|
||||
|
||||
return personalityWidgets[finalPersonality] ?? Container();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
doEverything();
|
||||
getPersonality();
|
||||
|
||||
return GestureDetector(
|
||||
onLongPressDown: (_) => setState(() => hold = true),
|
||||
onLongPressEnd: (_) => setState(() => hold = false),
|
||||
onLongPressCancel: () => setState(() => hold = false),
|
||||
child: AnimatedScale(
|
||||
scale: hold ? 1.018 : 1.0,
|
||||
curve: Curves.easeInOutBack,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 12, bottom: 12, left: 12, right: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0x280008FF),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.08),
|
||||
offset: const Offset(0, 5),
|
||||
blurRadius: 20,
|
||||
spreadRadius: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: DottedBorder(
|
||||
color: Colors.black.withOpacity(0.9),
|
||||
dashPattern: const [12, 12],
|
||||
padding:
|
||||
const EdgeInsets.only(top: 20, bottom: 20, left: 20, right: 20),
|
||||
child: cardInnerBuilder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
// main
|
||||
"unknown": "???",
|
||||
"unknown_personality": "Unknown personality...",
|
||||
// personalities
|
||||
"t_geek": "Know-It-All",
|
||||
"d_geek":
|
||||
"You learn a lot, but don't worry - Being a know-it-all is a blessing in disguise. You'll be successful in life.",
|
||||
"s_geek": "Year-end average",
|
||||
"t_sick": "Sick",
|
||||
"d_sick":
|
||||
"Get well soon, bro. Even if you lied about being sick to skip school.",
|
||||
"s_sick": "Absences",
|
||||
"t_late": "Late",
|
||||
"d_late":
|
||||
"The tram's wheel got punctured. The airplane was derailed. Your dog ate your shoe. We believe you.",
|
||||
"s_late": "Delays (minutes)",
|
||||
"t_quitter": "Skipper",
|
||||
"d_quitter": "Supplementary exam incoming.",
|
||||
"s_quitter": "Igazolatlan hiányzások",
|
||||
"t_healthy": "Healthy",
|
||||
"d_healthy":
|
||||
"As cool as a cucumber! You almost never missed a class.",
|
||||
"s_healthy": "Absences",
|
||||
"t_acceptable": "Acceptable",
|
||||
"d_acceptable":
|
||||
"Final exams are D. But who cares? It's still a grade. Not a good one, but it's definitely a grade.",
|
||||
"s_acceptable": "D's",
|
||||
"t_fallible": "Failed",
|
||||
"d_fallible": "Good luck next year.",
|
||||
"s_fallible": "F's",
|
||||
"t_average": "It's okay",
|
||||
"d_average": "Not good, not bad. The golden mean, if you will...",
|
||||
"s_average": "C's",
|
||||
"t_diligent": "Hard-worker",
|
||||
"d_diligent":
|
||||
"You noted everything, you made that presentation, and you lead the group project.",
|
||||
"s_diligent": "Class work A's",
|
||||
"t_cheater": "Cheater",
|
||||
"d_cheater":
|
||||
"You enabled the \"Good Student\" mode. Wow. You may have outsmarted me, but I have outsmarted your outsmarting.",
|
||||
"s_cheater": "Bitches",
|
||||
"t_npc": "NPC",
|
||||
"d_npc":
|
||||
"You're such a non-player character, we couldn't give you a personality.",
|
||||
"s_npc": "In-game playtime (hours)",
|
||||
},
|
||||
"hu_hu": {
|
||||
// main
|
||||
"unknown": "???",
|
||||
"unknown_personality": "Ismeretlen személyiség...",
|
||||
// personalities
|
||||
"t_geek": "Stréber",
|
||||
"d_geek":
|
||||
"Sokat tanulsz, de ezzel semmi baj! Ez egyben áldás és átok, de legalább az életben sikeres leszel.",
|
||||
"s_geek": "Év végi átlagod",
|
||||
"t_sick": "Beteges",
|
||||
"d_sick":
|
||||
"Jobbulást, tesó. Még akkor is, ha hazudtál arról, hogy beteg vagy, hogy ne kelljen suliba menned.",
|
||||
"s_sick": "Hiányzásaid",
|
||||
"t_late": "Késős",
|
||||
"d_late":
|
||||
"Kilyukadt a villamos kereke. Kisiklott a repülő. A kutyád megette a cipőd. Elhisszük.",
|
||||
"s_late": "Késések (perc)",
|
||||
"t_quitter": "Lógós",
|
||||
"d_quitter": "Osztályzóvizsga incoming.",
|
||||
"s_quitter": "Igazolatlan hiányzások",
|
||||
"t_healthy": "Makk",
|
||||
"d_healthy":
|
||||
"...egészséges vagy! Egész évben alig hiányoztál az iskolából.",
|
||||
"s_healthy": "Hiányzásaid",
|
||||
"t_acceptable": "Elmegy",
|
||||
"d_acceptable":
|
||||
"A kettes érettségi is érettségi. Nem egy jó érettségi, de biztos, hogy egy érettségi.",
|
||||
"s_acceptable": "Kettesek",
|
||||
"t_fallible": "Bukós",
|
||||
"d_fallible": "Jövőre több sikerrel jársz.",
|
||||
"s_fallible": "Karók",
|
||||
"t_average": "Közepes",
|
||||
"d_average": "Se jó, se rossz. Az arany középút, ha akarsz...",
|
||||
"s_average": "Hármasok",
|
||||
"t_diligent": "Szorgalmas",
|
||||
"d_diligent":
|
||||
"Leírtad a jegyzetet, megcsináltad a prezentációt, és te vezetted a projektmunkát.",
|
||||
"s_diligent": "Órai munka ötösök",
|
||||
"t_cheater": "Csaló",
|
||||
"d_cheater":
|
||||
"Bekapcsoltad a “Jó Tanuló” módot. Wow. Azt hitted, túl járhatsz az eszemen, de kijátszottam a kijátszásod.",
|
||||
"s_cheater": "Bitches",
|
||||
"t_npc": "NPC",
|
||||
"d_npc":
|
||||
"Egy akkora nagy non-player character vagy, hogy neked semmilyen személyiség nem jutott ezen kívül.",
|
||||
"s_npc": "In-game playtime (óra)",
|
||||
},
|
||||
"de_de": {
|
||||
// main
|
||||
"unknown": "???",
|
||||
"unknown_personality": "Unbekannte Persönlichkeit...",
|
||||
// personalities
|
||||
"t_geek": "Besserwisser",
|
||||
"d_geek":
|
||||
"Du lernst eine Menge, aber sorge dich nicht - ein Besserwisser zu sein wird sich letzten Endes doch als Segen erweisen. Du wirst erfolgreich sein im Leben.",
|
||||
"s_geek": "Durchschnittsschüler",
|
||||
"t_sick": "Krank",
|
||||
"d_sick":
|
||||
"Werd schnell wieder gesund, Brudi. Selbst wenn du gelogen hast, nur um Schule zu schwänzen zu können.",
|
||||
"s_sick": "Abwesenheiten",
|
||||
"t_late": "Verspätet",
|
||||
"d_late":
|
||||
"Die Straßenbahn hat eine Reifenpanne. Das Flugzeug ist entgleist. Dein Hund hat deinen Schuh gefressen. Klar, wir glauben dir.",
|
||||
"s_late": "Verspätung (Minuten)",
|
||||
"t_quitter": "Schulschwänzer",
|
||||
"d_quitter": "Ein zusätzlicher Test wird anstehen.",
|
||||
"s_quitter": "Unentschuldigte Abwesenheiten",
|
||||
"t_healthy": "Gesund",
|
||||
"d_healthy":
|
||||
"Du bist die Ruhe selbst! Du hast fast nie eine Unterrichtsstunde verpasst.",
|
||||
"s_healthy": "Abwesenheiten",
|
||||
"t_acceptable": "Akzeptabel",
|
||||
"d_acceptable":
|
||||
"Die Abschlussprüfungen waren gerade einmal eine 4. Aber wen juckt's? Es ist immer noch positiv. Nicht allzu gut, aber definitiv positiv.",
|
||||
"s_acceptable": "4er",
|
||||
"t_fallible": "Durchgefallen",
|
||||
"d_fallible": "Viel Glück im nächsten Jahr.",
|
||||
"s_fallible": "5er",
|
||||
"t_average": "Es ist in Ordnung",
|
||||
"d_average":
|
||||
"Nicht gut, nicht schlecht. Der goldene Durchschnitt, wenn du so willst...",
|
||||
"s_average": "3er",
|
||||
"t_diligent": "Ein Fleißiger",
|
||||
"d_diligent":
|
||||
"Du hast bei allem mitgeschrieben, du hast den Vortrag gehalten, und du hast die Gruppenarbeit geleitet.",
|
||||
"s_diligent": "1er Schüler",
|
||||
"t_cheater": "Geschummelt",
|
||||
"d_cheater":
|
||||
"Du hast den „Guter Schüler“ Modus aktiviert. Wow. Du magst mich zwar vielleicht überlistet haben, aber ich habe deine Überlistung überlistet.",
|
||||
"s_cheater": "Bitches",
|
||||
"t_npc": "COM",
|
||||
"d_npc":
|
||||
"Du bist einfach so sehr wie ein Computer, dass wir dir nicht einmal eine Persönlichkeit geben konnten.",
|
||||
"s_npc": "Spielzeit (Stunden)",
|
||||
}
|
||||
};
|
||||
|
||||
String get i18n => localize(this, _t);
|
||||
String fill(List<Object> params) => localizeFill(this, params);
|
||||
String plural(int value) => localizePlural(value, this, _t);
|
||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/subject.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_display.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AbsenceSubjectTile extends StatelessWidget {
|
||||
const AbsenceSubjectTile(this.subject, {Key? key, this.percentage = 0.0, this.excused = 0, this.unexcused = 0, this.pending = 0, this.onTap})
|
||||
@@ -19,6 +21,7 @@ class AbsenceSubjectTile extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: ListTile(
|
||||
@@ -33,7 +36,7 @@ class AbsenceSubjectTile extends StatelessWidget {
|
||||
subject.renamedTo ?? subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15.0, fontStyle: subject.isRenamed ? FontStyle.italic : null),
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15.0, fontStyle: subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: AbsenceDisplay(excused, unexcused, pending),
|
||||
trailing: Row(
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_container.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'absence_tile.i18n.dart';
|
||||
|
||||
class AbsenceTile extends StatelessWidget {
|
||||
@@ -18,6 +20,7 @@ class AbsenceTile extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
Color color = justificationColor(absence.state, context: context);
|
||||
bool group = AbsenceGroupContainer.of(context) != null;
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
@@ -66,7 +69,7 @@ class AbsenceTile extends StatelessWidget {
|
||||
(absence.lessonIndex != null ? "${absence.lessonIndex}. " : "") + (absence.subject.renamedTo ?? absence.subject.name.capital()),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 14.0, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 14.0, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: !group
|
||||
? Text(
|
||||
@@ -74,7 +77,7 @@ class AbsenceTile extends StatelessWidget {
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
// DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date),
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// ignore_for_file: empty_catches
|
||||
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
@@ -12,6 +13,7 @@ import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:filcnaplo/utils/reverse_search.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'absence_view.i18n.dart';
|
||||
|
||||
class AbsenceView extends StatelessWidget {
|
||||
@@ -28,6 +30,7 @@ class AbsenceView extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color color = AbsenceTile.justificationColor(absence.state, context: context);
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
@@ -57,7 +60,7 @@ class AbsenceView extends StatelessWidget {
|
||||
absence.subject.renamedTo ?? absence.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w700, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
|
||||
style: TextStyle(fontWeight: FontWeight.w700, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
absence.teacher,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
@@ -6,6 +7,7 @@ import 'package:filcnaplo_mobile_ui/pages/grades/subject_grades_container.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'certification_tile.i18n.dart';
|
||||
|
||||
class CertificationTile extends StatelessWidget {
|
||||
@@ -20,6 +22,8 @@ class CertificationTile extends StatelessWidget {
|
||||
bool isSubjectView = SubjectGradesContainer.of(context) != null;
|
||||
String certificationName;
|
||||
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
|
||||
switch (grade.type) {
|
||||
case GradeType.endYear:
|
||||
certificationName = "final".i18n;
|
||||
@@ -78,7 +82,7 @@ class CertificationTile extends StatelessWidget {
|
||||
title: Text(isSubjectView ? certificationName : grade.subject.renamedTo ?? grade.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 18.0, fontStyle: grade.subject.isRenamed ? FontStyle.italic : null)),
|
||||
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 18.0, fontStyle: grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null)),
|
||||
subtitle: Text(grade.value.valueName, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16.0)),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/subject.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/average_display.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class GradeSubjectTile extends StatelessWidget {
|
||||
const GradeSubjectTile(this.subject,
|
||||
@@ -19,10 +21,10 @@ class GradeSubjectTile extends StatelessWidget {
|
||||
final double average;
|
||||
final double groupAverage;
|
||||
final double averageBefore;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color textColor = AppColors.of(context).text;
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
|
||||
// Failing indicator
|
||||
if (average < 2.0 && average >= 1.0) {
|
||||
@@ -54,7 +56,7 @@ class GradeSubjectTile extends StatelessWidget {
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14.0,
|
||||
color: textColor,
|
||||
fontStyle: subject.isRenamed ? FontStyle.italic : null),
|
||||
fontStyle: settingsProvider.renamedSubjectsItalics && subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
@@ -17,6 +17,7 @@ class GradeView extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
@@ -29,7 +30,7 @@ class GradeView extends StatelessWidget {
|
||||
grade.subject.renamedTo ?? grade.subject.name.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: grade.subject.isRenamed ? FontStyle.italic : null),
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
!Provider.of<SettingsProvider>(context, listen: false).presentationMode ? grade.teacher : "Tanár",
|
||||
|
||||
@@ -62,7 +62,9 @@ class NewGradesSurprise extends StatelessWidget {
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
"new_grades".i18n,
|
||||
grades.length == 1 ?
|
||||
"new_grade".i18n :
|
||||
"new_grades".i18n,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
|
||||
@@ -9,6 +9,7 @@ extension Localization on String {
|
||||
"rare": "Rare",
|
||||
"epic": "Epic",
|
||||
"legendary": "Legendary",
|
||||
"new_grade": "New grade",
|
||||
"new_grades": "New grades",
|
||||
"tap_to_open": "Tap to open now!",
|
||||
"open_subtitle": "Tap to open...",
|
||||
@@ -19,6 +20,7 @@ extension Localization on String {
|
||||
"rare": "Ritka",
|
||||
"epic": "Epikus",
|
||||
"legendary": "Legendás",
|
||||
"new_grade": "Új jegy",
|
||||
"new_grades": "Új jegyek",
|
||||
"tap_to_open": "Nyisd ki őket!",
|
||||
"open_subtitle": "Nyomd meg a kinyitáshoz...",
|
||||
@@ -29,6 +31,7 @@ extension Localization on String {
|
||||
"rare": "Selten",
|
||||
"epic": "Episch",
|
||||
"legendary": "Legendär",
|
||||
"new_grade": "Neue Note",
|
||||
"new_grades": "Neue Noten",
|
||||
"tap_to_open": "Tippen, um jetzt zu öffnen!",
|
||||
"open_subtitle": "Antippen zum Öffnen...",
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/homework.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class HomeworkTile extends StatelessWidget {
|
||||
const HomeworkTile(this.homework,
|
||||
@@ -17,6 +19,8 @@ class HomeworkTile extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
|
||||
return Material(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
@@ -65,7 +69,7 @@ class HomeworkTile extends StatelessWidget {
|
||||
homework.subject.renamedTo ?? homework.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: homework.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: censored
|
||||
? Wrap(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/homework.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
@@ -7,6 +8,7 @@ import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_attachment_
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'homework_view.i18n.dart';
|
||||
|
||||
class HomeworkView extends StatelessWidget {
|
||||
@@ -21,6 +23,7 @@ class HomeworkView extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> attachmentTiles = [];
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
|
||||
for (var attachment in homework.attachments) {
|
||||
attachmentTiles.add(Padding(
|
||||
@@ -48,7 +51,7 @@ class HomeworkView extends StatelessWidget {
|
||||
homework.subject.renamedTo ?? homework.subject.name.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: homework.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
homework.teacher,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'lesson_view.i18n.dart';
|
||||
|
||||
class LessonView extends StatelessWidget {
|
||||
@@ -16,6 +18,8 @@ class LessonView extends StatelessWidget {
|
||||
Color accent = Theme.of(context).colorScheme.secondary;
|
||||
String lessonIndexTrailing = "";
|
||||
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
|
||||
if (RegExp(r'\d').hasMatch(lesson.lessonIndex)) lessonIndexTrailing = ".";
|
||||
|
||||
if (lesson.substituteTeacher != "") {
|
||||
@@ -50,7 +54,7 @@ class LessonView extends StatelessWidget {
|
||||
lesson.subject.renamedTo ?? lesson.subject.name.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null),
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
lesson.substituteTeacher == "" ? lesson.teacher : lesson.substituteTeacher,
|
||||
|
||||
@@ -22,16 +22,22 @@ class MessageViewTile extends StatelessWidget {
|
||||
UserProvider user = Provider.of<UserProvider>(context, listen: false);
|
||||
String recipientLabel = "";
|
||||
|
||||
if (message.recipients.any((r) => r.name == user.student?.name)) recipientLabel = "me".i18n;
|
||||
if (message.recipients.any((r) => r.name == user.student?.name)) {
|
||||
recipientLabel = "me".i18n;
|
||||
}
|
||||
|
||||
if (recipientLabel != "" && message.recipients.length > 1) {
|
||||
recipientLabel += " +";
|
||||
recipientLabel += message.recipients.where((r) => r.name != user.student?.name).length.toString();
|
||||
recipientLabel += message.recipients
|
||||
.where((r) => r.name != user.student?.name)
|
||||
.length
|
||||
.toString();
|
||||
}
|
||||
|
||||
if (recipientLabel == "") {
|
||||
// note: convertint to set to remove duplicates
|
||||
recipientLabel += message.recipients.map((r) => r.name).toSet().join(", ");
|
||||
recipientLabel +=
|
||||
message.recipients.map((r) => r.name).toSet().join(", ");
|
||||
}
|
||||
|
||||
List<Widget> attachments = [];
|
||||
@@ -75,9 +81,9 @@ class MessageViewTile extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
trailing: Row(
|
||||
trailing: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
children: [
|
||||
// IconButton(
|
||||
// onPressed: () {},
|
||||
// icon: Icon(FeatherIcons.cornerUpLeft, color: AppColors.of(context).text),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart';
|
||||
@@ -6,6 +7,7 @@ import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'missed_exam_tile.i18n.dart';
|
||||
|
||||
class MissedExamView extends StatelessWidget {
|
||||
@@ -30,6 +32,7 @@ class MissedExamViewTile extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
@@ -43,7 +46,7 @@ class MissedExamViewTile extends StatelessWidget {
|
||||
),
|
||||
title: Text(
|
||||
"${lesson.subject.renamedTo ?? lesson.subject.name.capital()} • ${lesson.date.format(context)}",
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null),
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
"missed_exam_contact".i18n.fill([lesson.teacher]),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/ui/date_widget.dart';
|
||||
import 'package:filcnaplo/utils/reverse_search.dart';
|
||||
@@ -15,6 +16,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_view.i18n.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AbsenceSubjectView extends StatelessWidget {
|
||||
const AbsenceSubjectView(this.subject, {Key? key, this.absences = const []}) : super(key: key);
|
||||
@@ -54,10 +56,12 @@ class AbsenceSubjectView extends StatelessWidget {
|
||||
.toList();
|
||||
List<Widget> absenceTiles = sortDateWidgets(context, dateWidgets: dateWidgets, padding: EdgeInsets.zero, hasShadow: true);
|
||||
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
|
||||
return Scaffold(
|
||||
body: HeroScrollView(
|
||||
title: subject.renamedTo ?? subject.name.capital(),
|
||||
italic: subject.isRenamed,
|
||||
italic: subject.isRenamed && settingsProvider.renamedSubjectsItalics,
|
||||
icon: SubjectIcon.resolveVariant(subject: subject, context: context),
|
||||
child: AbsenceSubjectViewContainer(
|
||||
child: CupertinoScrollbar(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
|
||||
import 'package:filcnaplo/helpers/average_helper.dart';
|
||||
@@ -56,6 +57,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
|
||||
// Providers
|
||||
late GradeProvider gradeProvider;
|
||||
late GradeCalculatorProvider calculatorProvider;
|
||||
late SettingsProvider settingsProvider;
|
||||
|
||||
late double average;
|
||||
late Widget gradeGraph;
|
||||
@@ -149,6 +151,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
|
||||
Widget build(BuildContext context) {
|
||||
gradeProvider = Provider.of<GradeProvider>(context);
|
||||
calculatorProvider = Provider.of<GradeCalculatorProvider>(context);
|
||||
settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
|
||||
List<Grade> subjectGrades = getSubjectGrades(widget.subject).toList();
|
||||
average = AverageHelper.averageEvals(subjectGrades);
|
||||
@@ -260,7 +263,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
|
||||
subject: widget.subject, context: context),
|
||||
scrollController: _scrollController,
|
||||
title: widget.subject.renamedTo ?? widget.subject.name.capital(),
|
||||
italic: widget.subject.isRenamed,
|
||||
italic: settingsProvider.renamedSubjectsItalics && widget.subject.isRenamed,
|
||||
child: SubjectGradesContainer(
|
||||
child: CupertinoScrollbar(
|
||||
child: ListView.builder(
|
||||
@@ -277,7 +280,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
|
||||
|
||||
void gradeCalc(BuildContext context) {
|
||||
// Scroll to the top of the page
|
||||
_scrollController.animateTo(75,
|
||||
_scrollController.animateTo(100,
|
||||
duration: const Duration(milliseconds: 500), curve: Curves.ease);
|
||||
|
||||
calculatorProvider.clear();
|
||||
|
||||
@@ -2,7 +2,9 @@ import 'package:animations/animations.dart';
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/icons/filc_icons.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/home/live_card/heads_up_countdown.dart';
|
||||
import 'package:filcnaplo_mobile_ui/screens/summary/summary_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo/api/providers/live_card_provider.dart';
|
||||
@@ -43,6 +45,7 @@ class _LiveCardState extends State<LiveCard> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
liveCard = Provider.of<LiveCardProvider>(context);
|
||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||
|
||||
if (!liveCard.show) return Container();
|
||||
|
||||
@@ -50,10 +53,54 @@ class _LiveCardState extends State<LiveCard> {
|
||||
Duration bellDelay = liveCard.delay;
|
||||
|
||||
switch (liveCard.currentState) {
|
||||
case LiveCardState.summary:
|
||||
child = LiveCardWidget(
|
||||
key: const Key('livecard.summary'),
|
||||
title: 'Vége a tanévnek! 🥳',
|
||||
icon: FeatherIcons.arrowRight,
|
||||
description: Text(
|
||||
'Irány az összefoglaláshoz',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 18.0,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
// showSlidingBottomSheet(
|
||||
// context,
|
||||
// useRootNavigator: true,
|
||||
// builder: (context) => SlidingSheetDialog(
|
||||
// color: Colors.black.withOpacity(0.99),
|
||||
// duration: const Duration(milliseconds: 400),
|
||||
// scrollSpec: const ScrollSpec.bouncingScroll(),
|
||||
// snapSpec: const SnapSpec(
|
||||
// snap: true,
|
||||
// snappings: [1.0],
|
||||
// initialSnap: 1.0,
|
||||
// positioning: SnapPositioning.relativeToAvailableSpace,
|
||||
// ),
|
||||
// minHeight: MediaQuery.of(context).size.height,
|
||||
// cornerRadius: 16,
|
||||
// cornerRadiusOnFullscreen: 0,
|
||||
// builder: (context, state) => const Material(
|
||||
// color: Colors.black,
|
||||
// child: SummaryScreen(
|
||||
// currentPage: 'start',
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
SummaryScreen.show(context: context, currentPage: 'start');
|
||||
},
|
||||
);
|
||||
break;
|
||||
case LiveCardState.morning:
|
||||
child = LiveCardWidget(
|
||||
key: const Key('livecard.morning'),
|
||||
title: DateFormat("EEEE", I18n.of(context).locale.toString()).format(DateTime.now()).capital(),
|
||||
title: DateFormat("EEEE", I18n.of(context).locale.toString())
|
||||
.format(DateTime.now())
|
||||
.capital(),
|
||||
icon: FeatherIcons.sun,
|
||||
description: liveCard.nextLesson != null
|
||||
? Text.rich(
|
||||
@@ -61,26 +108,40 @@ class _LiveCardState extends State<LiveCard> {
|
||||
children: [
|
||||
TextSpan(text: "first_lesson_1".i18n),
|
||||
TextSpan(
|
||||
text: liveCard.nextLesson!.subject.renamedTo ?? liveCard.nextLesson!.subject.name.capital(),
|
||||
text: liveCard.nextLesson!.subject.renamedTo ??
|
||||
liveCard.nextLesson!.subject.name.capital(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.secondary.withOpacity(.85),
|
||||
fontStyle: liveCard.nextLesson!.subject.isRenamed ? FontStyle.italic : null),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary
|
||||
.withOpacity(.85),
|
||||
fontStyle: liveCard.nextLesson!.subject.isRenamed &&
|
||||
settingsProvider.renamedSubjectsItalics
|
||||
? FontStyle.italic
|
||||
: null),
|
||||
),
|
||||
TextSpan(text: "first_lesson_2".i18n),
|
||||
TextSpan(
|
||||
text: liveCard.nextLesson!.room.capital(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.secondary.withOpacity(.85),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary
|
||||
.withOpacity(.85),
|
||||
),
|
||||
),
|
||||
TextSpan(text: "first_lesson_3".i18n),
|
||||
TextSpan(
|
||||
text: DateFormat('H:mm').format(liveCard.nextLesson!.start),
|
||||
text: DateFormat('H:mm')
|
||||
.format(liveCard.nextLesson!.start),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.secondary.withOpacity(.85),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary
|
||||
.withOpacity(.85),
|
||||
),
|
||||
),
|
||||
TextSpan(text: "first_lesson_4".i18n),
|
||||
@@ -91,30 +152,48 @@ class _LiveCardState extends State<LiveCard> {
|
||||
);
|
||||
break;
|
||||
case LiveCardState.duringLesson:
|
||||
final elapsedTime = DateTime.now().difference(liveCard.currentLesson!.start).inSeconds.toDouble() + bellDelay.inSeconds;
|
||||
final maxTime = liveCard.currentLesson!.end.difference(liveCard.currentLesson!.start).inSeconds.toDouble();
|
||||
final elapsedTime = DateTime.now()
|
||||
.difference(liveCard.currentLesson!.start)
|
||||
.inSeconds
|
||||
.toDouble() +
|
||||
bellDelay.inSeconds;
|
||||
final maxTime = liveCard.currentLesson!.end
|
||||
.difference(liveCard.currentLesson!.start)
|
||||
.inSeconds
|
||||
.toDouble();
|
||||
|
||||
final showMinutes = maxTime - elapsedTime > 60;
|
||||
|
||||
child = LiveCardWidget(
|
||||
key: const Key('livecard.duringLesson'),
|
||||
leading: liveCard.currentLesson!.lessonIndex + (RegExp(r'\d').hasMatch(liveCard.currentLesson!.lessonIndex) ? "." : ""),
|
||||
title: liveCard.currentLesson!.subject.renamedTo ?? liveCard.currentLesson!.subject.name.capital(),
|
||||
leading: liveCard.currentLesson!.lessonIndex +
|
||||
(RegExp(r'\d').hasMatch(liveCard.currentLesson!.lessonIndex)
|
||||
? "."
|
||||
: ""),
|
||||
title: liveCard.currentLesson!.subject.renamedTo ??
|
||||
liveCard.currentLesson!.subject.name.capital(),
|
||||
titleItalic: liveCard.currentLesson!.subject.isRenamed,
|
||||
subtitle: liveCard.currentLesson!.room,
|
||||
icon: SubjectIcon.resolveVariant(subject: liveCard.currentLesson!.subject, context: context),
|
||||
description: liveCard.currentLesson!.description != "" ? Text(liveCard.currentLesson!.description) : null,
|
||||
nextSubject: liveCard.nextLesson?.subject.renamedTo ?? liveCard.nextLesson?.subject.name.capital(),
|
||||
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed ?? false,
|
||||
icon: SubjectIcon.resolveVariant(
|
||||
subject: liveCard.currentLesson!.subject, context: context),
|
||||
description: liveCard.currentLesson!.description != ""
|
||||
? Text(liveCard.currentLesson!.description)
|
||||
: null,
|
||||
nextSubject: liveCard.nextLesson?.subject.renamedTo ??
|
||||
liveCard.nextLesson?.subject.name.capital(),
|
||||
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed == true &&
|
||||
settingsProvider.renamedSubjectsItalics,
|
||||
nextRoom: liveCard.nextLesson?.room,
|
||||
progressMax: showMinutes ? maxTime / 60 : maxTime,
|
||||
progressCurrent: showMinutes ? elapsedTime / 60 : elapsedTime,
|
||||
progressAccuracy: showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
|
||||
progressAccuracy:
|
||||
showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
|
||||
onProgressTap: () {
|
||||
showDialog(
|
||||
barrierColor: Colors.black,
|
||||
context: context,
|
||||
builder: (context) => HeadsUpCountdown(maxTime: maxTime, elapsedTime: elapsedTime),
|
||||
builder: (context) =>
|
||||
HeadsUpCountdown(maxTime: maxTime, elapsedTime: elapsedTime),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -129,8 +208,15 @@ class _LiveCardState extends State<LiveCard> {
|
||||
|
||||
final diff = liveCard.getFloorDifference();
|
||||
|
||||
final maxTime = liveCard.nextLesson!.start.difference(liveCard.prevLesson!.end).inSeconds.toDouble();
|
||||
final elapsedTime = DateTime.now().difference(liveCard.prevLesson!.end).inSeconds.toDouble() + bellDelay.inSeconds.toDouble();
|
||||
final maxTime = liveCard.nextLesson!.start
|
||||
.difference(liveCard.prevLesson!.end)
|
||||
.inSeconds
|
||||
.toDouble();
|
||||
final elapsedTime = DateTime.now()
|
||||
.difference(liveCard.prevLesson!.end)
|
||||
.inSeconds
|
||||
.toDouble() +
|
||||
bellDelay.inSeconds.toDouble();
|
||||
|
||||
final showMinutes = maxTime - elapsedTime > 60;
|
||||
|
||||
@@ -139,14 +225,21 @@ class _LiveCardState extends State<LiveCard> {
|
||||
title: "break".i18n,
|
||||
icon: iconFloorMap[diff],
|
||||
description: liveCard.nextLesson!.room != liveCard.prevLesson!.room
|
||||
? Text("go $diff".i18n.fill([diff != "to room" ? (liveCard.nextLesson!.getFloor() ?? 0) : liveCard.nextLesson!.room]))
|
||||
? Text("go $diff".i18n.fill([
|
||||
diff != "to room"
|
||||
? (liveCard.nextLesson!.getFloor() ?? 0)
|
||||
: liveCard.nextLesson!.room
|
||||
]))
|
||||
: Text("stay".i18n),
|
||||
nextSubject: liveCard.nextLesson?.subject.renamedTo ?? liveCard.nextLesson?.subject.name.capital(),
|
||||
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed ?? false,
|
||||
nextSubject: liveCard.nextLesson?.subject.renamedTo ??
|
||||
liveCard.nextLesson?.subject.name.capital(),
|
||||
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed == true &&
|
||||
settingsProvider.renamedSubjectsItalics,
|
||||
nextRoom: diff != "to room" ? liveCard.nextLesson?.room : null,
|
||||
progressMax: showMinutes ? maxTime / 60 : maxTime,
|
||||
progressCurrent: showMinutes ? elapsedTime / 60 : elapsedTime,
|
||||
progressAccuracy: showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
|
||||
progressAccuracy:
|
||||
showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
|
||||
onProgressTap: () {
|
||||
showDialog(
|
||||
barrierColor: Colors.black,
|
||||
@@ -162,14 +255,18 @@ class _LiveCardState extends State<LiveCard> {
|
||||
case LiveCardState.afternoon:
|
||||
child = LiveCardWidget(
|
||||
key: const Key('livecard.afternoon'),
|
||||
title: DateFormat("EEEE", I18n.of(context).locale.toString()).format(DateTime.now()).capital(),
|
||||
title: DateFormat("EEEE", I18n.of(context).locale.toString())
|
||||
.format(DateTime.now())
|
||||
.capital(),
|
||||
icon: FeatherIcons.coffee,
|
||||
);
|
||||
break;
|
||||
case LiveCardState.night:
|
||||
child = LiveCardWidget(
|
||||
key: const Key('livecard.night'),
|
||||
title: DateFormat("EEEE", I18n.of(context).locale.toString()).format(DateTime.now()).capital(),
|
||||
title: DateFormat("EEEE", I18n.of(context).locale.toString())
|
||||
.format(DateTime.now())
|
||||
.capital(),
|
||||
icon: FeatherIcons.moon,
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -9,6 +9,7 @@ enum ProgressAccuracy { minutes, seconds }
|
||||
class LiveCardWidget extends StatefulWidget {
|
||||
const LiveCardWidget({
|
||||
Key? key,
|
||||
this.isEvent = false,
|
||||
this.leading,
|
||||
this.title,
|
||||
this.titleItalic = false,
|
||||
@@ -22,8 +23,10 @@ class LiveCardWidget extends StatefulWidget {
|
||||
this.progressMax,
|
||||
this.progressAccuracy = ProgressAccuracy.minutes,
|
||||
this.onProgressTap,
|
||||
this.onTap,
|
||||
}) : super(key: key);
|
||||
|
||||
final bool isEvent;
|
||||
final String? leading;
|
||||
final String? title;
|
||||
final bool titleItalic;
|
||||
@@ -37,6 +40,7 @@ class LiveCardWidget extends StatefulWidget {
|
||||
final double? progressMax;
|
||||
final ProgressAccuracy? progressAccuracy;
|
||||
final Function()? onProgressTap;
|
||||
final Function()? onTap;
|
||||
|
||||
@override
|
||||
State<LiveCardWidget> createState() => _LiveCardWidgetState();
|
||||
@@ -51,6 +55,7 @@ class _LiveCardWidgetState extends State<LiveCardWidget> {
|
||||
onLongPressDown: (_) => setState(() => hold = true),
|
||||
onLongPressEnd: (_) => setState(() => hold = false),
|
||||
onLongPressCancel: () => setState(() => hold = false),
|
||||
onTap: widget.onTap,
|
||||
child: AnimatedScale(
|
||||
scale: hold ? 1.03 : 1.0,
|
||||
curve: Curves.easeInOutBack,
|
||||
@@ -73,171 +78,305 @@ class _LiveCardWidgetState extends State<LiveCardWidget> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||
child: OverflowBox(
|
||||
maxHeight: 96.0,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
child: widget.isEvent
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (widget.leading != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12.0, top: 8.0),
|
||||
child: Text(
|
||||
widget.leading!,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 32.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
if (widget.title != null)
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(text: widget.title!, style: TextStyle(fontStyle: widget.titleItalic ? FontStyle.italic : null)),
|
||||
if (widget.subtitle != null)
|
||||
WidgetSpan(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(left: 6.0, bottom: 3.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 2.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.secondary.withOpacity(.3),
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
child: Text(
|
||||
widget.subtitle!,
|
||||
style: TextStyle(
|
||||
height: 1.2,
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 22.0),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (widget.title != null) const SizedBox(width: 6.0),
|
||||
if (widget.icon != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: Icon(
|
||||
widget.icon,
|
||||
size: 26.0,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (widget.description != null)
|
||||
DefaultTextStyle(
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16.0,
|
||||
height: 1.0,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
maxLines: !(widget.nextSubject == null && widget.progressCurrent == null && widget.progressMax == null) ? 1 : 2,
|
||||
softWrap: false,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: widget.description!,
|
||||
),
|
||||
],
|
||||
Text(
|
||||
widget.title ?? 'Esemény',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 24.0,
|
||||
color:
|
||||
Theme.of(context).textTheme.bodyMedium?.color,
|
||||
fontStyle:
|
||||
widget.titleItalic ? FontStyle.italic : null,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
widget.description ??
|
||||
Text(
|
||||
'Nincs leírás megadva.',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 18.0,
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.color,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 15,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary
|
||||
.withOpacity(0.5),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(10),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Icon(widget.icon),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!(widget.nextSubject == null && widget.progressCurrent == null && widget.progressMax == null))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.nextSubject != null) const Icon(FeatherIcons.arrowRight, size: 12.0),
|
||||
if (widget.nextSubject != null) const SizedBox(width: 4.0),
|
||||
if (widget.nextSubject != null)
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
)
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.leading != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 12.0, top: 8.0),
|
||||
child: Text(
|
||||
widget.leading!,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 32.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: widget.nextSubject!, style: TextStyle(fontStyle: widget.nextSubjectItalic ? FontStyle.italic : null)),
|
||||
if (widget.nextRoom != null)
|
||||
WidgetSpan(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(left: 4.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3.0, vertical: 1.5),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.secondary.withOpacity(.25),
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
child: Text(
|
||||
widget.nextRoom!,
|
||||
style: TextStyle(
|
||||
height: 1.1,
|
||||
fontSize: 11.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.secondary.withOpacity(.9),
|
||||
Row(
|
||||
children: [
|
||||
if (widget.title != null)
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: widget.title!,
|
||||
style: TextStyle(
|
||||
fontStyle: widget
|
||||
.titleItalic
|
||||
? FontStyle.italic
|
||||
: null)),
|
||||
if (widget.subtitle != null)
|
||||
WidgetSpan(
|
||||
child: Container(
|
||||
margin: const EdgeInsets
|
||||
.only(
|
||||
left: 6.0,
|
||||
bottom: 3.0),
|
||||
padding:
|
||||
const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 4.0,
|
||||
vertical: 2.0),
|
||||
decoration:
|
||||
BoxDecoration(
|
||||
color: Theme.of(
|
||||
context)
|
||||
.colorScheme
|
||||
.secondary
|
||||
.withOpacity(.3),
|
||||
borderRadius:
|
||||
BorderRadius
|
||||
.circular(
|
||||
4.0),
|
||||
),
|
||||
child: Text(
|
||||
widget.subtitle!,
|
||||
style: TextStyle(
|
||||
height: 1.2,
|
||||
fontSize: 14.0,
|
||||
fontWeight:
|
||||
FontWeight.w600,
|
||||
color: Theme.of(
|
||||
context)
|
||||
.colorScheme
|
||||
.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 22.0),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.title != null)
|
||||
const SizedBox(width: 6.0),
|
||||
if (widget.icon != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4.0),
|
||||
child: Icon(
|
||||
widget.icon,
|
||||
size: 26.0,
|
||||
color: AppColors.of(context)
|
||||
.text
|
||||
.withOpacity(.75),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (widget.description != null)
|
||||
DefaultTextStyle(
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16.0,
|
||||
height: 1.0,
|
||||
color: AppColors.of(context)
|
||||
.text
|
||||
.withOpacity(.75),
|
||||
),
|
||||
maxLines:
|
||||
!(widget.nextSubject == null &&
|
||||
widget.progressCurrent ==
|
||||
null &&
|
||||
widget.progressMax == null)
|
||||
? 1
|
||||
: 2,
|
||||
softWrap: false,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: widget.description!,
|
||||
),
|
||||
],
|
||||
),
|
||||
style: TextStyle(
|
||||
color: AppColors.of(context).text.withOpacity(.8),
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
if (widget.nextRoom == null && widget.nextSubject == null) const Spacer(),
|
||||
if (widget.progressCurrent != null && widget.progressMax != null)
|
||||
GestureDetector(
|
||||
onTap: widget.onProgressTap,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Text(
|
||||
"remaining ${widget.progressAccuracy == ProgressAccuracy.minutes ? 'min' : 'sec'}"
|
||||
.plural((widget.progressMax! - widget.progressCurrent!).round()),
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!(widget.nextSubject == null &&
|
||||
widget.progressCurrent == null &&
|
||||
widget.progressMax == null))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.nextSubject != null)
|
||||
const Icon(FeatherIcons.arrowRight,
|
||||
size: 12.0),
|
||||
if (widget.nextSubject != null)
|
||||
const SizedBox(width: 4.0),
|
||||
if (widget.nextSubject != null)
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: widget.nextSubject!,
|
||||
style: TextStyle(
|
||||
fontStyle:
|
||||
widget.nextSubjectItalic
|
||||
? FontStyle.italic
|
||||
: null)),
|
||||
if (widget.nextRoom != null)
|
||||
WidgetSpan(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(
|
||||
left: 4.0),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 3.0,
|
||||
vertical: 1.5),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary
|
||||
.withOpacity(.25),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
4.0),
|
||||
),
|
||||
child: Text(
|
||||
widget.nextRoom!,
|
||||
style: TextStyle(
|
||||
height: 1.1,
|
||||
fontSize: 11.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary
|
||||
.withOpacity(.9),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
style: TextStyle(
|
||||
color: AppColors.of(context)
|
||||
.text
|
||||
.withOpacity(.8),
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (widget.nextRoom == null &&
|
||||
widget.nextSubject == null)
|
||||
const Spacer(),
|
||||
if (widget.progressCurrent != null &&
|
||||
widget.progressMax != null)
|
||||
GestureDetector(
|
||||
onTap: widget.onProgressTap,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Text(
|
||||
"remaining ${widget.progressAccuracy == ProgressAccuracy.minutes ? 'min' : 'sec'}"
|
||||
.plural((widget.progressMax! -
|
||||
widget.progressCurrent!)
|
||||
.round()),
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.of(context)
|
||||
.text
|
||||
.withOpacity(.75),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
if (widget.progressCurrent != null &&
|
||||
widget.progressMax != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: ProgressBar(
|
||||
value: widget.progressCurrent! /
|
||||
widget.progressMax!),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (widget.progressCurrent != null && widget.progressMax != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: ProgressBar(value: widget.progressCurrent! / widget.progressMax!),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -199,7 +199,7 @@ class _TimetablePageState extends State<TimetablePage>
|
||||
snap: false,
|
||||
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
actions: [
|
||||
PremiumFSTimetableButton(controller: _controller),
|
||||
PremiumFSTimetableButton(controller: _controller, tabcontroller: _tabController),
|
||||
|
||||
// Profile Icon
|
||||
Padding(
|
||||
|
||||
@@ -8,18 +8,21 @@ extension Localization on String {
|
||||
"empty": "No school this week!",
|
||||
"week": "Week",
|
||||
"error": "Failed to fetch timetable!",
|
||||
"empty_timetable": "Timetable is empty!",
|
||||
},
|
||||
"hu_hu": {
|
||||
"timetable": "Órarend",
|
||||
"empty": "Ezen a héten nincs iskola.",
|
||||
"week": "Hét",
|
||||
"error": "Nem sikerült lekérni az órarendet!",
|
||||
"empty_timetable": "Az órarend üres!",
|
||||
},
|
||||
"de_de": {
|
||||
"timetable": "Zeitplan",
|
||||
"empty": "Keine Schule diese Woche.",
|
||||
"week": "Woche",
|
||||
"error": "Der Fahrplan konnte nicht abgerufen werden!",
|
||||
"empty_timetable": "Der Zeitplan ist blank!",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -12,14 +12,16 @@ class PremiumButton extends StatefulWidget {
|
||||
State<PremiumButton> createState() => _PremiumButtonState();
|
||||
}
|
||||
|
||||
class _PremiumButtonState extends State<PremiumButton> with TickerProviderStateMixin {
|
||||
class _PremiumButtonState extends State<PremiumButton>
|
||||
with TickerProviderStateMixin {
|
||||
late final AnimationController _animation;
|
||||
bool _heldDown = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_animation = AnimationController(vsync: this, duration: const Duration(seconds: 3));
|
||||
_animation =
|
||||
AnimationController(vsync: this, duration: const Duration(seconds: 3));
|
||||
_animation.repeat();
|
||||
}
|
||||
|
||||
@@ -38,7 +40,8 @@ class _PremiumButtonState extends State<PremiumButton> with TickerProviderStateM
|
||||
transitionType: ContainerTransitionType.fadeThrough,
|
||||
openElevation: 0,
|
||||
closedElevation: 0,
|
||||
closedShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
closedShape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
openBuilder: (context, _) => const PremiumScreen(),
|
||||
closedBuilder: (context, action) => GestureDetector(
|
||||
onTapDown: (_) => setState(() => _heldDown = true),
|
||||
@@ -57,16 +60,20 @@ class _PremiumButtonState extends State<PremiumButton> with TickerProviderStateM
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
child: ImageFiltered(
|
||||
imageFilter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
|
||||
imageFilter:
|
||||
ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
|
||||
child: Container(
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
gradient: SweepGradient(colors: const [
|
||||
Colors.blue,
|
||||
Colors.orange,
|
||||
Colors.purple,
|
||||
Colors.blue,
|
||||
], transform: GradientRotation(_animation.value * 6.283185)),
|
||||
gradient: SweepGradient(
|
||||
colors: const [
|
||||
Colors.blue,
|
||||
Colors.orange,
|
||||
Colors.purple,
|
||||
Colors.blue,
|
||||
],
|
||||
transform: GradientRotation(
|
||||
_animation.value * 6.283185)),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -92,9 +99,9 @@ class _PremiumButtonState extends State<PremiumButton> with TickerProviderStateM
|
||||
Color(0xff1EA18F),
|
||||
]),
|
||||
),
|
||||
child: Row(
|
||||
child: const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const [
|
||||
children: [
|
||||
Icon(FilcIcons.premium, color: Colors.white),
|
||||
SizedBox(width: 12.0),
|
||||
Text(
|
||||
|
||||
@@ -21,8 +21,9 @@ class PremiumScreen extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final middleColor =
|
||||
Theme.of(context).brightness == Brightness.dark ? const Color.fromARGB(255, 20, 57, 46) : const Color.fromARGB(255, 10, 140, 123);
|
||||
final middleColor = Theme.of(context).brightness == Brightness.dark
|
||||
? const Color.fromARGB(255, 20, 57, 46)
|
||||
: const Color.fromARGB(255, 10, 140, 123);
|
||||
|
||||
final future = FilcAPI.getSupporters();
|
||||
|
||||
@@ -78,7 +79,8 @@ class PremiumScreen extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -87,16 +89,25 @@ class PremiumScreen extends StatelessWidget {
|
||||
const SizedBox(height: 12.0),
|
||||
const Text(
|
||||
"Még több filc.",
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 25.0, color: Colors.white),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 25.0,
|
||||
color: Colors.white),
|
||||
),
|
||||
const Text(
|
||||
"reFilc Premium.",
|
||||
style: TextStyle(fontWeight: FontWeight.w800, fontSize: 35.0, color: Colors.white),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 35.0,
|
||||
color: Colors.white),
|
||||
),
|
||||
const SizedBox(height: 15.0),
|
||||
Text(
|
||||
"Támogasd a filcet, és szerezz cserébe pár kényelmes jutalmat!",
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20, color: Colors.white.withOpacity(.8)),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 20,
|
||||
color: Colors.white.withOpacity(.8)),
|
||||
),
|
||||
const SizedBox(height: 25.0),
|
||||
SupportersButton(supporters: future),
|
||||
@@ -110,43 +121,61 @@ class PremiumScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0).add(const EdgeInsets.only(bottom: 100)),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0)
|
||||
.add(const EdgeInsets.only(bottom: 100)),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: [
|
||||
PremiumPlanCard(
|
||||
icon: const Icon(FilcIcons.kupak),
|
||||
title: Text("Kupak", style: TextStyle(foreground: GradientStyles.kupakPaint)),
|
||||
title: Text("Kupak",
|
||||
style: TextStyle(
|
||||
foreground: GradientStyles.kupakPaint)),
|
||||
gradient: GradientStyles.kupak,
|
||||
price: 2,
|
||||
description: const Text("Szabd személyre a filcet és láss részletesebb statisztikákat."),
|
||||
url: "https://github.com/sponsors/filc/sponsorships?tier_id=238453&preview=true",
|
||||
active: ActiveSponsorCard.estimateLevel(context.watch<PremiumProvider>().scopes) == PremiumFeatureLevel.kupak,
|
||||
description: const Text(
|
||||
"Szabd személyre a filcet és láss részletesebb statisztikákat."),
|
||||
url:
|
||||
"https://github.com/sponsors/filc/sponsorships?tier_id=238453&preview=true",
|
||||
active: ActiveSponsorCard.estimateLevel(
|
||||
context.watch<PremiumProvider>().scopes) ==
|
||||
PremiumFeatureLevel.kupak,
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
PremiumPlanCard(
|
||||
icon: const Icon(FilcIcons.tinta),
|
||||
title: Text("Tinta", style: TextStyle(foreground: GradientStyles.tintaPaint)),
|
||||
title: Text("Tinta",
|
||||
style: TextStyle(
|
||||
foreground: GradientStyles.tintaPaint)),
|
||||
gradient: GradientStyles.tinta,
|
||||
price: 5,
|
||||
description: const Text("Kényelmesebb órarend, asztali alkalmazás és célok kitűzése."),
|
||||
url: "https://github.com/sponsors/filc/sponsorships?tier_id=238454&preview=true",
|
||||
active: ActiveSponsorCard.estimateLevel(context.watch<PremiumProvider>().scopes) == PremiumFeatureLevel.tinta,
|
||||
description: const Text(
|
||||
"Kényelmesebb órarend, asztali alkalmazás és célok kitűzése."),
|
||||
url:
|
||||
"https://github.com/sponsors/filc/sponsorships?tier_id=238454&preview=true",
|
||||
active: ActiveSponsorCard.estimateLevel(
|
||||
context.watch<PremiumProvider>().scopes) ==
|
||||
PremiumFeatureLevel.tinta,
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
PremiumGoalCard(progress: snapshot.data?.progress ?? 0, target: snapshot.data?.max ?? 1),
|
||||
PremiumGoalCard(
|
||||
progress: snapshot.data?.progress ?? 0,
|
||||
target: snapshot.data?.max ?? 1),
|
||||
const SizedBox(height: 12.0),
|
||||
const GithubConnectButton(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14.0).add(const EdgeInsets.only(top: 12.0)),
|
||||
child: Row(
|
||||
children: const [
|
||||
padding: const EdgeInsets.symmetric(vertical: 14.0)
|
||||
.add(const EdgeInsets.only(top: 12.0)),
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(FilcIcons.kupak),
|
||||
SizedBox(width: 12.0),
|
||||
Expanded(
|
||||
child: Text(
|
||||
"Kupak jutalmak",
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 20),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -154,40 +183,50 @@ class PremiumScreen extends StatelessWidget {
|
||||
),
|
||||
PremiumRewardCard(
|
||||
imageKey: "premium_nickname_showcase",
|
||||
icon: SvgPicture.asset("assets/images/nickname_icon.svg", color: Theme.of(context).iconTheme.color),
|
||||
icon: SvgPicture.asset(
|
||||
"assets/images/nickname_icon.svg",
|
||||
color: Theme.of(context).iconTheme.color),
|
||||
title: const Text("Profil személyre szabás"),
|
||||
description: const Text("Állíts be egy saját becenevet és egy profilképet (akár animáltat is!)"),
|
||||
description: const Text(
|
||||
"Állíts be egy saját becenevet és egy profilképet (akár animáltat is!)"),
|
||||
),
|
||||
const SizedBox(height: 14.0),
|
||||
PremiumRewardCard(
|
||||
imageKey: "premium_theme_showcase",
|
||||
icon: SvgPicture.asset("assets/images/theme_icon.svg", color: Theme.of(context).iconTheme.color),
|
||||
icon: SvgPicture.asset("assets/images/theme_icon.svg",
|
||||
color: Theme.of(context).iconTheme.color),
|
||||
title: const Text("Téma+"),
|
||||
description: const Text("Válassz saját háttérszínt és kártyaszínt is, akár saját HEX-kóddal!"),
|
||||
description: const Text(
|
||||
"Válassz saját háttérszínt és kártyaszínt is, akár saját HEX-kóddal!"),
|
||||
),
|
||||
const SizedBox(height: 14.0),
|
||||
PremiumRewardCard(
|
||||
imageKey: "premium_stats_showcase",
|
||||
icon: SvgPicture.asset("assets/images/stats_icon.svg", color: Theme.of(context).iconTheme.color),
|
||||
icon: SvgPicture.asset("assets/images/stats_icon.svg",
|
||||
color: Theme.of(context).iconTheme.color),
|
||||
title: const Text("Részletes jegy statisztika"),
|
||||
description: const Text("Válassz heti, havi és háromhavi időtartam közül, és pontosan lásd, mennyi jegyed van."),
|
||||
description: const Text(
|
||||
"Válassz heti, havi és háromhavi időtartam közül, és pontosan lásd, mennyi jegyed van."),
|
||||
),
|
||||
const SizedBox(height: 14.0),
|
||||
const PremiumRewardCard(
|
||||
title: Text("Még pár dolog..."),
|
||||
description:
|
||||
Text("🔣\tVálassz ikon témát\n✨\tPrémium rang és csevegő a discord szerverünkön\n📬\tElsőbbségi segítségnyújtás"),
|
||||
description: Text(
|
||||
"🔣\tVálassz ikon témát\n✨\tPrémium rang és csevegő a discord szerverünkön\n📬\tElsőbbségi segítségnyújtás"),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14.0).add(const EdgeInsets.only(top: 12.0)),
|
||||
child: Row(
|
||||
children: const [
|
||||
padding: const EdgeInsets.symmetric(vertical: 14.0)
|
||||
.add(const EdgeInsets.only(top: 12.0)),
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(FilcIcons.tinta),
|
||||
SizedBox(width: 12.0),
|
||||
Expanded(
|
||||
child: Text(
|
||||
"Tinta jutalmak",
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 20),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -195,48 +234,62 @@ class PremiumScreen extends StatelessWidget {
|
||||
),
|
||||
PremiumRewardCard(
|
||||
imageKey: "premium_timetable_showcase",
|
||||
icon: SvgPicture.asset("assets/images/timetable_icon.svg", color: Theme.of(context).iconTheme.color),
|
||||
icon: SvgPicture.asset(
|
||||
"assets/images/timetable_icon.svg",
|
||||
color: Theme.of(context).iconTheme.color),
|
||||
title: const Text("Heti órarend nézet"),
|
||||
description:
|
||||
const Text("Egy órarend, ami a teljes képernyődet kihasználja, csak nem olyan idegesítő, mint az eKRÉTA féle."),
|
||||
description: const Text(
|
||||
"Egy órarend, ami a teljes képernyődet kihasználja, csak nem olyan idegesítő, mint az eKRÉTA féle."),
|
||||
),
|
||||
const SizedBox(height: 14.0),
|
||||
PremiumRewardCard(
|
||||
imageKey: "premium_widget_showcase",
|
||||
icon: SvgPicture.asset("assets/images/widget_icon.svg", color: Theme.of(context).iconTheme.color),
|
||||
icon: SvgPicture.asset(
|
||||
"assets/images/widget_icon.svg",
|
||||
color: Theme.of(context).iconTheme.color),
|
||||
title: const Text("Widget"),
|
||||
description: const Text("Mindig lásd, milyen órád lesz, a kezdőképernyőd kényelméből."),
|
||||
description: const Text(
|
||||
"Mindig lásd, milyen órád lesz, a kezdőképernyőd kényelméből."),
|
||||
),
|
||||
const SizedBox(height: 14.0),
|
||||
PremiumRewardCard(
|
||||
soon: true,
|
||||
imageKey: "premium_goal_showcase",
|
||||
icon: SvgPicture.asset("assets/images/goal_icon.svg", color: Theme.of(context).iconTheme.color),
|
||||
icon: SvgPicture.asset("assets/images/goal_icon.svg",
|
||||
color: Theme.of(context).iconTheme.color),
|
||||
title: const Text("Cél követés"),
|
||||
description: const Text("Add meg, mi a célod, és mi majd kiszámoljuk, hogyan juthatsz oda!"),
|
||||
description: const Text(
|
||||
"Add meg, mi a célod, és mi majd kiszámoljuk, hogyan juthatsz oda!"),
|
||||
),
|
||||
const SizedBox(height: 14.0),
|
||||
PremiumRewardCard(
|
||||
soon: true,
|
||||
imageKey: "premium_desktop_showcase",
|
||||
icon: SvgPicture.asset("assets/images/desktop_icon.svg", color: Theme.of(context).iconTheme.color),
|
||||
icon: SvgPicture.asset(
|
||||
"assets/images/desktop_icon.svg",
|
||||
color: Theme.of(context).iconTheme.color),
|
||||
title: const Text("Asztali verzió"),
|
||||
description: const Text("Érd el a reFilcet a gépeden is, és menekülj meg a csúnya felhasználói felületektől!"),
|
||||
description: const Text(
|
||||
"Érd el a reFilcet a gépeden is, és menekülj meg a csúnya felhasználói felületektől!"),
|
||||
),
|
||||
const SizedBox(height: 14.0),
|
||||
const PremiumRewardCard(
|
||||
title: Text("Még pár dolog..."),
|
||||
description: Text("🖋️\tMinden kupak jutalom\n✨\tKorai hozzáférés új verziókhoz"),
|
||||
description: Text(
|
||||
"🖋️\tMinden kupak jutalom\n✨\tKorai hozzáférés új verziókhoz"),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14.0).add(const EdgeInsets.only(top: 12.0)),
|
||||
child: Row(
|
||||
children: const [
|
||||
padding: const EdgeInsets.symmetric(vertical: 14.0)
|
||||
.add(const EdgeInsets.only(top: 12.0)),
|
||||
child: const Row(
|
||||
children: [
|
||||
SizedBox(width: 12.0),
|
||||
Expanded(
|
||||
child: Text(
|
||||
"Mire vársz még?",
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 20),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -244,20 +297,24 @@ class PremiumScreen extends StatelessWidget {
|
||||
),
|
||||
GithubCard(
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (context) {
|
||||
return const PremiumActivationView();
|
||||
}));
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14.0).add(const EdgeInsets.only(top: 12.0)),
|
||||
child: Row(
|
||||
children: const [
|
||||
padding: const EdgeInsets.symmetric(vertical: 14.0)
|
||||
.add(const EdgeInsets.only(top: 12.0)),
|
||||
child: const Row(
|
||||
children: [
|
||||
SizedBox(width: 12.0),
|
||||
Expanded(
|
||||
child: Text(
|
||||
"Gyakori kérdések",
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 20),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -110,7 +110,9 @@ class NavigationScreenState extends State<NavigationScreen>
|
||||
requiredNetworkType: NetworkType.ANY), (String taskId) async {
|
||||
// <-- Event handler
|
||||
// This is the fetch-event callback.
|
||||
print("[BackgroundFetch] Event received $taskId");
|
||||
if (kDebugMode) {
|
||||
print("[BackgroundFetch] Event received $taskId");
|
||||
}
|
||||
|
||||
// IMPORTANT: You must signal completion of your task or the OS can punish your app
|
||||
// for taking too long in the background.
|
||||
@@ -118,10 +120,14 @@ class NavigationScreenState extends State<NavigationScreen>
|
||||
}, (String taskId) async {
|
||||
// <-- Task timeout handler.
|
||||
// This task has exceeded its allowed running-time. You must stop what you're doing and immediately .finish(taskId)
|
||||
print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
|
||||
if (kDebugMode) {
|
||||
print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
|
||||
}
|
||||
BackgroundFetch.finish(taskId);
|
||||
});
|
||||
print('[BackgroundFetch] configure success: $status');
|
||||
if (kDebugMode) {
|
||||
print('[BackgroundFetch] configure success: $status');
|
||||
}
|
||||
|
||||
// If the widget was removed from the tree while the asynchronous platform
|
||||
// message was in flight, we want to discard the reply rather than calling
|
||||
@@ -133,7 +139,6 @@ class NavigationScreenState extends State<NavigationScreen>
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
initPlatformState();
|
||||
|
||||
HomeWidget.setAppGroupId('hu.refilc.naplo.group');
|
||||
|
||||
@@ -173,7 +178,10 @@ class NavigationScreenState extends State<NavigationScreen>
|
||||
@override
|
||||
void didChangePlatformBrightness() {
|
||||
if (settings.theme == ThemeMode.system) {
|
||||
Brightness? brightness = MediaQuery.of(context).platformBrightness;
|
||||
// ignore: deprecated_member_use
|
||||
Brightness? brightness =
|
||||
// ignore: deprecated_member_use
|
||||
WidgetsBinding.instance.window.platformBrightness;
|
||||
Provider.of<ThemeModeObserver>(context, listen: false).changeTheme(
|
||||
brightness == Brightness.light ? ThemeMode.light : ThemeMode.dark);
|
||||
}
|
||||
|
||||
@@ -484,8 +484,9 @@ class _BellDelaySettingState extends State<BellDelaySetting>
|
||||
Duration sdiff = lesson.start.difference(now);
|
||||
Duration ediff = lesson.end.difference(now);
|
||||
|
||||
if (closest == null || sdiff.abs() < closest.abs())
|
||||
if (closest == null || sdiff.abs() < closest.abs()) {
|
||||
closest = sdiff;
|
||||
}
|
||||
if (ediff.abs() < closest.abs()) closest = ediff;
|
||||
}
|
||||
if (closest != null) {
|
||||
|
||||
@@ -84,34 +84,37 @@ class _SettingsScreenState extends State<SettingsScreen>
|
||||
|
||||
String _firstName;
|
||||
|
||||
List<String> _nameParts = user.displayName?.split(" ") ?? ["?"];
|
||||
List<String> _nameParts = account.displayName.split(" ");
|
||||
if (!settings.presentationMode) {
|
||||
_firstName = _nameParts.length > 1 ? _nameParts[1] : _nameParts[0];
|
||||
} else {
|
||||
_firstName = "János";
|
||||
}
|
||||
|
||||
accountTiles.add(AccountTile(
|
||||
name: Text(!settings.presentationMode ? account.name : "János",
|
||||
style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||
username:
|
||||
Text(!settings.presentationMode ? account.username : "01234567890"),
|
||||
profileImage: ProfileImage(
|
||||
name: _firstName,
|
||||
backgroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary, //!settings.presentationMode
|
||||
//? ColorUtils.stringToColor(account.name)
|
||||
//: Theme.of(context).colorScheme.secondary,
|
||||
role: account.role,
|
||||
accountTiles.add(
|
||||
AccountTile(
|
||||
name: Text(!settings.presentationMode ? account.name : "János",
|
||||
style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||
username: Text(
|
||||
!settings.presentationMode ? account.username : "01234567890"),
|
||||
profileImage: ProfileImage(
|
||||
name: _firstName,
|
||||
role: account.role,
|
||||
profilePictureString: account.picture,
|
||||
backgroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary, //!settings.presentationMode
|
||||
//? ColorUtils.stringToColor(account.name)
|
||||
//: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
onTap: () {
|
||||
user.setUser(account.id);
|
||||
restore().then((_) => user.setUser(account.id));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
onTapMenu: () => _showBottomSheet(account),
|
||||
),
|
||||
onTap: () {
|
||||
user.setUser(account.id);
|
||||
restore().then((_) => user.setUser(account.id));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
onTapMenu: () => _showBottomSheet(account),
|
||||
));
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -153,7 +156,7 @@ class _SettingsScreenState extends State<SettingsScreen>
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.delayed(Duration.zero, () {
|
||||
futureRelease = Provider.of<UpdateProvider>(context).installedVersion();
|
||||
futureRelease = Provider.of<UpdateProvider>(context, listen: false).installedVersion();
|
||||
});
|
||||
_hideContainersController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 200));
|
||||
@@ -434,6 +437,62 @@ class _SettingsScreenState extends State<SettingsScreen>
|
||||
activeColor: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
Material(
|
||||
type: MaterialType.transparency,
|
||||
child: SwitchListTile(
|
||||
value: settings.notificationsEnabled,
|
||||
activeColor: Theme.of(context).colorScheme.secondary,
|
||||
contentPadding: const EdgeInsets.only(left: 12.0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0)),
|
||||
title: Row(children: [
|
||||
Icon(FeatherIcons.messageSquare,
|
||||
color: settings.notificationsEnabled ? Theme.of(context).colorScheme.secondary : AppColors.of(context).text.withOpacity(.25)),
|
||||
const SizedBox(width: 14.0),
|
||||
Text(
|
||||
"notifications".i18n,
|
||||
style: TextStyle(
|
||||
color: AppColors.of(context).text.withOpacity(
|
||||
settings.notificationsEnabled ? 1.0 : .5),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 10, right: 10),
|
||||
child: Center(
|
||||
child: Text("BETA",
|
||||
style: TextStyle(
|
||||
fontSize: 9.1,
|
||||
color: AppColors.of(context)
|
||||
.text
|
||||
.withOpacity(
|
||||
settings.notificationsEnabled
|
||||
? 1.0
|
||||
: .5),
|
||||
fontWeight: FontWeight.w600,
|
||||
overflow: TextOverflow.ellipsis))),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: settings.notificationsEnabled
|
||||
? Theme.of(context).colorScheme.secondary
|
||||
: AppColors.of(context).text.withOpacity(.25),
|
||||
borderRadius: BorderRadius.circular(40)),
|
||||
),
|
||||
)
|
||||
]),
|
||||
onChanged: (value) =>
|
||||
settings.update(notificationsEnabled: value),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -854,7 +913,7 @@ class _SettingsScreenState extends State<SettingsScreen>
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12.0, horizontal: 24.0),
|
||||
child: Panel(
|
||||
title: const Text("Developer Settings"),
|
||||
title: Text("devsettings".i18n),
|
||||
child: Column(
|
||||
children: [
|
||||
Material(
|
||||
@@ -863,8 +922,8 @@ class _SettingsScreenState extends State<SettingsScreen>
|
||||
contentPadding: const EdgeInsets.only(left: 12.0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0)),
|
||||
title: const Text("Developer Mode",
|
||||
style: TextStyle(fontWeight: FontWeight.w500)),
|
||||
title: Text("devmode".i18n,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||
onChanged: (v) =>
|
||||
settings.update(developerMode: false),
|
||||
value: settings.developerMode,
|
||||
@@ -873,7 +932,7 @@ class _SettingsScreenState extends State<SettingsScreen>
|
||||
),
|
||||
PanelButton(
|
||||
leading: const Icon(FeatherIcons.copy),
|
||||
title: const Text("Copy JWT"),
|
||||
title: Text("copy_jwt".i18n),
|
||||
onPressed: () => Clipboard.setData(ClipboardData(
|
||||
text:
|
||||
Provider.of<KretaClient>(context, listen: false)
|
||||
@@ -918,6 +977,9 @@ class _SettingsScreenState extends State<SettingsScreen>
|
||||
child: Text("v${release.data!['version']}"),
|
||||
);
|
||||
} else {
|
||||
String envAppVer = const String.fromEnvironment(
|
||||
"APPVER",
|
||||
defaultValue: "?");
|
||||
return DefaultTextStyle(
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
@@ -927,7 +989,7 @@ class _SettingsScreenState extends State<SettingsScreen>
|
||||
color: AppColors.of(context)
|
||||
.text
|
||||
.withOpacity(0.65)),
|
||||
child: const Text("v?"),
|
||||
child: Text("v$envAppVer"),
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -937,13 +999,13 @@ class _SettingsScreenState extends State<SettingsScreen>
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
content: Text(
|
||||
"You are $devmodeCountdown taps away from Developer Mode."),
|
||||
"devmoretaps".i18n.fill([devmodeCountdown])),
|
||||
));
|
||||
|
||||
setState(() => devmodeCountdown--);
|
||||
} else if (devmodeCountdown == 0) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||
content: Text("Developer Mode successfully activated."),
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text("devactivated".i18n),
|
||||
));
|
||||
|
||||
settings.update(developerMode: true);
|
||||
|
||||
@@ -66,6 +66,11 @@ extension SettingsLocalization on String {
|
||||
"Adaptive Theme": "Adaptive Theme",
|
||||
"presentation": "Presentation mode",
|
||||
"uwufymode": "UwU-fied mode (hungarian)",
|
||||
"devmoretaps": "You are %s taps away from Developer Mode.",
|
||||
"devactivated": "Developer Mode successfully activated.",
|
||||
"devsettings": "Developer Settings",
|
||||
"devmode": "Developer Mode",
|
||||
"copy_jwt": "Copy JWT",
|
||||
},
|
||||
"hu_hu": {
|
||||
"personal_details": "Személyes információk",
|
||||
@@ -130,6 +135,11 @@ extension SettingsLocalization on String {
|
||||
"Adaptive Theme": "Adaptív téma",
|
||||
"presentation": "Bemutató mód",
|
||||
"uwufymode": "UwU mód (magyar)",
|
||||
"devmoretaps": "Még %s koppintásra vagy a Fejlesztői módtól.",
|
||||
"devactivated": "Fejlesztői mód sikeresen aktiválva.",
|
||||
"devsettings": "Fejlesztői Beállítások",
|
||||
"devmode": "Fejlesztői mód",
|
||||
"copy_jwt": "JWT másolása",
|
||||
},
|
||||
"de_de": {
|
||||
"personal_details": "Persönliche Angaben",
|
||||
@@ -193,6 +203,11 @@ extension SettingsLocalization on String {
|
||||
"Adaptive Theme": "Adaptive Theme",
|
||||
"presentation": "Präsentationsmodus",
|
||||
"uwufymode": "UwU-Modus (ungarisch)",
|
||||
"devmoretaps": "Sie sind %s Taps vom Entwicklermodus entfernt.",
|
||||
"devactivated": "Entwicklermodus erfolgreich aktiviert.",
|
||||
"devsettings": "Entwickleroptionen",
|
||||
"devmode": "Entwicklermodus",
|
||||
"copy_jwt": "JWT kopieren",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
227
filcnaplo_mobile_ui/lib/screens/summary/pages/allsum_page.dart
Normal file
227
filcnaplo_mobile_ui/lib/screens/summary/pages/allsum_page.dart
Normal file
@@ -0,0 +1,227 @@
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/subject.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/homework_provider.dart';
|
||||
import 'package:filcnaplo_mobile_ui/screens/summary/summary_screen.i18n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AllSumBody extends StatefulWidget {
|
||||
const AllSumBody({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_AllSumBodyState createState() => _AllSumBodyState();
|
||||
}
|
||||
|
||||
class _AllSumBodyState extends State<AllSumBody> {
|
||||
late UserProvider user;
|
||||
late GradeProvider gradeProvider;
|
||||
late HomeworkProvider homeworkProvider;
|
||||
late AbsenceProvider absenceProvider;
|
||||
//late TimetableProvider timetableProvider;
|
||||
|
||||
late Map<String, Map<String, dynamic>> things = {};
|
||||
late List<Widget> firstSixTiles = [];
|
||||
late List<Widget> lastSixTiles = [];
|
||||
|
||||
int avgDropValue = 0;
|
||||
bool animation = false;
|
||||
|
||||
List<Grade> getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider
|
||||
.grades
|
||||
.where((e) =>
|
||||
e.subject == subject &&
|
||||
e.type == GradeType.midYear &&
|
||||
(days == 0 ||
|
||||
e.date.isBefore(DateTime.now().subtract(Duration(days: days)))))
|
||||
.toList();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
gradeProvider = Provider.of<GradeProvider>(context, listen: false);
|
||||
homeworkProvider = Provider.of<HomeworkProvider>(context, listen: false);
|
||||
absenceProvider = Provider.of<AbsenceProvider>(context, listen: false);
|
||||
//timetableProvider = Provider.of<TimetableProvider>(context, listen: false);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
setState(() {
|
||||
animation = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void getGrades() {
|
||||
var allGrades = gradeProvider.grades;
|
||||
var testsGrades = gradeProvider.grades.where((a) => a.value.weight == 100);
|
||||
var closingTestsGrades =
|
||||
gradeProvider.grades.where((a) => a.value.weight >= 200);
|
||||
|
||||
things.addAll({
|
||||
'tests': {'name': 'test'.i18n, 'value': testsGrades.length},
|
||||
'closingTests': {
|
||||
'name': 'closingtest'.i18n,
|
||||
'value': closingTestsGrades.length
|
||||
},
|
||||
'grades': {'name': 'grade'.i18n, 'value': allGrades.length}
|
||||
});
|
||||
}
|
||||
|
||||
void getHomework() {
|
||||
var allHomework = homeworkProvider.homework;
|
||||
|
||||
things.addAll({
|
||||
'homework': {'name': 'hw'.i18n, 'value': allHomework.length}
|
||||
});
|
||||
}
|
||||
|
||||
void getSubjects() {
|
||||
var allSubjects = gradeProvider.grades
|
||||
.map((e) => e.subject)
|
||||
.toSet()
|
||||
.toList()
|
||||
..sort((a, b) => a.name.compareTo(b.name));
|
||||
//var totalLessons;
|
||||
var totalLessons = 0;
|
||||
|
||||
things.addAll({
|
||||
'subjects': {'name': 'subject'.i18n, 'value': allSubjects.length},
|
||||
'lessons': {'name': 'lesson'.i18n, 'value': totalLessons}
|
||||
});
|
||||
}
|
||||
|
||||
void getAbsences() {
|
||||
var allAbsences = absenceProvider.absences.where((a) => a.delay == 0);
|
||||
var excusedAbsences = absenceProvider.absences
|
||||
.where((a) => a.state == Justification.excused && a.delay == 0);
|
||||
var unexcusedAbsences = absenceProvider.absences.where((a) =>
|
||||
(a.state == Justification.unexcused ||
|
||||
a.state == Justification.pending) &&
|
||||
a.delay == 0);
|
||||
|
||||
things.addAll({
|
||||
'absences': {'name': 'absence_sum'.i18n, 'value': allAbsences.length},
|
||||
'excusedAbsences': {
|
||||
'name': 'excused'.i18n,
|
||||
'value': excusedAbsences.length
|
||||
},
|
||||
'unexcusedAbsences': {
|
||||
'name': 'unexcused'.i18n,
|
||||
'value': unexcusedAbsences.length
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void getDelays() {
|
||||
var allDelays = absenceProvider.absences.where((a) => a.delay > 0);
|
||||
var totalDelayTime = (allDelays.map((a) {
|
||||
return a.delay;
|
||||
}).toList())
|
||||
.reduce((a, b) => a + b);
|
||||
var unexcusedDelays = absenceProvider.absences
|
||||
.where((a) => a.state == Justification.unexcused && a.delay > 0);
|
||||
|
||||
things.addAll({
|
||||
'delays': {'name': 'delay_sum'.i18n, 'value': allDelays.length},
|
||||
'totalDelay': {'name': 'min'.i18n, 'value': totalDelayTime},
|
||||
'unexcusedDelays': {
|
||||
'name': 'unexcused'.i18n,
|
||||
'value': unexcusedDelays.length
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void getEverything() {
|
||||
getGrades();
|
||||
getHomework();
|
||||
getSubjects();
|
||||
getAbsences();
|
||||
getDelays();
|
||||
}
|
||||
|
||||
void generateTiles() {
|
||||
for (var i in things.values) {
|
||||
Widget w = Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
i.values.toList()[1].toString(),
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 36.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
i.values.toList()[0],
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// TODO: az orakat es a hazikat szarul keri le, de majd meg lesz csinalva
|
||||
if (firstSixTiles.length < 6) {
|
||||
firstSixTiles.add(w);
|
||||
} else if (lastSixTiles.length < 6) {
|
||||
lastSixTiles.add(w);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
getEverything();
|
||||
generateTiles();
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 45,
|
||||
),
|
||||
AnimatedContainer(
|
||||
curve: Curves.easeInOut,
|
||||
duration: const Duration(milliseconds: 420),
|
||||
transform: Matrix4.translationValues(
|
||||
animation ? 0 : MediaQuery.of(context).size.width, 0, 0),
|
||||
height: 250,
|
||||
child: GridView.count(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 0,
|
||||
crossAxisSpacing: 5,
|
||||
children: firstSixTiles,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
AnimatedContainer(
|
||||
curve: Curves.easeInOut,
|
||||
duration: const Duration(milliseconds: 420),
|
||||
transform: Matrix4.translationValues(
|
||||
animation ? 0 : -MediaQuery.of(context).size.width, 0, 0),
|
||||
height: 250,
|
||||
child: GridView.count(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 0,
|
||||
crossAxisSpacing: 5,
|
||||
children: lastSixTiles,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,362 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:math';
|
||||
|
||||
class GradesBody extends StatelessWidget {
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo/helpers/average_helper.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/subject.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
|
||||
import 'package:filcnaplo_mobile_ui/screens/summary/summary_screen.i18n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:i18n_extension/i18n_widget.dart';
|
||||
|
||||
List<String> faces = [
|
||||
"(·.·)",
|
||||
"(≥o≤)",
|
||||
"(·_·)",
|
||||
"(˚Δ˚)b",
|
||||
"(^-^*)",
|
||||
"(='X'=)",
|
||||
"(>_<)",
|
||||
"(;-;)",
|
||||
"\\(^Д^)/",
|
||||
"\\(o_o)/",
|
||||
];
|
||||
|
||||
class GradesBody extends StatefulWidget {
|
||||
const GradesBody({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_GradesBodyState createState() => _GradesBodyState();
|
||||
}
|
||||
|
||||
class _GradesBodyState extends State<GradesBody> {
|
||||
late UserProvider user;
|
||||
late GradeProvider gradeProvider;
|
||||
late SettingsProvider settings;
|
||||
|
||||
late double subjectAvg;
|
||||
late double endYearAvg;
|
||||
late String endYearAvgText;
|
||||
|
||||
List<Widget> subjectTiles5 = [];
|
||||
List<Widget> subjectTiles3 = [];
|
||||
List<Widget> subjectTiles1 = [];
|
||||
|
||||
int avgDropValue = 0;
|
||||
bool animation = false;
|
||||
|
||||
List<Grade> getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider
|
||||
.grades
|
||||
.where((e) =>
|
||||
e.subject == subject &&
|
||||
e.type == GradeType.midYear &&
|
||||
(days == 0 ||
|
||||
e.date.isBefore(DateTime.now().subtract(Duration(days: days)))))
|
||||
.toList();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
gradeProvider = Provider.of<GradeProvider>(context, listen: false);
|
||||
settings = Provider.of<SettingsProvider>(context, listen: false);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
setState(() {
|
||||
animation = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void generateTiles({required int filter}) {
|
||||
List<Subject> subjects = gradeProvider.grades
|
||||
.map((e) => e.subject)
|
||||
.toSet()
|
||||
.toList()
|
||||
..sort((a, b) => a.name.compareTo(b.name));
|
||||
List<Widget> tiles = [];
|
||||
|
||||
Map<Subject, double> subjectAvgs = {};
|
||||
|
||||
var count = 1;
|
||||
|
||||
for (Subject subject in subjects) {
|
||||
List<Grade> subjectGrades = getSubjectGrades(subject);
|
||||
|
||||
double avg = AverageHelper.averageEvals(subjectGrades);
|
||||
if (avg != 0) subjectAvgs[subject] = avg;
|
||||
|
||||
Widget widget = AnimatedContainer(
|
||||
curve: Curves.easeInOut,
|
||||
duration: Duration(milliseconds: 300 + (count * 120)),
|
||||
transform: Matrix4.translationValues(
|
||||
animation ? 0 : MediaQuery.of(context).size.width, 0, 0),
|
||||
child: Row(
|
||||
children: [
|
||||
GradeValueWidget(
|
||||
GradeValue(avg.round(), '', '', 100),
|
||||
fill: true,
|
||||
size: 28.0,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
subject.renamedTo ?? subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20.0,
|
||||
color: Colors.white.withOpacity(0.98),
|
||||
fontStyle: settings.renamedSubjectsItalics && subject.isRenamed
|
||||
? FontStyle.italic
|
||||
: null,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (avg.round() == filter) {
|
||||
tiles.add(widget);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (tiles.isEmpty) {
|
||||
int index = Random(DateTime.now().minute).nextInt(faces.length);
|
||||
Widget faceWidget = Center(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: faces[index],
|
||||
style: const TextStyle(
|
||||
fontSize: 32.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "\n${'no_grades'.i18n}",
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
height: 2.0,
|
||||
color: Colors.white.withOpacity(0.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
tiles.insert(0, faceWidget);
|
||||
}
|
||||
|
||||
subjectAvg = subjectAvgs.isNotEmpty
|
||||
? subjectAvgs.values.fold(0.0, (double a, double b) => a + b) /
|
||||
subjectAvgs.length
|
||||
: 0.0;
|
||||
|
||||
List<Grade> endYearGrades = gradeProvider.grades
|
||||
.where((grade) => grade.type == GradeType.endYear)
|
||||
.toList();
|
||||
endYearAvg = AverageHelper.averageEvals(endYearGrades, finalAvg: true);
|
||||
endYearAvgText = endYearAvg.toStringAsFixed(1);
|
||||
if (I18n.of(context).locale.languageCode != "en") {
|
||||
endYearAvgText = endYearAvgText.replaceAll(".", ",");
|
||||
}
|
||||
|
||||
if (filter == 5) {
|
||||
subjectTiles5 = List.castFrom(tiles);
|
||||
if (subjectTiles5.length > 4) {
|
||||
subjectTiles5.length = 4;
|
||||
}
|
||||
} else if (filter == 3) {
|
||||
subjectTiles3 = List.castFrom(tiles);
|
||||
if (subjectTiles3.length > 3) {
|
||||
subjectTiles3.length = 3;
|
||||
}
|
||||
} else if (filter == 1) {
|
||||
subjectTiles1 = List.castFrom(tiles);
|
||||
if (subjectTiles1.length > 2) {
|
||||
subjectTiles1.length = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getGrades() {
|
||||
generateTiles(filter: 5);
|
||||
generateTiles(filter: 3);
|
||||
generateTiles(filter: 1);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column();
|
||||
user = Provider.of<UserProvider>(context);
|
||||
settings = Provider.of<SettingsProvider>(context);
|
||||
|
||||
getGrades();
|
||||
|
||||
return Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: ((100 * subjectTiles5.length) /
|
||||
(subjectTiles5[0].runtimeType == AnimatedContainer
|
||||
? 1.95
|
||||
: 1.2))
|
||||
.toDouble(),
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: max(subjectTiles5.length, 1),
|
||||
itemBuilder: (context, index) {
|
||||
if (subjectTiles5.isNotEmpty) {
|
||||
EdgeInsetsGeometry panelPadding =
|
||||
const EdgeInsets.symmetric(horizontal: 24.0);
|
||||
|
||||
if (subjectTiles5[index].runtimeType == AnimatedContainer) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: subjectTiles5[index]);
|
||||
} else {
|
||||
return Padding(
|
||||
padding: panelPadding, child: subjectTiles5[index]);
|
||||
}
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
Text(
|
||||
'tryagain'.i18n,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
SizedBox(
|
||||
height: ((100 * subjectTiles3.length) /
|
||||
(subjectTiles3[0].runtimeType == AnimatedContainer
|
||||
? 1.95
|
||||
: 1.2))
|
||||
.toDouble(),
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: max(subjectTiles3.length, 1),
|
||||
itemBuilder: (context, index) {
|
||||
if (subjectTiles3.isNotEmpty) {
|
||||
EdgeInsetsGeometry panelPadding =
|
||||
const EdgeInsets.symmetric(horizontal: 24.0);
|
||||
|
||||
if (subjectTiles3[index].runtimeType == AnimatedContainer) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: subjectTiles3[index]);
|
||||
} else {
|
||||
return Padding(
|
||||
padding: panelPadding, child: subjectTiles3[index]);
|
||||
}
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
Text(
|
||||
'oops'.i18n,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
SizedBox(
|
||||
height: ((100 * subjectTiles1.length) /
|
||||
(subjectTiles1[0].runtimeType == AnimatedContainer
|
||||
? 1.95
|
||||
: 1.2))
|
||||
.toDouble(),
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: max(subjectTiles1.length, 1),
|
||||
itemBuilder: (context, index) {
|
||||
if (subjectTiles1.isNotEmpty) {
|
||||
EdgeInsetsGeometry panelPadding =
|
||||
const EdgeInsets.symmetric(horizontal: 24.0);
|
||||
|
||||
if (subjectTiles1[index].runtimeType == AnimatedContainer) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: subjectTiles1[index]);
|
||||
} else {
|
||||
return Padding(
|
||||
padding: panelPadding, child: subjectTiles1[index]);
|
||||
}
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30.0),
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'endyear_avg'.i18n,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 10.0),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 4.0),
|
||||
decoration: BoxDecoration(
|
||||
color: gradeColor(context: context, value: endYearAvg)
|
||||
.withOpacity(.2),
|
||||
border: Border.all(
|
||||
color: (gradeColor(context: context, value: endYearAvg))
|
||||
.withOpacity(0.0),
|
||||
width: 2.0,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(45.0),
|
||||
),
|
||||
child: AutoSizeText.rich(
|
||||
TextSpan(
|
||||
text: endYearAvgText,
|
||||
),
|
||||
maxLines: 1,
|
||||
minFontSize: 5,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: gradeColor(context: context, value: endYearAvg),
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 32.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
319
filcnaplo_mobile_ui/lib/screens/summary/pages/lessons_page.dart
Normal file
319
filcnaplo_mobile_ui/lib/screens/summary/pages/lessons_page.dart
Normal file
@@ -0,0 +1,319 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/subject.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/week.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
|
||||
import 'package:filcnaplo_mobile_ui/screens/summary/summary_screen.i18n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
List<String> faces = [
|
||||
"(·.·)",
|
||||
"(≥o≤)",
|
||||
"(·_·)",
|
||||
"(˚Δ˚)b",
|
||||
"(^-^*)",
|
||||
"(='X'=)",
|
||||
"(>_<)",
|
||||
"(;-;)",
|
||||
"\\(^Д^)/",
|
||||
"\\(o_o)/",
|
||||
];
|
||||
|
||||
class SubjectAbsence {
|
||||
Subject subject;
|
||||
List<Absence> absences;
|
||||
double percentage;
|
||||
|
||||
SubjectAbsence(
|
||||
{required this.subject, this.absences = const [], this.percentage = 0.0});
|
||||
}
|
||||
|
||||
class LessonsBody extends StatefulWidget {
|
||||
const LessonsBody({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_LessonsBodyState createState() => _LessonsBodyState();
|
||||
}
|
||||
|
||||
class _LessonsBodyState extends State<LessonsBody> {
|
||||
late UserProvider user;
|
||||
late AbsenceProvider absenceProvider;
|
||||
late SettingsProvider settingsProvider;
|
||||
late TimetableProvider timetableProvider;
|
||||
|
||||
late List<SubjectAbsence> absences = [];
|
||||
late List<Widget> lessons = [];
|
||||
late List<Absence> delays = [];
|
||||
final Map<Subject, Lesson> _lessonCount = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
absenceProvider = Provider.of<AbsenceProvider>(context, listen: false);
|
||||
settingsProvider = Provider.of<SettingsProvider>(context, listen: false);
|
||||
timetableProvider = Provider.of<TimetableProvider>(context, listen: false);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
for (final lesson in timetableProvider.getWeek(Week.current()) ?? []) {
|
||||
if (!lesson.isEmpty &&
|
||||
lesson.subject.id != '' &&
|
||||
lesson.lessonYearIndex != null) {
|
||||
_lessonCount.update(
|
||||
lesson.subject,
|
||||
(value) {
|
||||
if (lesson.lessonYearIndex! > value.lessonYearIndex!) {
|
||||
return lesson;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
ifAbsent: () => lesson,
|
||||
);
|
||||
}
|
||||
}
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
void buildSubjectAbsences() {
|
||||
Map<Subject, SubjectAbsence> _absences = {};
|
||||
|
||||
for (final absence in absenceProvider.absences) {
|
||||
if (absence.delay != 0) continue;
|
||||
|
||||
if (!_absences.containsKey(absence.subject)) {
|
||||
_absences[absence.subject] =
|
||||
SubjectAbsence(subject: absence.subject, absences: [absence]);
|
||||
} else {
|
||||
_absences[absence.subject]?.absences.add(absence);
|
||||
}
|
||||
}
|
||||
|
||||
_absences.forEach((subject, absence) {
|
||||
final absentLessonsOfSubject = absenceProvider.absences
|
||||
.where((e) => e.subject == subject && e.delay == 0)
|
||||
.length;
|
||||
final totalLessonsOfSubject = _lessonCount[subject]?.lessonYearIndex ?? 0;
|
||||
|
||||
double absentLessonsOfSubjectPercentage;
|
||||
|
||||
if (absentLessonsOfSubject <= totalLessonsOfSubject) {
|
||||
absentLessonsOfSubjectPercentage =
|
||||
absentLessonsOfSubject / totalLessonsOfSubject * 100;
|
||||
} else {
|
||||
absentLessonsOfSubjectPercentage = -1;
|
||||
}
|
||||
|
||||
_absences[subject]?.percentage =
|
||||
absentLessonsOfSubjectPercentage.clamp(-1, 100.0);
|
||||
});
|
||||
|
||||
absences = _absences.values.toList();
|
||||
absences.sort((a, b) => -a.percentage.compareTo(b.percentage));
|
||||
}
|
||||
|
||||
void getAndSortDelays() {
|
||||
delays = absenceProvider.absences;
|
||||
delays.sort((a, b) => -a.delay.compareTo(b.delay));
|
||||
}
|
||||
|
||||
void generateTiles() {
|
||||
Widget leastAbsent = Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
SubjectIcon.resolveVariant(
|
||||
subject: absences.last.subject, context: context),
|
||||
color: Colors.white,
|
||||
size: 64,
|
||||
),
|
||||
Text(
|
||||
absences.last.subject.renamedTo ??
|
||||
absences.last.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 36.0,
|
||||
fontStyle: absences.last.subject.isRenamed &&
|
||||
settingsProvider.renamedSubjectsItalics
|
||||
? FontStyle.italic
|
||||
: null,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'absence'.i18n.fill([absences.last.absences.length]),
|
||||
style: const TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
if (absences.last.absences.isNotEmpty) {
|
||||
lessons.add(leastAbsent);
|
||||
} else {
|
||||
lessons.add(buildFaceWidget());
|
||||
}
|
||||
|
||||
Widget mostAbsent = Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
SubjectIcon.resolveVariant(
|
||||
subject: absences.first.subject, context: context),
|
||||
color: Colors.white,
|
||||
size: 64,
|
||||
),
|
||||
Text(
|
||||
absences.first.subject.renamedTo ??
|
||||
absences.first.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 36.0,
|
||||
fontStyle: absences.first.subject.isRenamed &&
|
||||
settingsProvider.renamedSubjectsItalics
|
||||
? FontStyle.italic
|
||||
: null,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'absence'.i18n.fill([absences.first.absences.length]),
|
||||
style: const TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
if (absences.first.absences.isNotEmpty) {
|
||||
lessons.add(mostAbsent);
|
||||
} else {
|
||||
lessons.add(buildFaceWidget());
|
||||
}
|
||||
|
||||
Widget mostDelays = Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
SubjectIcon.resolveVariant(
|
||||
subject: delays.first.subject, context: context),
|
||||
color: Colors.white,
|
||||
size: 64,
|
||||
),
|
||||
Text(
|
||||
delays.first.subject.renamedTo ??
|
||||
delays.first.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 36.0,
|
||||
fontStyle: delays.first.subject.isRenamed &&
|
||||
settingsProvider.renamedSubjectsItalics
|
||||
? FontStyle.italic
|
||||
: null,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'delay'.i18n.fill([delays.first.delay]),
|
||||
style: const TextStyle(
|
||||
fontSize: 18.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
if (delays.first.delay != 0) {
|
||||
lessons.add(mostDelays);
|
||||
} else {
|
||||
lessons.add(buildFaceWidget());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
buildSubjectAbsences();
|
||||
getAndSortDelays();
|
||||
generateTiles();
|
||||
|
||||
return Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
lessons[0],
|
||||
const SizedBox(height: 18.0),
|
||||
Text(
|
||||
'dontfelt'.i18n,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 18.0),
|
||||
lessons[1],
|
||||
const SizedBox(height: 18.0),
|
||||
Text(
|
||||
'youlate'.i18n,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 18.0),
|
||||
lessons[2],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildFaceWidget() {
|
||||
int index = Random(DateTime.now().minute).nextInt(faces.length);
|
||||
return Center(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: faces[index],
|
||||
style: const TextStyle(
|
||||
fontSize: 32.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "\n${'no_lesson'.i18n}",
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
height: 2.0,
|
||||
color: Colors.white.withOpacity(0.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,128 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io';
|
||||
|
||||
class PersonalityBody extends StatelessWidget {
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/personality_card/empty_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/personality_card/personality_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/screens/summary/summary_screen.i18n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:screenshot/screenshot.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||
|
||||
class PersonalityBody extends StatefulWidget {
|
||||
const PersonalityBody({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_PersonalityBodyState createState() => _PersonalityBodyState();
|
||||
}
|
||||
|
||||
class _PersonalityBodyState extends State<PersonalityBody> {
|
||||
late UserProvider user;
|
||||
|
||||
bool isRevealed = false;
|
||||
|
||||
ScreenshotController screenshotController = ScreenshotController();
|
||||
|
||||
sharePersonality() async {
|
||||
await screenshotController.capture().then((image) async {
|
||||
if (image != null) {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
if (await File('${directory.path}/refilc_personality.png').exists()) {
|
||||
await File('${directory.path}/refilc_personality.png').delete();
|
||||
}
|
||||
final imagePath =
|
||||
await File('${directory.path}/refilc_personality.png').create();
|
||||
await imagePath.writeAsBytes(image);
|
||||
|
||||
await Share.shareXFiles([XFile(imagePath.path)]);
|
||||
}
|
||||
}).catchError((err) {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
savePersonality() async {
|
||||
await screenshotController.capture().then((image) async {
|
||||
if (image != null) {
|
||||
await ImageGallerySaver.saveImage(image, name: 'refilc_personality');
|
||||
}
|
||||
}).catchError((err) {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column();
|
||||
user = Provider.of<UserProvider>(context);
|
||||
|
||||
return Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
const SizedBox(height: 30),
|
||||
AnimatedCrossFade(
|
||||
duration: const Duration(milliseconds: 1000),
|
||||
sizeCurve: Curves.easeInToLinear,
|
||||
firstChild: Screenshot(
|
||||
controller: screenshotController,
|
||||
child: PersonalityCard(user: user),
|
||||
),
|
||||
secondChild: GestureDetector(
|
||||
onTap: () => setState(() {
|
||||
isRevealed = true;
|
||||
}),
|
||||
child: EmptyCard(text: 'click_reveal'.i18n),
|
||||
),
|
||||
crossFadeState: isRevealed
|
||||
? CrossFadeState.showFirst
|
||||
: CrossFadeState.showSecond,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
if (isRevealed)
|
||||
Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await sharePersonality();
|
||||
},
|
||||
icon: const Icon(
|
||||
FeatherIcons.share,
|
||||
color: Colors.white,
|
||||
size: 30,
|
||||
),
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
Colors.white.withOpacity(0.2)),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await savePersonality();
|
||||
},
|
||||
icon: const Icon(
|
||||
FeatherIcons.bookmark,
|
||||
color: Colors.white,
|
||||
size: 30,
|
||||
),
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
Colors.white.withOpacity(0.2)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 60),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
|
||||
import 'package:filcnaplo_mobile_ui/screens/summary/summary_screen.dart';
|
||||
import 'package:filcnaplo_mobile_ui/screens/summary/summary_screen.i18n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wtf_sliding_sheet/wtf_sliding_sheet.dart';
|
||||
|
||||
class StartBody extends StatefulWidget {
|
||||
const StartBody({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_StartBodyState createState() => _StartBodyState();
|
||||
}
|
||||
|
||||
class _StartBodyState extends State<StartBody> {
|
||||
late UserProvider user;
|
||||
late GradeProvider gradeProvider;
|
||||
late SettingsProvider settings;
|
||||
|
||||
late String firstName;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
gradeProvider = Provider.of<GradeProvider>(context, listen: false);
|
||||
settings = Provider.of<SettingsProvider>(context, listen: false);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(height: 40.0),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showSlidingBottomSheet(
|
||||
context,
|
||||
useRootNavigator: true,
|
||||
builder: (context) => SlidingSheetDialog(
|
||||
color: Colors.black.withOpacity(0.99),
|
||||
duration: const Duration(milliseconds: 400),
|
||||
scrollSpec: const ScrollSpec.bouncingScroll(),
|
||||
snapSpec: const SnapSpec(
|
||||
snap: true,
|
||||
snappings: [1.0],
|
||||
initialSnap: 1.0,
|
||||
positioning: SnapPositioning.relativeToAvailableSpace,
|
||||
),
|
||||
minHeight: MediaQuery.of(context).size.height,
|
||||
cornerRadius: 16,
|
||||
cornerRadiusOnFullscreen: 0,
|
||||
builder: (context, state) => const Material(
|
||||
color: Colors.black,
|
||||
child: SummaryScreen(
|
||||
currentPage: 'grades',
|
||||
isBottomSheet: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
FeatherIcons.arrowRight,
|
||||
size: 145,
|
||||
color: Colors.white,
|
||||
grade: 0.001,
|
||||
weight: 0.001,
|
||||
),
|
||||
Text(
|
||||
'start'.i18n,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16.0,
|
||||
color: Colors.white.withOpacity(0.7),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 169.69),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,48 @@
|
||||
import 'package:confetti/confetti.dart';
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wtf_sliding_sheet/wtf_sliding_sheet.dart';
|
||||
import 'summary_screen.i18n.dart';
|
||||
|
||||
import 'pages/allsum_page.dart';
|
||||
import 'pages/start_page.dart';
|
||||
import 'pages/grades_page.dart';
|
||||
import 'pages/lessons_page.dart';
|
||||
import 'pages/personality_page.dart';
|
||||
|
||||
class SummaryScreen extends StatefulWidget {
|
||||
final String currentPage;
|
||||
final bool isBottomSheet;
|
||||
|
||||
const SummaryScreen({Key? key, this.currentPage = 'personality'})
|
||||
: super(key: key);
|
||||
const SummaryScreen({
|
||||
Key? key,
|
||||
this.currentPage = 'personality',
|
||||
this.isBottomSheet = false,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SummaryScreenState createState() => _SummaryScreenState();
|
||||
|
||||
static show(
|
||||
{required BuildContext context,
|
||||
String currentPage = 'personality'}) =>
|
||||
Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute(
|
||||
builder: (context) => SummaryScreen(currentPage: currentPage)));
|
||||
}
|
||||
|
||||
class _SummaryScreenState extends State<SummaryScreen> {
|
||||
class _SummaryScreenState extends State<SummaryScreen>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late UserProvider user;
|
||||
late SettingsProvider settings;
|
||||
|
||||
ConfettiController? _confettiController;
|
||||
|
||||
late String firstName;
|
||||
|
||||
final LinearGradient _backgroundGradient = const LinearGradient(
|
||||
colors: [
|
||||
Color(0xff1d56ac),
|
||||
@@ -22,34 +53,170 @@ class _SummaryScreenState extends State<SummaryScreen> {
|
||||
stops: [-1.0, 1.0],
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_confettiController?.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
user = Provider.of<UserProvider>(context);
|
||||
settings = Provider.of<SettingsProvider>(context);
|
||||
|
||||
List<String> nameParts = user.displayName?.split(" ") ?? ["?"];
|
||||
if (!settings.presentationMode) {
|
||||
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
|
||||
} else {
|
||||
firstName = "János";
|
||||
}
|
||||
|
||||
return widget.isBottomSheet
|
||||
? buildContainer()
|
||||
: Scaffold(
|
||||
body: buildContainer(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildContainer() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(gradient: _backgroundGradient),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(gradient: _backgroundGradient),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(gradient: _backgroundGradient),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 24.0,
|
||||
right: 24.0,
|
||||
top: 26.0 + MediaQuery.of(context).padding.top,
|
||||
bottom: 52.0,
|
||||
),
|
||||
child: widget.currentPage == 'grades'
|
||||
? const GradesBody()
|
||||
: widget.currentPage == 'lessons'
|
||||
? const GradesBody()
|
||||
: widget.currentPage == 'allsum'
|
||||
? const GradesBody()
|
||||
: const GradesBody(),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 24.0,
|
||||
right: 24.0,
|
||||
top: 15.0,
|
||||
bottom: 40.0,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: widget.currentPage == 'start'
|
||||
? CrossAxisAlignment.center
|
||||
: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: widget.currentPage == 'start'
|
||||
? MainAxisAlignment.spaceBetween
|
||||
: MainAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'greeting'.i18n.fill([firstName]),
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 26.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
widget.currentPage == 'start'
|
||||
? 'title_start'.i18n
|
||||
: widget.currentPage == 'grades'
|
||||
? 'title_grades'.i18n
|
||||
: widget.currentPage == 'lessons'
|
||||
? 'title_lessons'.i18n
|
||||
: widget.currentPage == 'personality'
|
||||
? 'title_personality'.i18n
|
||||
: '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
widget.currentPage != 'start'
|
||||
? IconButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
if (widget.currentPage == 'grades') {
|
||||
openNewPage(page: 'lessons');
|
||||
} else if (widget.currentPage == 'lessons') {
|
||||
openNewPage(page: 'allsum');
|
||||
} else if (widget.currentPage == 'allsum') {
|
||||
openNewPage(page: 'personality');
|
||||
} else {
|
||||
Navigator.of(context).maybePop();
|
||||
}
|
||||
},
|
||||
icon: Icon(
|
||||
widget.currentPage == 'personality'
|
||||
? FeatherIcons.x
|
||||
: FeatherIcons.arrowRight,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
: Container()
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
widget.currentPage == 'start'
|
||||
? const StartBody()
|
||||
: widget.currentPage == 'grades'
|
||||
? const GradesBody()
|
||||
: widget.currentPage == 'lessons'
|
||||
? const LessonsBody()
|
||||
: widget.currentPage == 'allsum'
|
||||
? const AllSumBody()
|
||||
: const PersonalityBody(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void openNewPage({String page = 'personality'}) {
|
||||
showSlidingBottomSheet(
|
||||
context,
|
||||
useRootNavigator: true,
|
||||
builder: (context) => SlidingSheetDialog(
|
||||
color: Colors.black.withOpacity(0.99),
|
||||
duration: const Duration(milliseconds: 400),
|
||||
scrollSpec: const ScrollSpec.bouncingScroll(),
|
||||
snapSpec: const SnapSpec(
|
||||
snap: true,
|
||||
snappings: [1.0],
|
||||
initialSnap: 1.0,
|
||||
positioning: SnapPositioning.relativeToAvailableSpace,
|
||||
),
|
||||
minHeight: MediaQuery.of(context).size.height,
|
||||
cornerRadius: 16,
|
||||
cornerRadiusOnFullscreen: 0,
|
||||
builder: (context, state) => Material(
|
||||
color: Colors.black,
|
||||
child: SummaryScreen(
|
||||
currentPage: page,
|
||||
isBottomSheet: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
//SummaryScreen.show(context: context, currentPage: page);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,111 @@ import 'package:i18n_extension/i18n_extension.dart';
|
||||
extension SettingsLocalization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {},
|
||||
"hu_hu": {},
|
||||
"de_de": {},
|
||||
"en_en": {
|
||||
// main thingies
|
||||
"no_grades": "No grades found",
|
||||
"no_lesson": "No lessons found",
|
||||
"greeting": "You had a good year, %s!",
|
||||
"title_start": "So let's summarize...",
|
||||
"title_grades": "Let's look at your marks... 📖",
|
||||
"title_lessons": "Your favorite lesson 💓",
|
||||
"title_personality": "Your personality is...",
|
||||
// start page
|
||||
"start": "Start",
|
||||
// grades page
|
||||
"tryagain": "He puts the master to the test! 🔃",
|
||||
"oops": "Ouch... 🥴",
|
||||
"endyear_avg": "Year-end average",
|
||||
// lessons page
|
||||
"absence": "%s absence(s)",
|
||||
"delay": "A total of %s minute(s) late",
|
||||
"dontfelt": "You didn't like it...",
|
||||
"youlate": "You're late!",
|
||||
// allsum page
|
||||
"test": "test(s)",
|
||||
"closingtest": "module test(s)",
|
||||
"grade": "grades",
|
||||
"hw": "homework",
|
||||
"subject": "subjects",
|
||||
"lesson": "lessons",
|
||||
"absence_sum": "absence(s)",
|
||||
"excused": "excused",
|
||||
"unexcused": "unexcused",
|
||||
"delay_sum": "delay(s)",
|
||||
"min": "minute(s)",
|
||||
// personality page
|
||||
"click_reveal": "Click to reveal...",
|
||||
},
|
||||
"hu_hu": {
|
||||
// main thingies
|
||||
"no_grades": "Nincsenek jegyek",
|
||||
"no_lesson": "Nincsenek tanórák",
|
||||
"greeting": "Jó éved volt, %s!",
|
||||
"title_start": "Összegezzünk hát...",
|
||||
"title_grades": "Nézzük a jegyeidet... 📖",
|
||||
"title_lessons": "A kedvenc órád 💓",
|
||||
"title_personality": "A te személyiséged...",
|
||||
// start page
|
||||
"start": "Kezdés",
|
||||
// grades page
|
||||
"tryagain": "Próba teszi a mestert! 🔃",
|
||||
"oops": "Ajjaj... 🥴",
|
||||
"endyear_avg": "Év végi átlagod",
|
||||
// lessons page
|
||||
"absence": "%s hiányzás",
|
||||
"delay": "Összesen %s perc késés",
|
||||
"dontfelt": "Nem volt kedved hozzá...",
|
||||
"youlate": "Késtél!",
|
||||
// allsum page
|
||||
"test": "dolgozat",
|
||||
"closingtest": "témazáró",
|
||||
"grade": "jegy",
|
||||
"hw": "házi",
|
||||
"subject": "tantárgy",
|
||||
"lesson": "óra",
|
||||
"absence_sum": "hiányzás",
|
||||
"excused": "igazolt",
|
||||
"unexcused": "igazolatlan",
|
||||
"delay_sum": "késés",
|
||||
"min": "perc",
|
||||
// personality page
|
||||
"click_reveal": "Kattints a felfedéshez...",
|
||||
},
|
||||
"de_de": {
|
||||
// main thingies
|
||||
"no_grades": "Keine Grade gefunden",
|
||||
"no_lesson": "Keine Lektionen gefunden",
|
||||
"greeting": "Du hattest ein gutes Jahr, %s!",
|
||||
"title_start": "Fassen wir also zusammen...",
|
||||
"title_grades": "Schauen wir uns eure Tickets an... 📖",
|
||||
"title_lessons": "Deine Lieblingsuhr 💓",
|
||||
"title_personality": "Deine Persönlichkeit...",
|
||||
// start page
|
||||
"start": "Anfang",
|
||||
// grades page
|
||||
"tryagain": "Er stellt den Meister auf die Probe! 🔃",
|
||||
"oops": "Autsch... 🥴",
|
||||
"endyear_avg": "Ihr Jahresenddurchschnitt",
|
||||
// lessons page
|
||||
"absence": "%s Abwesenheit(en)",
|
||||
"delay": "Insgesamt %s Minute(n) zu spät",
|
||||
"dontfelt": "Es hat dir nicht gefallen...",
|
||||
"youlate": "Du bist spät!",
|
||||
// allsum page
|
||||
"test": "These(n)",
|
||||
"closingtest": "Modultest",
|
||||
"grade": "Grad",
|
||||
"hw": "Hausaufgaben",
|
||||
"subject": "Themen",
|
||||
"lesson": "Lektionen",
|
||||
"absence_sum": "Abwesenheit(en)",
|
||||
"excused": "bescheinigte",
|
||||
"unexcused": "unentschuldigte",
|
||||
"delay_sum": "Verzögerung(en)",
|
||||
"min": "Minute(n)",
|
||||
// personality page
|
||||
"click_reveal": "Klicken Sie hier, um es anzuzeigen...",
|
||||
},
|
||||
};
|
||||
|
||||
String get i18n => localize(this, _t);
|
||||
|
||||
@@ -39,7 +39,10 @@ dependencies:
|
||||
flutter_svg: ^1.1.6
|
||||
background_fetch: ^1.1.5
|
||||
wtf_sliding_sheet: ^1.0.0
|
||||
package_info_plus: ^3.1.2
|
||||
package_info_plus: ^4.0.2
|
||||
dotted_border: ^2.0.0+3
|
||||
screenshot: ^2.1.0
|
||||
image_gallery_saver: ^2.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^1.0.0
|
||||
|
||||
@@ -24,7 +24,8 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
|
||||
setState(() {
|
||||
manualActivationLoading = true;
|
||||
});
|
||||
final result = await context.read<PremiumProvider>().auth.finishAuth(data.text!);
|
||||
final result =
|
||||
await context.read<PremiumProvider>().auth.finishAuth(data.text!);
|
||||
setState(() {
|
||||
manualActivationLoading = false;
|
||||
});
|
||||
@@ -63,24 +64,27 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14.0)),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: const [
|
||||
Icon(FeatherIcons.alertTriangle, size: 20.0, color: Colors.orange),
|
||||
children: [
|
||||
Icon(FeatherIcons.alertTriangle,
|
||||
size: 20.0, color: Colors.orange),
|
||||
SizedBox(width: 12.0),
|
||||
Text(
|
||||
"Figyelem!",
|
||||
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
fontSize: 18.0, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6.0),
|
||||
const Text(
|
||||
SizedBox(height: 6.0),
|
||||
Text(
|
||||
"Csak akkor érzékeli a Filc a támogatói státuszod, ha nem állítod privátra!",
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
),
|
||||
@@ -90,24 +94,27 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14.0)),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: const [
|
||||
Icon(FeatherIcons.alertTriangle, size: 20.0, color: Colors.orange),
|
||||
children: [
|
||||
Icon(FeatherIcons.alertTriangle,
|
||||
size: 20.0, color: Colors.orange),
|
||||
SizedBox(width: 12.0),
|
||||
Text(
|
||||
"Figyelem!",
|
||||
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
fontSize: 18.0, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6.0),
|
||||
const Text(
|
||||
SizedBox(height: 6.0),
|
||||
Text(
|
||||
"Ha friss támogató vagy, 5-10 percbe telhet az aktiválás. Kérlek gyere vissza később, és próbáld újra!",
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
),
|
||||
@@ -117,7 +124,8 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14.0)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
@@ -125,15 +133,20 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
|
||||
children: [
|
||||
const Text(
|
||||
"Ha bejelentkezés után nem lép vissza az alkalmazásba automatikusan, aktiváld a támogatásod manuálisan",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w500),
|
||||
style:
|
||||
TextStyle(fontSize: 15.0, fontWeight: FontWeight.w500),
|
||||
),
|
||||
const SizedBox(height: 6.0),
|
||||
Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: onManualActivation,
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStatePropertyAll(Theme.of(context).colorScheme.secondary),
|
||||
overlayColor: MaterialStatePropertyAll(Theme.of(context).colorScheme.secondary.withOpacity(.1)),
|
||||
foregroundColor: MaterialStatePropertyAll(
|
||||
Theme.of(context).colorScheme.secondary),
|
||||
overlayColor: MaterialStatePropertyAll(Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary
|
||||
.withOpacity(.1)),
|
||||
),
|
||||
icon: manualActivationLoading
|
||||
? const SizedBox(
|
||||
@@ -164,8 +177,10 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStatePropertyAll(AppColors.of(context).text),
|
||||
overlayColor: MaterialStatePropertyAll(AppColors.of(context).text.withOpacity(.1)),
|
||||
foregroundColor:
|
||||
MaterialStatePropertyAll(AppColors.of(context).text),
|
||||
overlayColor: MaterialStatePropertyAll(
|
||||
AppColors.of(context).text.withOpacity(.1)),
|
||||
),
|
||||
icon: const Icon(FeatherIcons.arrowLeft, size: 20.0),
|
||||
label: const Text(
|
||||
|
||||
@@ -22,7 +22,8 @@ import 'package:provider/provider.dart';
|
||||
import 'modify_subject_names.i18n.dart';
|
||||
|
||||
class MenuRenamedSubjects extends StatelessWidget {
|
||||
const MenuRenamedSubjects({Key? key, required this.settings}) : super(key: key);
|
||||
const MenuRenamedSubjects({Key? key, required this.settings})
|
||||
: super(key: key);
|
||||
|
||||
final SettingsProvider settings;
|
||||
|
||||
@@ -31,8 +32,10 @@ class MenuRenamedSubjects extends StatelessWidget {
|
||||
return PanelButton(
|
||||
padding: const EdgeInsets.only(left: 14.0),
|
||||
onPressed: () {
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false).hasScope(PremiumScopes.renameSubjects)) {
|
||||
PremiumLockedFeatureUpsell.show(context: context, feature: PremiumFeature.subjectrename);
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||
.hasScope(PremiumScopes.renameSubjects)) {
|
||||
PremiumLockedFeatureUpsell.show(
|
||||
context: context, feature: PremiumFeature.subjectrename);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -42,23 +45,32 @@ class MenuRenamedSubjects extends StatelessWidget {
|
||||
},
|
||||
title: Text(
|
||||
"rename_subjects".i18n,
|
||||
style: TextStyle(color: AppColors.of(context).text.withOpacity(settings.renamedSubjectsEnabled ? 1.0 : .5)),
|
||||
style: TextStyle(
|
||||
color: AppColors.of(context)
|
||||
.text
|
||||
.withOpacity(settings.renamedSubjectsEnabled ? 1.0 : .5)),
|
||||
),
|
||||
leading: settings.renamedSubjectsEnabled
|
||||
? const Icon(FeatherIcons.penTool)
|
||||
: Icon(FeatherIcons.penTool, color: AppColors.of(context).text.withOpacity(.25)),
|
||||
: Icon(FeatherIcons.penTool,
|
||||
color: AppColors.of(context).text.withOpacity(.25)),
|
||||
trailingDivider: true,
|
||||
trailing: Switch(
|
||||
onChanged: (v) async {
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false).hasScope(PremiumScopes.renameSubjects)) {
|
||||
PremiumLockedFeatureUpsell.show(context: context, feature: PremiumFeature.subjectrename);
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||
.hasScope(PremiumScopes.renameSubjects)) {
|
||||
PremiumLockedFeatureUpsell.show(
|
||||
context: context, feature: PremiumFeature.subjectrename);
|
||||
return;
|
||||
}
|
||||
|
||||
settings.update(renamedSubjectsEnabled: v);
|
||||
await Provider.of<GradeProvider>(context, listen: false).convertBySettings();
|
||||
await Provider.of<TimetableProvider>(context, listen: false).convertBySettings();
|
||||
await Provider.of<AbsenceProvider>(context, listen: false).convertBySettings();
|
||||
await Provider.of<GradeProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<TimetableProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<AbsenceProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
},
|
||||
value: settings.renamedSubjectsEnabled,
|
||||
activeColor: Theme.of(context).colorScheme.secondary,
|
||||
@@ -82,11 +94,16 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
|
||||
late List<Subject> subjects;
|
||||
late UserProvider user;
|
||||
late DatabaseProvider dbProvider;
|
||||
late SettingsProvider settings;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
subjects = Provider.of<GradeProvider>(context, listen: false).grades.map((e) => e.subject).toSet().toList()
|
||||
subjects = Provider.of<GradeProvider>(context, listen: false)
|
||||
.grades
|
||||
.map((e) => e.subject)
|
||||
.toSet()
|
||||
.toList()
|
||||
..sort((a, b) => a.name.compareTo(b.name));
|
||||
user = Provider.of<UserProvider>(context, listen: false);
|
||||
dbProvider = Provider.of<DatabaseProvider>(context, listen: false);
|
||||
@@ -101,7 +118,8 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(builder: (context, setS) {
|
||||
return AlertDialog(
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(14.0))),
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14.0))),
|
||||
title: Text("rename_subject".i18n),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -161,13 +179,18 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
|
||||
border: Border.all(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 8.0),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12.0, horizontal: 8.0),
|
||||
child: Text(
|
||||
selectedSubjectId == null ? "select_subject".i18n : subjects.firstWhere((element) => element.id == selectedSubjectId).name,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall!
|
||||
.copyWith(fontWeight: FontWeight.w700, color: AppColors.of(context).text.withOpacity(0.75)),
|
||||
selectedSubjectId == null
|
||||
? "select_subject".i18n
|
||||
: subjects
|
||||
.firstWhere(
|
||||
(element) => element.id == selectedSubjectId)
|
||||
.name,
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.of(context).text.withOpacity(0.75)),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
@@ -182,11 +205,13 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
|
||||
controller: _subjectName,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: Colors.grey, width: 1.5),
|
||||
borderSide:
|
||||
const BorderSide(color: Colors.grey, width: 1.5),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: Colors.grey, width: 1.5),
|
||||
borderSide:
|
||||
const BorderSide(color: Colors.grey, width: 1.5),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
@@ -226,10 +251,14 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
|
||||
final renamedSubs = await fetchRenamedSubjects();
|
||||
|
||||
renamedSubs[selectedSubjectId!] = _subjectName.text;
|
||||
await dbProvider.userStore.storeRenamedSubjects(renamedSubs, userId: user.id!);
|
||||
await Provider.of<GradeProvider>(context, listen: false).convertBySettings();
|
||||
await Provider.of<TimetableProvider>(context, listen: false).convertBySettings();
|
||||
await Provider.of<AbsenceProvider>(context, listen: false).convertBySettings();
|
||||
await dbProvider.userStore
|
||||
.storeRenamedSubjects(renamedSubs, userId: user.id!);
|
||||
await Provider.of<GradeProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<TimetableProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<AbsenceProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
}
|
||||
Navigator.of(context).pop(true);
|
||||
setState(() {});
|
||||
@@ -246,6 +275,7 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
settings = Provider.of<SettingsProvider>(context);
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
appBar: AppBar(
|
||||
@@ -262,6 +292,15 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Panel(
|
||||
child: SwitchListTile(
|
||||
title: Text("italics_toggle".i18n),
|
||||
onChanged: (value) => settings.update(renamedSubjectsItalics: value),
|
||||
value: settings.renamedSubjectsItalics,),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
InkWell(
|
||||
onTap: showRenameDialog,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
@@ -271,7 +310,8 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
|
||||
border: Border.all(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 18.0, horizontal: 12.0),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 18.0, horizontal: 12.0),
|
||||
child: Center(
|
||||
child: Text(
|
||||
"rename_new_subject".i18n,
|
||||
@@ -290,14 +330,17 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
|
||||
FutureBuilder<Map<String, String>>(
|
||||
future: fetchRenamedSubjects(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData || snapshot.data!.isEmpty) return Container();
|
||||
if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return Panel(
|
||||
title: Text("renamed_subjects".i18n),
|
||||
child: Column(
|
||||
children: snapshot.data!.keys.map(
|
||||
(key) {
|
||||
Subject? subject = subjects.firstWhere((element) => key == element.id);
|
||||
Subject? subject = subjects
|
||||
.firstWhere((element) => key == element.id);
|
||||
String renameTo = snapshot.data![key]!;
|
||||
return RenamedSubjectItem(
|
||||
subject: subject,
|
||||
@@ -311,9 +354,12 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
|
||||
},
|
||||
removeCallback: () {
|
||||
setState(() {
|
||||
Map<String, String> subs = Map.from(snapshot.data!);
|
||||
Map<String, String> subs =
|
||||
Map.from(snapshot.data!);
|
||||
subs.remove(key);
|
||||
dbProvider.userStore.storeRenamedSubjects(subs, userId: user.id!);
|
||||
dbProvider.userStore.storeRenamedSubjects(
|
||||
subs,
|
||||
userId: user.id!);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -349,11 +395,14 @@ class RenamedSubjectItem extends StatelessWidget {
|
||||
return ListTile(
|
||||
minLeadingWidth: 32.0,
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0),
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onTap: () {},
|
||||
leading: Icon(SubjectIcon.resolveVariant(subject: subject, context: context), color: AppColors.of(context).text.withOpacity(.75)),
|
||||
leading: Icon(
|
||||
SubjectIcon.resolveVariant(subject: subject, context: context),
|
||||
color: AppColors.of(context).text.withOpacity(.75)),
|
||||
title: InkWell(
|
||||
onTap: modifyCallback,
|
||||
child: Column(
|
||||
@@ -361,7 +410,10 @@ class RenamedSubjectItem extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
subject.name.capital(),
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 14, color: AppColors.of(context).text.withOpacity(.75)),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
color: AppColors.of(context).text.withOpacity(.75)),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -376,7 +428,8 @@ class RenamedSubjectItem extends StatelessWidget {
|
||||
),
|
||||
trailing: InkWell(
|
||||
onTap: removeCallback,
|
||||
child: Icon(FeatherIcons.trash, color: AppColors.of(context).red.withOpacity(.75)),
|
||||
child: Icon(FeatherIcons.trash,
|
||||
color: AppColors.of(context).red.withOpacity(.75)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ extension SettingsLocalization on String {
|
||||
"cancel": "Cancel",
|
||||
"done": "Done",
|
||||
"rename_new_subject": "Rename New Subject",
|
||||
"italics_toggle": "Toggle Italics",
|
||||
},
|
||||
"hu_hu": {
|
||||
"renamed_subjects": "Átnevezett Tantárgyaid",
|
||||
@@ -24,6 +25,7 @@ extension SettingsLocalization on String {
|
||||
"cancel": "Mégse",
|
||||
"done": "Kész",
|
||||
"rename_new_subject": "Új Tantárgy átnevezése",
|
||||
"italics_toggle": "Dőlt betűs megjelenítés",
|
||||
},
|
||||
"de_de": {
|
||||
"renamed_subjects": "Umbenannte Fächer",
|
||||
@@ -35,6 +37,7 @@ extension SettingsLocalization on String {
|
||||
"cancel": "Abbrechen",
|
||||
"done": "Erledigt",
|
||||
"rename_new_subject": "Neues Fach umbenennen",
|
||||
"italics_toggle": "Kursivschrift umschalten",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -9,11 +9,15 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.i18n.dart';
|
||||
|
||||
class PremiumFSTimetableButton extends StatelessWidget {
|
||||
const PremiumFSTimetableButton({Key? key, required this.controller}) : super(key: key);
|
||||
const PremiumFSTimetableButton(
|
||||
{Key? key, required this.controller, required this.tabcontroller})
|
||||
: super(key: key);
|
||||
|
||||
final TimetableController controller;
|
||||
final TabController tabcontroller;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -22,19 +26,32 @@ class PremiumFSTimetableButton extends StatelessWidget {
|
||||
child: IconButton(
|
||||
splashRadius: 24.0,
|
||||
onPressed: () {
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false).hasScope(PremiumScopes.fsTimetable)) {
|
||||
PremiumLockedFeatureUpsell.show(context: context, feature: PremiumFeature.weeklytimetable);
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||
.hasScope(PremiumScopes.fsTimetable)) {
|
||||
PremiumLockedFeatureUpsell.show(
|
||||
context: context, feature: PremiumFeature.weeklytimetable);
|
||||
return;
|
||||
}
|
||||
|
||||
// If timetable empty, show empty
|
||||
if (tabcontroller.length == 0) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text("empty_timetable".i18n),
|
||||
duration: const Duration(seconds: 2),
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.of(context, rootNavigator: true)
|
||||
.push(PageRouteBuilder(
|
||||
pageBuilder: (context, animation, secondaryAnimation) => PremiumFSTimetable(
|
||||
pageBuilder: (context, animation, secondaryAnimation) =>
|
||||
PremiumFSTimetable(
|
||||
controller: controller,
|
||||
),
|
||||
))
|
||||
.then((_) {
|
||||
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
SystemChrome.setPreferredOrientations(
|
||||
[DeviceOrientation.portraitUp]);
|
||||
setSystemChrome(context);
|
||||
});
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user