Compare commits

...

32 Commits

Author SHA1 Message Date
kima
9d9f99a955 fixed long subject names in grades summary 2023-06-25 14:55:01 +02:00
kima
2c5939fab4 fixed misaligned text on final sum page 2023-06-25 14:50:54 +02:00
kima
95ed503e53 fixed 0 min delay thingies 2023-06-25 14:47:06 +02:00
kima
51e2c63134 fixed navigation and ui bugs in summary 2023-06-25 14:36:18 +02:00
kima
50bc03f403 updated version string 2023-06-25 00:11:44 +02:00
kima
f5bc16ba42 created translation for personality card 2023-06-24 23:58:06 +02:00
kima
f4501ce251 finished translation for summary 2023-06-24 21:41:18 +02:00
kima
3317472773 fixed end-year average 2023-06-23 11:20:18 +02:00
kima
acdd47a49a fixed style bug and added reveal check to buttons 2023-06-23 01:17:26 +02:00
kima
fa8be89aaf fixed some issues and added gallery save feature 2023-06-23 01:06:48 +02:00
kima
5034af2fb4 finished animations and started reveal/share 2023-06-23 00:54:56 +02:00
kima
87842de421 finished personality card style 2023-06-22 23:33:28 +02:00
kima
1f46a0509f fixed summary crash error 2023-06-22 21:55:57 +02:00
kima
8e9713e436 almost finished personality page 2023-06-22 21:12:34 +02:00
kima
fe0a7d81ae finished personalities map 2023-06-22 20:52:45 +02:00
kima
f9e14349b7 Merge branch 'master' of github.com:refilc/naplo 2023-06-22 19:44:09 +02:00
kima
a1f087758f started summary personality page 2023-06-22 19:15:02 +02:00
kima
4068abdb95 finished final summary style and fixed thingies 2023-06-22 16:45:57 +02:00
kima
9314c613bc added lessons/misses page and final summary page 2023-06-22 15:59:57 +02:00
Márton Kiss
458e93e19f Merge pull request #25 from Monke14/master
fix italics toggle bugs
2023-06-20 17:44:42 +02:00
hihihaha
ba8d26d250 fix italics toggle bugs 2023-06-20 16:19:49 +02:00
Márton Kiss
33e3495d9f Merge pull request #23 from Monke14/master
fix high network usage
2023-06-20 10:28:27 +02:00
Márton Kiss
f71e72e443 Merge pull request #24 from PredatorPotatoX/master
Contribution guide update
2023-06-20 10:26:28 +02:00
hihihaha
c615a33bd2 make variable private 2023-06-19 22:34:53 +02:00
PredatorPotatoX
7ad0ea26e2 Contributing guide update 2023-06-19 21:04:32 +02:00
hihihaha
0ad663beb3 fix network activity 2023-06-18 17:36:01 +02:00
kima
1366984c15 added start page to summary 2023-06-17 21:30:48 +02:00
kima
62d3895373 added next page button to summary 2023-06-17 20:41:30 +02:00
kima
3579c4e821 fixed grades page ui in summary 2023-06-17 20:04:11 +02:00
Kima
5c39865d40 commit 2023-06-17 16:37:24 +02:00
Kima
551b2849fe grade page done in summary 2023-06-16 01:30:17 +02:00
Márton Kiss
ce1c5eb0d8 fixed nonce login error 2023-06-15 09:33:36 +02:00
28 changed files with 2179 additions and 93 deletions

View File

