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!
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/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?
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](https://docs.flutter.dev/get-started/install)
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 [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.
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!**

View File

@@ -5,7 +5,7 @@
#### 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
@@ -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)

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",
);
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;

View File

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

View File

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

View File

@@ -54,6 +54,12 @@ Future loginApi({
"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)
.postAPI(KretaAPI.login,
headers: headers,

View File

@@ -40,6 +40,8 @@ class LiveCardProvider extends ChangeNotifier {
String? _latestActivityId;
Map<String, String> _lastActivity = {};
bool _hasCheckedTimetable = false;
LiveCardProvider({
required TimetableProvider timetable,
required SettingsProvider settings,
@@ -198,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);
}
@@ -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;
} else if (nextLesson != null && prevLesson != null) {
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"],
lastAccountId: map["last_account_id"],
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
publish_to: "none"
version: 4.1.0+213
version: 4.1.1+215
environment:
sdk: ">=2.17.0 <3.0.0"
@@ -65,6 +65,7 @@ dependencies:
background_fetch: ^1.1.5
flutter_local_notifications: ^14.1.0
package_info_plus: ^4.0.2
screenshot: ^2.1.0
dev_dependencies:
flutter_lints: ^2.0.1

View File

@@ -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,

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:
child = LiveCardWidget(
key: const Key('livecard.summary'),
title: '',
title: 'Vége a tanévnek! 🥳',
icon: FeatherIcons.arrowRight,
description: const Text(''),
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => const SummaryScreen(
currentPage: 'grades',
),
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:

View File

@@ -81,22 +81,35 @@ class _LiveCardWidgetState extends State<LiveCardWidget> {
child: widget.isEvent
? Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
widget.title ?? 'Esemény',
style: TextStyle(
fontSize: 32,
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.spaceAround,
mainAxisAlignment: MainAxisAlignment.center,
children: [
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(
height: 15,
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);
@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 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);
@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 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_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);
}
}

View File

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

View File

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

View File

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