@@ -1,39 +1,36 @@
# Hozzájárulási útmutató # Contribution guide
Köszönjük, ha programozással segíted a munkánkat! 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. 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 nem csak kicsit tudsz programozni, és ha ismered a Gitet és a GitHubot ;) 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? ## 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/naplo/issues) oldalról szemezgetsz, **ha lehet, a [help wanted taggel megjelöltekkel kezdd](https://github.com/refilc/naplo/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22),** vagy ha új vagy a Flutterhez, 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 é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? ## 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. #### [Segítség a Flutter telepítéséhez](https://docs.flutter.dev/get-started/install)
Ha még nem használtál Fluttert, mindenképp böngészd át a [YouTube csatornájukat](https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw). **Használd a Flutter stable verzióját!** Írd be a terminálba: `flutter channel stable`
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 és a forráskód futtatásához](https://docs.flutter.dev/get-started/install) 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.
Fontos: **Legyél a flutter beta verzióján!** Írd be: `flutter channel beta`
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.
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. 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.
Készíts egy forkot a saját 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 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.
--- ---
Az általad fejlesztett funkciók mellé a changelogban odakerül GitHub felhasználóneved. 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, Discordon megkapod a `DEV` rangot. Ha jelentős és rendszeres hozzájáruló vagy, Discord-on megkaphatod a `DEV` rangot.
Ha bárhol elakadtál, keress minket Discordon. Ha bárhol elakadtál vagy kérdésed van, keress bátran Discordon!
Jó fejlesztést kívánunk! **Jó fejlesztést kívánunk!**

View File

@@ -5,7 +5,7 @@
#### Nem hivatalos e-napló alkalmazás az eKRÉTA rendszerhez - tanulóktól, tanulóknak. #### Nem hivatalos e-napló alkalmazás az eKRÉTA rendszerhez - tanulóktól, tanulóknak.
[![Downloads](https://img.shields.io/github/downloads-pre/refilc/naplo/total?color=%23&label=Downloads&logo=github&sort=semver)](https://github.com/refilc/naplo/releases)   [![discord](https://img.shields.io/discord/1111649116020285532?label=Discord)](http://dc.refilc.hu) [![Downloads](https://img.shields.io/github/downloads-pre/refilc/naplo/total?&logo=github&label=Downloads)](https://github.com/refilc/naplo/releases)   [![Discord](https://img.shields.io/discord/1111649116020285532?logo=discord&label=Discord)](https://dc.refilc.hu)
## Setup ## Setup
@@ -29,11 +29,13 @@ flutter run
### Contribution ### 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. 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) **annon:** a Filc napló készítője (ez az app a Filcen alapul)

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View 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

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

View File

@@ -488,7 +488,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 3.6.0; MARKETING_VERSION = 3.6.0;
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo; PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -526,7 +526,7 @@
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo.livecardpro; PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@@ -567,7 +567,7 @@
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo.livecardpro; PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@@ -607,7 +607,7 @@
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo.livecardpro; PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@@ -746,7 +746,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 3.6.0; MARKETING_VERSION = 3.6.0;
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo; PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -774,7 +774,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 3.6.0; MARKETING_VERSION = 3.6.0;
PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo; PRODUCT_BUNDLE_IDENTIFIER = com.refilctest.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;

View File

@@ -2,8 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.security.application-groups</key> <key>com.apple.security.application-groups</key>
<array> <array>
<string>group.refilcnaplo.livecard</string> <string>group.refilcnaplo.livecard</string>

View File

@@ -7,7 +7,5 @@
<key>NSExtensionPointIdentifier</key> <key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string> <string>com.apple.widgetkit-extension</string>
</dict> </dict>
<key>NSSupportsLiveActivities</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@@ -49,10 +49,16 @@ Future loginApi({
}) async { }) async {
Provider.of<KretaClient>(context, listen: false).userAgent = Provider.of<KretaClient>(context, listen: false).userAgent =
Provider.of<SettingsProvider>(context, listen: false).config.userAgent; Provider.of<SettingsProvider>(context, listen: false).config.userAgent;
Map<String, String> headers = { Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded", "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, instituteCode);
headers.addAll(nonce.header());
Map? res = await Provider.of<KretaClient>(context, listen: false) Map? res = await Provider.of<KretaClient>(context, listen: false)
.postAPI(KretaAPI.login, .postAPI(KretaAPI.login,

View File

@@ -40,6 +40,8 @@ class LiveCardProvider extends ChangeNotifier {
String? _latestActivityId; String? _latestActivityId;
Map<String, String> _lastActivity = {}; Map<String, String> _lastActivity = {};
bool _hasCheckedTimetable = false;
LiveCardProvider({ LiveCardProvider({
required TimetableProvider timetable, required TimetableProvider timetable,
required SettingsProvider settings, required SettingsProvider settings,
@@ -198,7 +200,8 @@ class LiveCardProvider extends ChangeNotifier {
List<Lesson> today = _today(_timetable); List<Lesson> today = _today(_timetable);
if (today.isEmpty) { if (today.isEmpty && !_hasCheckedTimetable) {
_hasCheckedTimetable = true;
await _timetable.fetch(week: Week.current()); await _timetable.fetch(week: Week.current());
today = _today(_timetable); today = _today(_timetable);
} }
@@ -252,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; currentState = LiveCardState.duringLesson;
} else if (nextLesson != null && prevLesson != null) { } else if (nextLesson != null && prevLesson != null) {
currentState = LiveCardState.duringBreak; currentState = LiveCardState.duringBreak;

View 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
}

View File

@@ -194,7 +194,7 @@ class SettingsProvider extends ChangeNotifier {
premiumLogin: map["premium_login"], premiumLogin: map["premium_login"],
lastAccountId: map["last_account_id"], lastAccountId: map["last_account_id"],
renameSubjectsEnabled: map["renamed_subjects_enabled"] == 1, renameSubjectsEnabled: map["renamed_subjects_enabled"] == 1,
renameSubjectsItalics: map["renamed_subjects_italics"] == 0, renameSubjectsItalics: map["renamed_subjects_italics"] == 1,
); );
} }

View File

@@ -3,7 +3,7 @@ description: "Nem hivatalos e-napló alkalmazás az e-Kréta rendszerhez"
homepage: https://refilc.hu homepage: https://refilc.hu
publish_to: "none" publish_to: "none"
version: 4.1.0+213 version: 4.1.1+215
environment: environment:
sdk: ">=2.17.0 <3.0.0" sdk: ">=2.17.0 <3.0.0"
@@ -65,6 +65,7 @@ dependencies:
background_fetch: ^1.1.5 background_fetch: ^1.1.5
flutter_local_notifications: ^14.1.0 flutter_local_notifications: ^14.1.0
package_info_plus: ^4.0.2 package_info_plus: ^4.0.2
screenshot: ^2.1.0
dev_dependencies: dev_dependencies:
flutter_lints: ^2.0.1 flutter_lints: ^2.0.1

View File

@@ -23,7 +23,6 @@ class Empty extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// make the face randomness a bit more constant (to avoid strokes)
int index = Random(DateTime.now().minute).nextInt(faces.length); int index = Random(DateTime.now().minute).nextInt(faces.length);
return Center( return Center(
@@ -32,9 +31,19 @@ class Empty extends StatelessWidget {
child: Text.rich( child: Text.rich(
TextSpan( TextSpan(
text: faces[index], 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 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, textAlign: TextAlign.center,

View File

@@ -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),
),
),
),
),
),
),
);
}
}

View File

@@ -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(),
),
),
),
);
}
}

View File

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

View File

@@ -56,16 +56,43 @@ class _LiveCardState extends State<LiveCard> {
case LiveCardState.summary: case LiveCardState.summary:
child = LiveCardWidget( child = LiveCardWidget(
key: const Key('livecard.summary'), key: const Key('livecard.summary'),
title: '', title: 'Vége a tanévnek! 🥳',
icon: FeatherIcons.arrowRight, icon: FeatherIcons.arrowRight,
description: const Text(''), description: Text(
onTap: () => Navigator.of(context).push( 'Irány az összefoglaláshoz',
MaterialPageRoute( style: TextStyle(
builder: (BuildContext context) => const SummaryScreen( fontWeight: FontWeight.w500,
currentPage: 'grades', 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; break;
case LiveCardState.morning: case LiveCardState.morning:

View File

@@ -81,22 +81,35 @@ class _LiveCardWidgetState extends State<LiveCardWidget> {
child: widget.isEvent child: widget.isEvent
? Column( ? Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Text(
widget.title ?? 'Esemény', widget.title ?? 'Esemény',
style: TextStyle( style: TextStyle(
fontSize: 32, fontWeight: FontWeight.bold,
fontSize: 24.0,
color:
Theme.of(context).textTheme.bodyMedium?.color,
fontStyle: fontStyle:
widget.titleItalic ? FontStyle.italic : null, widget.titleItalic ? FontStyle.italic : null,
), ),
), ),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
widget.description ?? widget.description ??
const Text('Nincs leírás megadva.'), Text(
'Nincs leírás megadva.',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18.0,
color: Theme.of(context)
.textTheme
.bodyMedium
?.color,
),
),
SizedBox( SizedBox(
height: 15, height: 15,
child: Container( child: Container(

View 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,
),
),
],
);
}
}

View File

@@ -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); 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const 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,
),
),
),
],
),
),
],
),
);
} }
} }

View 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,
),
);
}
}

View File

@@ -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); 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const 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),
],
),
);
} }
} }

View File

@@ -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),
],
);
}
}

View File

@@ -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/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/grades_page.dart';
import 'pages/lessons_page.dart';
import 'pages/personality_page.dart';
class SummaryScreen extends StatefulWidget { class SummaryScreen extends StatefulWidget {
final String currentPage; final String currentPage;
final bool isBottomSheet;
const SummaryScreen({Key? key, this.currentPage = 'personality'}) const SummaryScreen({
: super(key: key); Key? key,
this.currentPage = 'personality',
this.isBottomSheet = false,
}) : super(key: key);
@override @override
_SummaryScreenState createState() => _SummaryScreenState(); _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( final LinearGradient _backgroundGradient = const LinearGradient(
colors: [ colors: [
Color(0xff1d56ac), Color(0xff1d56ac),
@@ -22,34 +53,170 @@ class _SummaryScreenState extends State<SummaryScreen> {
stops: [-1.0, 1.0], stops: [-1.0, 1.0],
); );
@override
void initState() {
super.initState();
}
@override
void dispose() {
_confettiController?.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( user = Provider.of<UserProvider>(context);
body: Container( 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), decoration: BoxDecoration(gradient: _backgroundGradient),
child: Container( width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(gradient: _backgroundGradient), height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width, child: SafeArea(
height: MediaQuery.of(context).size.height, child: Padding(
child: SafeArea( padding: const EdgeInsets.only(
child: Padding( left: 24.0,
padding: EdgeInsets.only( right: 24.0,
left: 24.0, top: 15.0,
right: 24.0, bottom: 40.0,
top: 26.0 + MediaQuery.of(context).padding.top, ),
bottom: 52.0, child: Column(
), crossAxisAlignment: widget.currentPage == 'start'
child: widget.currentPage == 'grades' ? CrossAxisAlignment.center
? const GradesBody() : CrossAxisAlignment.start,
: widget.currentPage == 'lessons' mainAxisAlignment: widget.currentPage == 'start'
? const GradesBody() ? MainAxisAlignment.spaceBetween
: widget.currentPage == 'allsum' : MainAxisAlignment.start,
? const GradesBody() children: [
: const GradesBody(), 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);
}
} }

View File

@@ -3,9 +3,111 @@ import 'package:i18n_extension/i18n_extension.dart';
extension SettingsLocalization on String { extension SettingsLocalization on String {
static final _t = Translations.byLocale("hu_hu") + static final _t = Translations.byLocale("hu_hu") +
{ {
"en_en": {}, "en_en": {
"hu_hu": {}, // main thingies
"de_de": {}, "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); String get i18n => localize(this, _t);

View File

@@ -40,6 +40,9 @@ dependencies:
background_fetch: ^1.1.5 background_fetch: ^1.1.5
wtf_sliding_sheet: ^1.0.0 wtf_sliding_sheet: ^1.0.0
package_info_plus: ^4.0.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: dev_dependencies:
flutter_lints: ^1.0.0 flutter_lints: ^1.0.0

View File

@@ -293,13 +293,10 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Panel( Panel(
child: PanelButton( child: SwitchListTile(
title: Text("italics_toggle".i18n), title: Text("italics_toggle".i18n),
trailing: Switch( onChanged: (value) => settings.update(renamedSubjectsItalics: value),
value: settings.renamedSubjectsItalics, value: settings.renamedSubjectsItalics,),
onChanged: (value) =>
settings.update(renamedSubjectsItalics: value),
)),
), ),
const SizedBox( const SizedBox(
height: 20, height: 20,