Compare commits

...

93 Commits

Author SHA1 Message Date
Márton Kiss
d929c804df Merge pull request #21 from TMarccci/master
LiveActivity
2023-06-14 23:53:17 +02:00
Tihanyi Marcell
19c128eecd LiveActivity 2023-06-14 23:46:21 +02:00
kima
57cf764804 updated version string 2023-06-14 23:08:54 +02:00
kima
ea812e0b67 fixed project problems 2023-06-13 23:37:35 +02:00
kima
1c517a99f2 fixed project problems 2023-06-13 23:37:25 +02:00
kima
2687cb146b gitignore changes 2023-06-13 23:35:11 +02:00
kima
1a9080dcc2 added event functionality and summary to livecard 2023-06-13 23:27:40 +02:00
Márton Kiss
580c92b13d Merge pull request #20 from TMarccci/master
On empty timetable block fullscreen mode.
2023-06-13 21:10:04 +02:00
Tihanyi Marcell
ae4c4aa89c Merge branch 'refilc:master' into master 2023-06-12 21:54:47 +02:00
Tihanyi Marcell
ded7c51f44 Block empty fullscreen timetable 2023-06-12 21:54:13 +02:00
Márton Kiss
485e85ddaa Merge pull request #19 from TMarccci/master
Fixes
2023-06-12 20:26:48 +02:00
Márton Kiss
3c0082a786 Merge pull request #18 from CroatianHusky/master
Developer Settings i18n + Notifications toggle color fix
2023-06-12 20:25:28 +02:00
Tihanyi Marcell
c474512088 Live Activity version update, build-ipa.sh 2023-06-12 20:11:15 +02:00
Tihanyi Marcell
f78a542be2 iOS notification permission fix 2023-06-12 20:09:48 +02:00
Tihanyi Marcell
131454b99d Notification Capabilitie 2023-06-12 20:09:27 +02:00
Tihanyi Marcell
e255182b93 Removed " " from login 2023-06-12 20:09:11 +02:00
Tihanyi Marcell
3857896d6c Ghost Grade title overflow fix 2023-06-12 20:08:53 +02:00
CroatianHusky
301e8cb638 Updated settings_screen.i18n.dart 2023-06-12 17:35:13 +02:00
CroatianHusky
ae7c724f65 notification toggle color fix 2023-06-12 17:33:00 +02:00
CroatianHusky
751cd04ce2 developer settings i18n 2023-06-12 17:14:42 +02:00
Márton Kiss
a88ccfa3fc Merge pull request #15 from Monke14/notifications
Értesítések
2023-06-10 22:47:03 +02:00
Márton Kiss
9e914974b7 Merge branch 'master' into notifications 2023-06-10 22:46:40 +02:00
Kima
9cfa8296b8 fixed some bugs 2023-06-10 22:38:01 +02:00
hihihaha
27ef942723 fix headless task 2023-06-10 21:46:53 +02:00
hihihaha
fe03554fbf backend changes 2023-06-10 21:19:49 +02:00
hihihaha
8c2227df73 update design 2023-06-10 21:03:01 +02:00
hihihaha
0274c2f070 start on boot 2023-06-10 20:43:03 +02:00
hihihaha
8f85c6a33b add timeout safety to headless task 2023-06-10 20:41:31 +02:00
hihihaha
cf81ca8207 add annotation to function 2023-06-10 20:39:06 +02:00
hihihaha
07bbafe7dd add toggle for notifications in settings 2023-06-10 20:34:01 +02:00
hihihaha
3eee2c7a55 set grade as seen 2023-06-10 19:55:40 +02:00
hihihaha
1cdde3b6ce add notification 2023-06-10 19:50:32 +02:00
Márton Kiss
67aea46c06 Update navigation_screen.dart 2023-06-10 13:59:03 +02:00
Márton Kiss
7adec7dfa5 maybe fixed dark mode bug 2023-06-10 13:57:11 +02:00
Kima
95fa819ed2 updatet things in pubspec 2023-06-10 13:26:28 +02:00
Kima
1e87d344e2 fixed versions in pubspec 2023-06-10 11:25:24 +02:00
Márton Kiss
9223375304 Merge pull request #11 from CroatianHusky/master
"Új jegyek" többesszám fix + verzió "v?" fix
2023-06-10 11:10:04 +02:00
Márton Kiss
bb862f15bd Merge pull request #10 from PredatorPotatoX/master
CONTRIBUTING.md link fix
2023-06-10 11:09:46 +02:00
CroatianHusky
261c94e9bb fixed version counter "v?" 2023-06-10 10:38:26 +02:00
CroatianHusky
06016514e5 new grade plurality fix 2023-06-10 09:32:21 +02:00
CroatianHusky
cead24b65a removed dead filc links from pubspec 2023-06-10 09:29:44 +02:00
PredatorPotatoX
6eaab57468 CONTRIBUTING.md link fix 2023-06-09 23:31:29 +02:00
Márton Kiss
5852fc233d Update AndroidManifest.xml 2023-06-09 21:15:25 +02:00
Márton Kiss
d7e21f6332 Update update_helper.dart 2023-06-09 21:13:38 +02:00
Márton Kiss
c78b8d3b97 Merge pull request #9 from Monke14/bug-fixes
Dőlt betűk toggle
2023-06-09 20:15:31 +02:00
hihihaha
940e3d8ca1 add toggle for italics 2023-06-09 17:29:03 +02:00
Márton Kiss
e31d0753d1 Merge pull request #8 from TMarccci/master
I think I fixed this
2023-06-09 16:55:05 +02:00
Tihanyi Marcell
526c66f358 I think I fixed this 2023-06-09 16:53:28 +02:00
Kima
d6833a952d changed version string 2023-06-09 16:40:53 +02:00
Kima
4760761bb7 Merge branch 'master' of github.com:refilc/naplo 2023-06-09 16:25:34 +02:00
Kima
a33e6dae3b fixed auto-update error bug 2023-06-09 16:25:31 +02:00
Márton Kiss
d393181f10 Update grade_provider.dart 2023-06-09 16:06:17 +02:00
Márton Kiss
12df8b82c7 Merge pull request #7 from TMarccci/master
Handle disabled Live Activity, i18n AverageSelector won't update, ...
2023-06-09 16:02:05 +02:00
Tihanyi Marcell
1c7eba7af3 Appgroup id: not refilc2 2023-06-09 15:44:37 +02:00
Tihanyi Marcell
d39cdaef10 Fixed i18n wont update to AverageSelector 2023-06-09 14:22:15 +02:00
Tihanyi Marcell
cc40fb9c0f Ignore disabled live activity 2023-06-09 11:13:35 +02:00
Tihanyi Marcell
85c6d548ad Ignore null and - i18n 2023-06-09 11:08:00 +02:00
Kima
8dbf605450 added black accent color 2023-06-08 21:28:28 +02:00
Kima
2f418a7c1a updated version string 2023-06-08 21:19:46 +02:00
Kima
5f0c82f54c added grade value translations 2023-06-08 21:18:20 +02:00
Kima
4e659308e5 fixed subject rename at homeworks 2023-06-08 20:38:10 +02:00
Kima
50e24bde17 added disable fading to all filter/tab bars 2023-06-08 20:07:17 +02:00
Márton Kiss
31f7c6a465 Merge pull request #6 from Monke14/bug-fixes
some fixes - by Monke14
2023-06-08 20:01:22 +02:00
Márton Kiss
e734579249 Merge branch 'master' into bug-fixes 2023-06-08 19:59:57 +02:00
Márton Kiss
2cff46d628 Merge pull request #5 from TMarccci/master
fixed lot of things - by TMarccci
2023-06-08 19:56:35 +02:00
Kima
87f3f93177 started creating end-year summary and stb 2023-06-08 19:53:50 +02:00
hihihaha
30733caa4a remove home screen tabbar fade 2023-06-08 16:18:39 +02:00
hihihaha
4e30a550e1 fix chart overflow 2023-06-08 16:18:08 +02:00
hihihaha
cb687d6b10 add themed icon 2023-06-08 16:17:56 +02:00
hihihaha
6c6d3a7cd8 fix english translations 2023-06-08 16:17:46 +02:00
Tihanyi Marcell
da06e400e1 Login add " ", Comment rename 2023-06-08 13:47:40 +02:00
Tihanyi Marcell
87b8cbb60f HotFix 2023-06-08 12:59:09 +02:00
Tihanyi Marcell
8bac300585 goalPlanner remove 2023-06-08 12:54:46 +02:00
Tihanyi Marcell
da7d2b9333 Fixed: Settings UI, Live Act, Class Avg 2023-06-08 12:44:47 +02:00
Kima
e1f84caf19 changed refilc bday strings 2023-06-07 21:27:56 +02:00
Kima
274da2b766 ok 2023-06-07 21:21:35 +02:00
Kima
35aba35801 Merge branch 'master' of github.com:refilc/naplo 2023-06-07 20:50:48 +02:00
Kima
964e002c46 added og filc color to accents 2023-06-07 20:50:45 +02:00
Márton Kiss
d3318e10ab fixed download counter in readme 2023-06-07 20:22:41 +02:00
Kima
4c43369d59 updated version string 2023-06-07 19:22:55 +02:00
Kima
176243b881 started uwu mode 2023-06-06 21:46:10 +02:00
Kima
ed02a340d0 ok 2023-06-06 21:11:52 +02:00
Kima
2877f4fc5c oke mostmar tenyleg fix 😭 vagy pedig mas baja 2023-06-06 21:03:30 +02:00
Kima
fceb3bf31a fixed settings screen version check 2023-06-06 20:58:33 +02:00
Kima
9883d081ff added back button to full-screen timetable 2023-06-06 20:27:37 +02:00
Kima
db5a9fb197 fixed translate bugs and subject name things 2023-06-06 19:18:56 +02:00
Kima
93438ce3df Merge branch 'master' of github.com:refilc/naplo 2023-06-05 21:21:48 +02:00
Kima
95bca64fb8 fixed quick actions colors 2023-06-05 21:21:45 +02:00
Pearoo
7821e10869 Update README.md 2023-06-01 10:58:53 +00:00
Pearoo
cede3c3129 Merge branch 'master' of https://github.com/refilc/naplo 2023-05-31 00:00:25 +02:00
Pearoo
528ee862b9 Update README.md - Squircle ikon 2023-05-30 21:28:25 +00:00
Pearoo
518932c260 Merge branch 'master' of https://github.com/refilc/naplo 2023-05-29 22:10:16 +02:00
Pearoo
281b9cf6c4 Update .gitignore 2023-05-29 13:31:03 +02:00
114 changed files with 2161 additions and 791 deletions

25
.gitignore vendored
View File

@@ -25,3 +25,28 @@ doc/api/
*.js.map
*.txt
filcnaplo/linux/flutter/generated_plugin_registrant.cc
filcnaplo/linux/flutter/generated_plugin_registrant.h
filcnaplo/linux/flutter/generated_plugins.cmake
filcnaplo/macos/Flutter/GeneratedPluginRegistrant.swift
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/connectivity_plus
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/dynamic_color
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/flutter_acrylic
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/share_plus_windows
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/url_launcher_windows
filcnaplo/windows/flutter/ephemeral/generated_config.cmake
filcnaplo/windows/flutter/generated_plugin_registrant.cc
filcnaplo/windows/flutter/generated_plugin_registrant.h
filcnaplo/windows/flutter/generated_plugins.cmake
filcnaplo/linux/flutter/generated_plugin_registrant.cc
filcnaplo/linux/flutter/generated_plugin_registrant.h
filcnaplo/linux/flutter/generated_plugins.cmake
filcnaplo/macos/Flutter/GeneratedPluginRegistrant.swift
filcnaplo/linux/flutter/generated_plugin_registrant.cc
filcnaplo/linux/flutter/generated_plugin_registrant.h
filcnaplo/linux/flutter/generated_plugins.cmake
filcnaplo/macos/Flutter/GeneratedPluginRegistrant.swift
.vscode/
key.properties

60
.idea/naplo.iml generated
View File

@@ -2,7 +2,65 @@
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/example/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_desktop_ui/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_desktop_ui/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_desktop_ui/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/example/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/dynamic_color/example/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_premium/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_premium/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_premium/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/share_plus/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/example/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/connectivity_plus/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_kreta_api/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_kreta_api/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_kreta_api/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_mobile_ui/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_mobile_ui/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo_mobile_ui/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/package_info_plus/example/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/.dart_tool" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>

2
.vscode/launch.json vendored
View File

@@ -27,7 +27,7 @@
"cwd": "filcnaplo",
"request": "launch",
"type": "dart",
"flutterMode": "release"
"flutterMode": "debug"
}
]
}

View File

@@ -1,5 +1,5 @@
# Contributing Guide
# Hozzájárulási útmutató
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 ;)
@@ -9,7 +9,7 @@ Kérünk, **olyan dologgal járulj hozzá** a reFilchez, ami valószínűleg **s
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.
A legjobban annak örülünk, ha az [Issues](https://github.com/refilc/filcnaplo/issues) oldalról szemezgetsz, **ha lehet, a [priority taggel megjelöltekkel kezdd](https://github.com/refilc/filcnaplo/issues?q=is%3Aissue+is%3Aopen+label%3Apriority),** vagy ha új vagy a Flutterhez, ajánljuk figyelmedbe [ezeket a viszonylag könnyen javítható hibákat](https://github.com/refilc/filcnaplo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) (ha épp van ilyen).
A legjobban annak örülünk, ha az [Issues](https://github.com/refilc/naplo/issues) oldalról szemezgetsz, **ha 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).
## Hogyan segítsek?
@@ -19,7 +19,7 @@ A reFilc a Google által pár éve létrehozott **[Fluttert](https://flutter.dev
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 és a forráskód futtatásához](/.github/SETUP.md)
#### [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`
@@ -27,7 +27,7 @@ Fontos: **Legyél a flutter beta verzióján!** Írd be: `flutter channel beta`
Ha nem értesz a Githez, ajánljuk figyelmedbe [ezt a cikket](https://medium.com/envienta-magyarorsz%C3%A1g/git-%C3%A9s-github-gyorstalpal%C3%B3-f2d78a732deb). Viszont arra kérünk, a Git használatát ne a reFilcen próbáld ki először, hozz létre előbb egy saját Repót, és abba tesztelgess. Ha már nagyjából kitapasztaltad, várjuk hozzájárulásodat.
Készíts egy forkot a saját fiókod alá.
A reFilc legfrissebb, épp fejlesztés alatt álló verzióját a [dev brancen](https://github.com/refilc/filcnaplo/tree/dev) találod, kérjük ide commitolj, és ide célozd a forkodból a Pull Requested. Írd le benne, mit változtattál, és ha lehet, csatolj képernyőképet is.
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.
---

View File

@@ -1,11 +1,11 @@
<p align=center>
<img src="https://media.discordapp.net/attachments/1111727410677825596/1111790518964326510/reFilc_Logo2.png?width=671&height=671" width=150>
<img src="https://media.discordapp.net/attachments/1111727410677825596/1113217167513624646/reFilc_Logo_Squircle.png?width=671&height=671" width=150>
<h1 align=center><b>reFilc</b></h1>
</p>
#### 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/latest/total?color=%23&label=Downloads&logo=github&sort=semver)](https://github.com/refilc/naplo/releases) &nbsp; [![discord](https://img.shields.io/discord/1111649116020285532?label=Discord)](http://dc.refilc.hu)
[![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) &nbsp; [![discord](https://img.shields.io/discord/1111649116020285532?label=Discord)](http://dc.refilc.hu)
## Setup
@@ -37,6 +37,6 @@ Az összes (ugyan azon verzióhoz tartozó) contribution meg fog jelenni a relea
**annon:** a Filc napló készítője (ez az app a Filcen alapul)
**kima, chromium, peighter, mog, WolfY:** a fejlesztői csapat
**kima, mog, WolfY:** fejlesztők
**Regő, Pearoo:** weboldal design és community management

View File

@@ -44,3 +44,4 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
key.properties

View File

@@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" package="hu.refilc.naplo">
<application android:label="reFilc" tools:replace="android:label" android:icon="@mipmap/ic_launcher"
<application android:name="${applicationName}" android:label="reFilc" tools:replace="android:label" android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
<activity android:exported="true" android:name=".MainActivity"
android:launchMode="singleTop" android:theme="@style/LaunchTheme"
@@ -62,9 +62,10 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
</manifest>
</manifest>

View File

@@ -1,18 +1,9 @@
package hu.refilc.naplo;
import android.os.Bundle;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.embedding.engine.FlutterEngine;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(new FlutterEngine(this));
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 916 B

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 KiB

View File

@@ -1,3 +1,7 @@
#!/bin/sh
flutter clean
dart pub get
flutter doctor -v
flutter build ipa --release --dart-define=APPVER=$(cat pubspec.yaml | grep version: | cut -d' ' -f2 | cut -d+ -f1) --no-tree-shake-icons

View File

@@ -204,7 +204,7 @@ SPEC CHECKSUMS:
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89
flutter_image_compress: 5a5e9aee05b6553048b8df1c3bc456d0afaac433
@@ -218,14 +218,14 @@ SPEC CHECKSUMS:
live_activities: 9ff56a06a2d43ecd68f56deeed13b18a8304789c
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
quick_actions_ios: 9e80dcfadfbc5d47d9cf8f47bcf428b11cf383d4
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866
SDWebImageWebPCoder: 18503de6621dd2c420d680e33d46bf8e1d5169b0
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a

View File

@@ -2,7 +2,11 @@
<!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/>
<array>
<string>group.refilcnaplo.livecard</string>
</array>
</dict>
</plist>

View File

@@ -1,13 +1,14 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "reFilc_Logo.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 KiB

View File

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

View File

@@ -13,7 +13,7 @@ class LessonData {
var nextRoom: String
init?() {
let sharedDefault = UserDefaults(suiteName: "group.filcnaplo.livecard")!
let sharedDefault = UserDefaults(suiteName: "group.refilc.livecard")!
self.icon = sharedDefault.string(forKey: "icon")!
self.index = sharedDefault.string(forKey: "index")!

View File

@@ -3,6 +3,8 @@
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array/>
<array>
<string>group.refilcnaplo.livecard</string>
</array>
</dict>
</plist>

View File

@@ -68,8 +68,6 @@ struct LockScreenLiveActivityView: View {
.monospacedDigit()
.padding(.trailing, CGFloat(24))
}
.activitySystemActionForegroundColor(.teal)
.activityBackgroundTint(.teal)
}
}

View File

@@ -7,6 +7,7 @@ import 'package:filcnaplo/models/release.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/models/supporter.dart';
import 'package:filcnaplo_kreta_api/models/school.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:connectivity_plus/connectivity_plus.dart';
@@ -68,7 +69,9 @@ class FilcAPI {
http.Response res = await http.get(Uri.parse(config), headers: headers);
if (res.statusCode == 200) {
print(jsonDecode(res.body));
if (kDebugMode) {
print(jsonDecode(res.body));
}
return Config.fromJson(jsonDecode(res.body));
} else if (res.statusCode == 429) {
res = await http.get(Uri.parse(config));

View File

@@ -54,12 +54,6 @@ 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

@@ -2,7 +2,6 @@ import 'dart:io';
import 'package:filcnaplo/database/query.dart';
import 'package:filcnaplo/database/store.dart';
import 'package:sqflite/sqflite.dart';
// ignore: depend_on_referenced_packages
import 'package:sqflite_common_ffi/sqflite_ffi.dart';

View File

@@ -13,7 +13,15 @@ import 'package:flutter/foundation.dart';
import 'package:live_activities/live_activities.dart';
import 'package:filcnaplo_mobile_ui/pages/home/live_card/live_card.i18n.dart';
enum LiveCardState { empty, duringLesson, duringBreak, morning, afternoon, night }
enum LiveCardState {
empty,
duringLesson,
duringBreak,
morning,
afternoon,
night,
summary
}
class LiveCardProvider extends ChangeNotifier {
Lesson? currentLesson;
@@ -37,19 +45,41 @@ class LiveCardProvider extends ChangeNotifier {
required SettingsProvider settings,
}) : _timetable = timetable,
_settings = settings {
_liveActivitiesPlugin.init(appGroupId: "group.filcnaplo.livecard");
_liveActivitiesPlugin.getAllActivitiesIds().then((value) {
_latestActivityId = value.isNotEmpty ? value.first : null;
// Check if live card is enabled .areActivitiesEnabled()
_liveActivitiesPlugin.areActivitiesEnabled().then((value) {
// Console log
if (kDebugMode) {
print("Live card enabled: $value");
}
if (value) {
_liveActivitiesPlugin.init(appGroupId: "group.refilc.livecard");
_liveActivitiesPlugin.getAllActivitiesIds().then((value) {
_latestActivityId = value.isNotEmpty ? value.first : null;
});
}
});
_timer = Timer.periodic(const Duration(seconds: 1), (timer) => update());
_delay = settings.bellDelayEnabled ? Duration(seconds: settings.bellDelay) : Duration.zero;
_delay = settings.bellDelayEnabled
? Duration(seconds: settings.bellDelay)
: Duration.zero;
update();
}
@override
void dispose() {
_timer.cancel();
if (_latestActivityId != null && Platform.isIOS) _liveActivitiesPlugin.endActivity(_latestActivityId!);
if (Platform.isIOS) {
_liveActivitiesPlugin.areActivitiesEnabled().then((value) {
if (value) {
if (_latestActivityId != null) {
_liveActivitiesPlugin.endActivity(_latestActivityId!);
}
}
});
}
super.dispose();
}
@@ -78,14 +108,25 @@ class LiveCardProvider extends ChangeNotifier {
switch (currentState) {
case LiveCardState.duringLesson:
return {
"icon": currentLesson != null ? SubjectIcon.resolveName(subject: currentLesson?.subject) : "book",
"index": currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "",
"title": currentLesson != null ? ShortSubject.resolve(subject: currentLesson?.subject).capital() : "",
"icon": currentLesson != null
? SubjectIcon.resolveName(subject: currentLesson?.subject)
: "book",
"index":
currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "",
"title": currentLesson != null
? ShortSubject.resolve(subject: currentLesson?.subject).capital()
: "",
"subtitle": currentLesson?.room.replaceAll("_", " ") ?? "",
"description": currentLesson?.description ?? "",
"startDate": ((currentLesson?.start.millisecondsSinceEpoch ?? 0) - _delay.inMilliseconds).toString(),
"endDate": ((currentLesson?.end.millisecondsSinceEpoch ?? 0) - _delay.inMilliseconds).toString(),
"nextSubject": nextLesson != null ? ShortSubject.resolve(subject: nextLesson?.subject).capital() : "",
"startDate": ((currentLesson?.start.millisecondsSinceEpoch ?? 0) -
_delay.inMilliseconds)
.toString(),
"endDate": ((currentLesson?.end.millisecondsSinceEpoch ?? 0) -
_delay.inMilliseconds)
.toString(),
"nextSubject": nextLesson != null
? ShortSubject.resolve(subject: nextLesson?.subject).capital()
: "",
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
};
case LiveCardState.duringBreak:
@@ -101,10 +142,19 @@ class LiveCardProvider extends ChangeNotifier {
return {
"icon": iconFloorMap[diff] ?? "cup.and.saucer",
"title": "Szünet",
"description": "go $diff".i18n.fill([diff != "to room" ? (nextLesson!.getFloor() ?? 0) : nextLesson!.room]),
"startDate": ((prevLesson?.end.millisecondsSinceEpoch ?? 0) - _delay.inMilliseconds).toString(),
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) - _delay.inMilliseconds).toString(),
"nextSubject": (nextLesson != null ? ShortSubject.resolve(subject: nextLesson?.subject) : "").capital(),
"description": "go $diff".i18n.fill([
diff != "to room" ? (nextLesson!.getFloor() ?? 0) : nextLesson!.room
]),
"startDate": ((prevLesson?.end.millisecondsSinceEpoch ?? 0) -
_delay.inMilliseconds)
.toString(),
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
_delay.inMilliseconds)
.toString(),
"nextSubject": (nextLesson != null
? ShortSubject.resolve(subject: nextLesson?.subject)
: "")
.capital(),
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
"index": "",
"subtitle": "",
@@ -116,20 +166,34 @@ class LiveCardProvider extends ChangeNotifier {
void update() async {
if (Platform.isIOS) {
final cmap = toMap();
if (!mapEquals(cmap, _lastActivity)) {
_lastActivity = cmap;
if (_lastActivity.isNotEmpty) {
if (_latestActivityId == null) {
_liveActivitiesPlugin.createActivity(_lastActivity).then((value) => _latestActivityId = value);
} else {
_liveActivitiesPlugin.updateActivity(_latestActivityId!, _lastActivity);
_liveActivitiesPlugin.areActivitiesEnabled().then((value) {
if (value) {
final cmap = toMap();
if (!mapEquals(cmap, _lastActivity)) {
_lastActivity = cmap;
try {
if (_lastActivity.isNotEmpty) {
if (_latestActivityId == null) {
_liveActivitiesPlugin
.createActivity(_lastActivity)
.then((value) => _latestActivityId = value);
} else {
_liveActivitiesPlugin.updateActivity(
_latestActivityId!, _lastActivity);
}
} else {
if (_latestActivityId != null) {
_liveActivitiesPlugin.endActivity(_latestActivityId!);
}
}
} catch (e) {
if (kDebugMode) {
print('ERROR: Unable to create or update iOS LiveCard!');
}
}
}
} else {
if (_latestActivityId != null) _liveActivitiesPlugin.endActivity(_latestActivityId!);
}
}
});
}
List<Lesson> today = _today(_timetable);
@@ -139,19 +203,28 @@ class LiveCardProvider extends ChangeNotifier {
today = _today(_timetable);
}
_delay = _settings.bellDelayEnabled ? Duration(seconds: _settings.bellDelay) : Duration.zero;
_delay = _settings.bellDelayEnabled
? Duration(seconds: _settings.bellDelay)
: Duration.zero;
final now = _now().add(_delay);
// Filter cancelled lessons #20
// Filter label lessons #128
today = today.where((lesson) => lesson.status?.name != "Elmaradt" && lesson.subject.id != '' && !lesson.isEmpty).toList();
today = today
.where((lesson) =>
lesson.status?.name != "Elmaradt" &&
lesson.subject.id != '' &&
!lesson.isEmpty)
.toList();
if (today.isNotEmpty) {
// sort
today.sort((a, b) => a.start.compareTo(b.start));
final _lesson = today.firstWhere((l) => l.start.isBefore(now) && l.end.isAfter(now), orElse: () => Lesson.fromJson({}));
final _lesson = today.firstWhere(
(l) => l.start.isBefore(now) && l.end.isAfter(now),
orElse: () => Lesson.fromJson({}));
if (_lesson.start.year != 0) {
currentLesson = _lesson;
@@ -159,7 +232,8 @@ class LiveCardProvider extends ChangeNotifier {
currentLesson = null;
}
final _next = today.firstWhere((l) => l.start.isAfter(now), orElse: () => Lesson.fromJson({}));
final _next = today.firstWhere((l) => l.start.isAfter(now),
orElse: () => Lesson.fromJson({}));
nextLessons = today.where((l) => l.start.isAfter(now)).toList();
if (_next.start.year != 0) {
@@ -168,7 +242,8 @@ class LiveCardProvider extends ChangeNotifier {
nextLesson = null;
}
final _prev = today.lastWhere((l) => l.end.isBefore(now), orElse: () => Lesson.fromJson({}));
final _prev = today.lastWhere((l) => l.end.isBefore(now),
orElse: () => Lesson.fromJson({}));
if (_prev.start.year != 0) {
prevLesson = _prev;
@@ -198,7 +273,10 @@ class LiveCardProvider extends ChangeNotifier {
Duration get delay => _delay;
bool _sameDate(DateTime a, DateTime b) => (a.year == b.year && a.month == b.month && a.day == b.day);
bool _sameDate(DateTime a, DateTime b) =>
(a.year == b.year && a.month == b.month && a.day == b.day);
List<Lesson> _today(TimetableProvider p) => (p.getWeek(Week.current()) ?? []).where((l) => _sameDate(l.date, _now())).toList();
List<Lesson> _today(TimetableProvider p) => (p.getWeek(Week.current()) ?? [])
.where((l) => _sameDate(l.date, _now()))
.toList();
}

View File

@@ -11,6 +11,7 @@ import 'package:filcnaplo/models/config.dart';
import 'package:filcnaplo/theme/observer.dart';
import 'package:filcnaplo/theme/theme.dart';
import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
@@ -22,13 +23,18 @@ import 'package:provider/provider.dart';
import 'package:filcnaplo_mobile_ui/common/system_chrome.dart' as mobile;
import 'package:filcnaplo_mobile_ui/screens/login/login_route.dart' as mobile;
import 'package:filcnaplo_mobile_ui/screens/login/login_screen.dart' as mobile;
import 'package:filcnaplo_mobile_ui/screens/navigation/navigation_screen.dart' as mobile;
import 'package:filcnaplo_mobile_ui/screens/settings/settings_route.dart' as mobile;
import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.dart' as mobile;
import 'package:filcnaplo_mobile_ui/screens/navigation/navigation_screen.dart'
as mobile;
import 'package:filcnaplo_mobile_ui/screens/settings/settings_route.dart'
as mobile;
import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.dart'
as mobile;
// Desktop UI
import 'package:filcnaplo_desktop_ui/screens/navigation/navigation_screen.dart' as desktop;
import 'package:filcnaplo_desktop_ui/screens/login/login_screen.dart' as desktop;
import 'package:filcnaplo_desktop_ui/screens/navigation/navigation_screen.dart'
as desktop;
import 'package:filcnaplo_desktop_ui/screens/login/login_screen.dart'
as desktop;
import 'package:filcnaplo_desktop_ui/screens/login/login_route.dart' as desktop;
// Providers
@@ -36,7 +42,6 @@ import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
import 'package:filcnaplo_kreta_api/providers/event_provider.dart';
import 'package:filcnaplo_kreta_api/providers/exam_provider.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:filcnaplo_kreta_api/providers/homework_provider.dart';
import 'package:filcnaplo_kreta_api/providers/message_provider.dart';
import 'package:filcnaplo_kreta_api/providers/note_provider.dart';
@@ -52,7 +57,12 @@ class App extends StatelessWidget {
final UserProvider user;
final DatabaseProvider database;
const App({Key? key, required this.database, required this.settings, required this.user}) : super(key: key);
const App(
{Key? key,
required this.database,
required this.settings,
required this.user})
: super(key: key);
@override
Widget build(BuildContext context) {
@@ -65,7 +75,8 @@ class App extends StatelessWidget {
final status = StatusProvider();
final kreta = KretaClient(user: user, settings: settings, status: status);
final timetable = TimetableProvider(user: user, database: database, kreta: kreta);
final timetable =
TimetableProvider(user: user, database: database, kreta: kreta);
final premium = PremiumProvider(settings: settings);
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -83,23 +94,44 @@ class App extends StatelessWidget {
ChangeNotifierProvider<StatusProvider>(create: (_) => status),
Provider<KretaClient>(create: (_) => kreta),
Provider<DatabaseProvider>(create: (context) => database),
ChangeNotifierProvider<ThemeModeObserver>(create: (context) => ThemeModeObserver(initialTheme: settings.theme)),
ChangeNotifierProvider<NewsProvider>(create: (context) => NewsProvider(context: context)),
ChangeNotifierProvider<UpdateProvider>(create: (context) => UpdateProvider(context: context)),
ChangeNotifierProvider<ThemeModeObserver>(
create: (context) =>
ThemeModeObserver(initialTheme: settings.theme)),
ChangeNotifierProvider<NewsProvider>(
create: (context) => NewsProvider(context: context)),
ChangeNotifierProvider<UpdateProvider>(
create: (context) => UpdateProvider(context: context)),
// User data providers
ChangeNotifierProvider<GradeProvider>(create: (_) => GradeProvider(settings: settings, user: user, database: database, kreta: kreta)),
ChangeNotifierProvider<GradeProvider>(
create: (_) => GradeProvider(
settings: settings,
user: user,
database: database,
kreta: kreta)),
ChangeNotifierProvider<TimetableProvider>(create: (_) => timetable),
ChangeNotifierProvider<ExamProvider>(create: (context) => ExamProvider(context: context)),
ChangeNotifierProvider<HomeworkProvider>(create: (context) => HomeworkProvider(context: context)),
ChangeNotifierProvider<MessageProvider>(create: (context) => MessageProvider(context: context)),
ChangeNotifierProvider<NoteProvider>(create: (context) => NoteProvider(context: context)),
ChangeNotifierProvider<EventProvider>(create: (context) => EventProvider(context: context)),
ChangeNotifierProvider<AbsenceProvider>(create: (context) => AbsenceProvider(context: context)),
ChangeNotifierProvider<ExamProvider>(
create: (context) => ExamProvider(context: context)),
ChangeNotifierProvider<HomeworkProvider>(
create: (context) => HomeworkProvider(context: context)),
ChangeNotifierProvider<MessageProvider>(
create: (context) => MessageProvider(context: context)),
ChangeNotifierProvider<NoteProvider>(
create: (context) => NoteProvider(context: context)),
ChangeNotifierProvider<EventProvider>(
create: (context) => EventProvider(context: context)),
ChangeNotifierProvider<AbsenceProvider>(
create: (context) => AbsenceProvider(context: context)),
ChangeNotifierProvider<GradeCalculatorProvider>(
create: (_) => GradeCalculatorProvider(settings: settings, user: user, database: database, kreta: kreta)),
ChangeNotifierProvider<LiveCardProvider>(create: (context) => LiveCardProvider(timetable: timetable, settings: settings))
create: (_) => GradeCalculatorProvider(
settings: settings,
user: user,
database: database,
kreta: kreta)),
ChangeNotifierProvider<LiveCardProvider>(
create: (context) =>
LiveCardProvider(timetable: timetable, settings: settings))
],
child: Consumer<ThemeModeObserver>(
builder: (context, themeMode, child) {
@@ -110,12 +142,15 @@ class App extends StatelessWidget {
return MaterialApp(
builder: (context, child) {
// Limit font size scaling to 1.0
double textScaleFactor = min(MediaQuery.of(context).textScaleFactor, 1.0);
double textScaleFactor =
min(MediaQuery.of(context).textScaleFactor, 1.0);
return I18n(
initialLocale: Locale(settings.language, settings.language.toUpperCase()),
initialLocale: Locale(
settings.language, settings.language.toUpperCase()),
child: MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: textScaleFactor),
data: MediaQuery.of(context)
.copyWith(textScaleFactor: textScaleFactor),
child: child ?? Container(),
),
);
@@ -148,7 +183,8 @@ class App extends StatelessWidget {
return locale;
},
onGenerateRoute: (settings) => rootNavigator(settings),
initialRoute: user.getUsers().isNotEmpty ? "navigation" : "login",
initialRoute:
user.getUsers().isNotEmpty ? "navigation" : "login",
);
},
);
@@ -162,7 +198,8 @@ class App extends StatelessWidget {
if (Platform.isAndroid || Platform.isIOS) {
switch (route.name) {
case "login_back":
return CupertinoPageRoute(builder: (context) => const mobile.LoginScreen(back: true));
return CupertinoPageRoute(
builder: (context) => const mobile.LoginScreen(back: true));
case "login":
return _rootRoute(const mobile.LoginScreen());
case "navigation":
@@ -175,7 +212,8 @@ class App extends StatelessWidget {
} else if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
switch (route.name) {
case "login_back":
return CupertinoPageRoute(builder: (context) => const desktop.LoginScreen(back: true));
return CupertinoPageRoute(
builder: (context) => const desktop.LoginScreen(back: true));
case "login":
return _rootRoute(const desktop.LoginScreen());
case "navigation":

View File

@@ -5,28 +5,36 @@ import 'dart:io';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/database/struct.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:sqflite/sqflite.dart';
// ignore: depend_on_referenced_packages
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
const settingsDB = DatabaseStruct("settings", {
"language": String, "start_page": int, "rounding": int, "theme": int, "accent_color": int, "news": int, "news_state": int, "developer_mode": int,
"update_channel": int, "config": String, "custom_accent_color": int, "custom_background_color": int, "custom_highlight_color": int, // general
"grade_color1": int, "grade_color2": int, "grade_color3": int, "grade_color4": int, "grade_color5": int, // grade colors
"language": String, "start_page": int, "rounding": int, "theme": int,
"accent_color": int, "news": int, "news_state": int, "developer_mode": int,
"update_channel": int, "config": String, "custom_accent_color": int,
"custom_background_color": int, "custom_highlight_color": int, // general
"grade_color1": int, "grade_color2": int, "grade_color3": int,
"grade_color4": int, "grade_color5": int, // grade colors
"vibration_strength": int, "ab_weeks": int, "swap_ab_weeks": int,
"notifications": int, "notifications_bitfield": int, "notification_poll_interval": int, // notifications
"x_filc_id": String, "graph_class_avg": int, "presentation_mode": int, "bell_delay": int, "bell_delay_enabled": int,
"grade_opening_fun": int, "icon_pack": String, "premium_scopes": String, "premium_token": String, "premium_login": String,
"notifications": int, "notifications_bitfield": int,
"notification_poll_interval": int, // notifications
"x_filc_id": String, "graph_class_avg": int, "presentation_mode": int,
"bell_delay": int, "bell_delay_enabled": int,
"grade_opening_fun": int, "icon_pack": String, "premium_scopes": String,
"premium_token": String, "premium_login": String,
"last_account_id": String, "renamed_subjects_enabled": int,
"renamed_subjects_italics": int,
});
// DON'T FORGET TO UPDATE DEFAULT VALUES IN `initDB` MIGRATION OR ELSE PARENTS WILL COMPLAIN ABOUT THEIR CHILDREN MISSING
// YOU'VE BEEN WARNED!!!
const usersDB = DatabaseStruct("users", {
"id": String, "name": String, "username": String, "password": String, "institute_code": String, "student": String, "role": int,
"id": String, "name": String, "username": String, "password": String,
"institute_code": String, "student": String, "role": int,
"nickname": String, "picture": String // premium only
});
const userDataDB = DatabaseStruct("user_data", {
"id": String, "grades": String, "timetable": String, "exams": String, "homework": String, "messages": String, "notes": String,
"id": String, "grades": String, "timetable": String, "exams": String,
"homework": String, "messages": String, "notes": String,
"events": String, "absences": String, "group_averages": String,
// renamed subjects // non kreta data
"renamed_subjects": String,
@@ -34,7 +42,8 @@ const userDataDB = DatabaseStruct("user_data", {
"last_seen_grade": int,
});
Future<void> createTable(Database db, DatabaseStruct struct) => db.execute("CREATE TABLE IF NOT EXISTS ${struct.table} ($struct)");
Future<void> createTable(Database db, DatabaseStruct struct) =>
db.execute("CREATE TABLE IF NOT EXISTS ${struct.table} ($struct)");
Future<Database> initDB(DatabaseProvider database) async {
Database db;
@@ -50,9 +59,11 @@ Future<Database> initDB(DatabaseProvider database) async {
await createTable(db, usersDB);
await createTable(db, userDataDB);
if ((await db.rawQuery("SELECT COUNT(*) FROM settings"))[0].values.first == 0) {
if ((await db.rawQuery("SELECT COUNT(*) FROM settings"))[0].values.first ==
0) {
// Set default values for table Settings
await db.insert("settings", SettingsProvider.defaultSettings(database: database).toMap());
await db.insert("settings",
SettingsProvider.defaultSettings(database: database).toMap());
}
// Migrate Databases
@@ -60,7 +71,8 @@ Future<Database> initDB(DatabaseProvider database) async {
await migrateDB(
db,
struct: settingsDB,
defaultValues: SettingsProvider.defaultSettings(database: database).toMap(),
defaultValues:
SettingsProvider.defaultSettings(database: database).toMap(),
);
await migrateDB(
db,
@@ -68,7 +80,8 @@ Future<Database> initDB(DatabaseProvider database) async {
defaultValues: {"role": 0, "nickname": "", "picture": ""},
);
await migrateDB(db, struct: userDataDB, defaultValues: {
"grades": "[]", "timetable": "[]", "exams": "[]", "homework": "[]", "messages": "[]", "notes": "[]", "events": "[]", "absences": "[]",
"grades": "[]", "timetable": "[]", "exams": "[]", "homework": "[]",
"messages": "[]", "notes": "[]", "events": "[]", "absences": "[]",
"group_averages": "[]",
// renamed subjects // non kreta data
"renamed_subjects": "{}",
@@ -99,7 +112,8 @@ Future<void> migrateDB(
// go through each row and add missing keys or delete non existing keys
await Future.forEach<Map<String, Object?>>(originalRows, (original) async {
bool migrationRequired = struct.struct.keys.any((key) => !original.containsKey(key) || original[key] == null) ||
bool migrationRequired = struct.struct.keys.any(
(key) => !original.containsKey(key) || original[key] == null) ||
original.keys.any((key) => !struct.struct.containsKey(key));
if (migrationRequired) {

View File

@@ -0,0 +1,76 @@
import 'dart:math';
import 'dart:ui';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/api/providers/status_provider.dart';
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/helpers/notification_helper.i18n.dart';
import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class NotificationsHelper {
@pragma('vm:entry-point')
void backgroundJob() async {
// initialize providers
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
DatabaseProvider database = DatabaseProvider();
await database.init();
SettingsProvider settingsProvider =
await database.query.getSettings(database);
UserProvider userProvider = await database.query.getUsers(settingsProvider);
if (userProvider.id != null && settingsProvider.notificationsEnabled) {
// refresh grades
final status = StatusProvider();
final kretaClient = KretaClient(
user: userProvider, settings: settingsProvider, status: status);
kretaClient.refreshLogin();
GradeProvider gradeProvider = GradeProvider(
settings: settingsProvider,
user: userProvider,
database: database,
kreta: kretaClient);
gradeProvider.fetch();
List<Grade> grades =
await database.userQuery.getGrades(userId: userProvider.id ?? "");
DateTime lastSeenGrade =
await database.userQuery.lastSeenGrade(userId: userProvider.id ?? "");
// loop through grades and see which hasn't been seen yet
for (Grade grade in grades) {
// if the grade was added over a week ago, don't show it to avoid notification spam
if (grade.seenDate.isAfter(lastSeenGrade) &&
grade.date.difference(DateTime.now()).inDays * -1 < 7) {
// send notificiation about new grade
const AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails('GRADES', 'Jegyek',
channelDescription: 'Értesítés jegyek beírásakor',
importance: Importance.max,
priority: Priority.max,
color: Color(0xFF3D7BF4),
ticker: 'Jegyek');
const NotificationDetails notificationDetails =
NotificationDetails(android: androidNotificationDetails);
await flutterLocalNotificationsPlugin.show(
// probably shouldn't use a random int
Random().nextInt(432234 * 2),
"title".i18n,
"body".i18n.fill([
grade.value.value.toString(),
grade.subject.isRenamed &&
settingsProvider.renamedSubjectsEnabled
? grade.subject.renamedTo!
: grade.subject.name
]),
notificationDetails);
}
}
// set grade seen status
gradeProvider.seenAll();
}
}
}

View File

@@ -0,0 +1,24 @@
import 'package:i18n_extension/i18n_extension.dart';
extension Localization on String {
static final _t = Translations.byLocale("hu_hu") +
{
"en_en": {
"title": "New grade",
"body": "You got a %s in %s"
},
"hu_hu": {
"title": "Új jegy",
"body": "%s-st kaptál %s tantárgyból"
},
"de_de": {
"title": "Neue Note",
"body": "Du hast eine %s in %s"
},
};
String get i18n => localize(this, _t);
String fill(List<Object> params) => localizeFill(this, params);
String plural(int value) => localizePlural(value, this, _t);
String version(Object modifier) => localizeVersion(modifier, this, _t);
}

View File

@@ -19,13 +19,15 @@ extension UpdateHelper on Release {
updateCallback!(-1, UpdateState.preparing);
String downloads = await StorageHelper.downloadsPath();
File apk = File("$downloads/filcnaplo-$version.apk");
File apk = File("$downloads/refilc-v$version.apk");
if (!await apk.exists()) {
updateCallback(-1, UpdateState.downloading);
var bytes = await download(updateCallback: updateCallback);
if (!await StorageHelper.write(apk.path, bytes)) throw "failed to write apk: permission denied";
if (!await StorageHelper.write(apk.path, bytes)) {
throw "failed to write apk: permission denied";
}
}
updateCallback(-1, UpdateState.installing);
@@ -50,7 +52,8 @@ extension UpdateHelper on Release {
var completer = Completer<Uint8List>();
response?.stream.listen((List<int> chunk) {
updateCallback!(downloaded / (response.contentLength ?? 0), UpdateState.downloading);
updateCallback!(
downloaded / (response.contentLength ?? 0), UpdateState.downloading);
chunks.add(chunk);
downloaded += chunk.length;

View File

@@ -1,7 +1,10 @@
import 'dart:io';
import 'package:background_fetch/background_fetch.dart';
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/database/init.dart';
import 'package:filcnaplo/helpers/notification_helper.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -9,6 +12,7 @@ import 'package:filcnaplo/app.dart';
import 'package:flutter/services.dart';
import 'package:filcnaplo_mobile_ui/screens/error_screen.dart';
import 'package:filcnaplo_mobile_ui/screens/error_report_screen.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
void main() async {
// Initalize
@@ -43,6 +47,57 @@ class Startup {
await database.init();
settings = await database.query.getSettings(database);
user = await database.query.getUsers(settings);
// Notifications setup
initPlatformState();
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
// Get permission to show notifications
if (Platform.isAndroid) {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()!
.requestPermission();
} else if (Platform.isIOS) {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: false,
badge: true,
sound: true,
);
} else if (Platform.isMacOS) {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
MacOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: false,
badge: true,
sound: true,
);
}
// Platform specific settings
const DarwinInitializationSettings initializationSettingsDarwin =
DarwinInitializationSettings(
requestSoundPermission: true,
requestBadgePermission: true,
requestAlertPermission: false,
);
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('ic_notification');
const InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsDarwin,
macOS: initializationSettingsDarwin);
// Initialize notifications
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
);
}
}
@@ -72,8 +127,58 @@ Widget errorBuilder(FlutterErrorDetails details) {
});
}
Future<void> initPlatformState() async {
// Configure BackgroundFetch.
int status = await BackgroundFetch.configure(
BackgroundFetchConfig(
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: true,
requiresBatteryNotLow: false,
requiresCharging: false,
requiresStorageNotLow: false,
requiresDeviceIdle: false,
requiredNetworkType: NetworkType.ANY,
startOnBoot: true), (String taskId) async {
// <-- Event handler
if (kDebugMode) {
print("[BackgroundFetch] Event received $taskId");
}
NotificationsHelper().backgroundJob();
BackgroundFetch.finish(taskId);
}, (String taskId) async {
// <-- Task timeout handler.
if (kDebugMode) {
print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
}
BackgroundFetch.finish(taskId);
});
if (kDebugMode) {
print('[BackgroundFetch] configure success: $status');
}
BackgroundFetch.scheduleTask(TaskConfig(
taskId: "com.transistorsoft.refilcnotification",
delay: 900000, // 15 minutes
periodic: true,
forceAlarmManager: true,
stopOnTerminate: false,
enableHeadless: true));
}
@pragma('vm:entry-point')
void backgroundHeadlessTask(HeadlessTask task) {
print('[BackgroundFetch] Headless event received.');
String taskId = task.taskId;
bool isTimeout = task.timeout;
if (isTimeout) {
if (kDebugMode) {
print("[BackgroundFetch] Headless task timed-out: $taskId");
}
BackgroundFetch.finish(taskId);
return;
}
if (kDebugMode) {
print('[BackgroundFetch] Headless event received.');
}
NotificationsHelper().backgroundJob();
BackgroundFetch.finish(task.taskId);
}

View File

@@ -68,6 +68,7 @@ class SettingsProvider extends ChangeNotifier {
String _premiumLogin;
String _lastAccountId;
bool _renamedSubjectsEnabled;
bool _renamedSubjectsItalics;
SettingsProvider({
DatabaseProvider? database,
@@ -104,6 +105,7 @@ class SettingsProvider extends ChangeNotifier {
required String premiumLogin,
required String lastAccountId,
required bool renameSubjectsEnabled,
required bool renameSubjectsItalics,
}) : _database = database,
_language = language,
_startPage = startPage,
@@ -137,7 +139,8 @@ class SettingsProvider extends ChangeNotifier {
_premiumAccessToken = premiumAccessToken,
_premiumLogin = premiumLogin,
_lastAccountId = lastAccountId,
_renamedSubjectsEnabled = renameSubjectsEnabled;
_renamedSubjectsEnabled = renameSubjectsEnabled,
_renamedSubjectsItalics = renameSubjectsItalics;
factory SettingsProvider.fromMap(Map map,
{required DatabaseProvider database}) {
@@ -191,6 +194,7 @@ class SettingsProvider extends ChangeNotifier {
premiumLogin: map["premium_login"],
lastAccountId: map["last_account_id"],
renameSubjectsEnabled: map["renamed_subjects_enabled"] == 1,
renameSubjectsItalics: map["renamed_subjects_italics"] == 0,
);
}
@@ -231,7 +235,8 @@ class SettingsProvider extends ChangeNotifier {
"premium_token": _premiumAccessToken,
"premium_login": _premiumLogin,
"last_account_id": _lastAccountId,
"renamed_subjects_enabled": _renamedSubjectsEnabled ? 1 : 0
"renamed_subjects_enabled": _renamedSubjectsEnabled ? 1 : 0,
"renamed_subjects_italics": _renamedSubjectsItalics ? 1 : 0
};
}
@@ -277,6 +282,7 @@ class SettingsProvider extends ChangeNotifier {
premiumLogin: "igen",
lastAccountId: "",
renameSubjectsEnabled: false,
renameSubjectsItalics: false,
);
}
@@ -317,6 +323,7 @@ class SettingsProvider extends ChangeNotifier {
String get premiumLogin => _premiumLogin;
String get lastAccountId => _lastAccountId;
bool get renamedSubjectsEnabled => _renamedSubjectsEnabled;
bool get renamedSubjectsItalics => _renamedSubjectsItalics;
Future<void> update({
bool store = true,
@@ -353,69 +360,94 @@ class SettingsProvider extends ChangeNotifier {
String? premiumLogin,
String? lastAccountId,
bool? renamedSubjectsEnabled,
bool? renamedSubjectsItalics,
}) async {
if (language != null && language != _language) _language = language;
if (startPage != null && startPage != _startPage) _startPage = startPage;
if (rounding != null && rounding != _rounding) _rounding = rounding;
if (theme != null && theme != _theme) _theme = theme;
if (accentColor != null && accentColor != _accentColor)
if (accentColor != null && accentColor != _accentColor) {
_accentColor = accentColor;
if (gradeColors != null && gradeColors != _gradeColors)
}
if (gradeColors != null && gradeColors != _gradeColors) {
_gradeColors = gradeColors;
if (newsEnabled != null && newsEnabled != _newsEnabled)
}
if (newsEnabled != null && newsEnabled != _newsEnabled) {
_newsEnabled = newsEnabled;
}
if (newsState != null && newsState != _newsState) _newsState = newsState;
if (notificationsEnabled != null &&
notificationsEnabled != _notificationsEnabled)
notificationsEnabled != _notificationsEnabled) {
_notificationsEnabled = notificationsEnabled;
}
if (notificationsBitfield != null &&
notificationsBitfield != _notificationsBitfield)
notificationsBitfield != _notificationsBitfield) {
_notificationsBitfield = notificationsBitfield;
if (developerMode != null && developerMode != _developerMode)
}
if (developerMode != null && developerMode != _developerMode) {
_developerMode = developerMode;
}
if (notificationPollInterval != null &&
notificationPollInterval != _notificationPollInterval) {
_notificationPollInterval = notificationPollInterval;
}
if (vibrate != null && vibrate != _vibrate) _vibrate = vibrate;
if (abWeeks != null && abWeeks != _abWeeks) _abWeeks = abWeeks;
if (swapABweeks != null && swapABweeks != _swapABweeks)
if (swapABweeks != null && swapABweeks != _swapABweeks) {
_swapABweeks = swapABweeks;
if (updateChannel != null && updateChannel != _updateChannel)
}
if (updateChannel != null && updateChannel != _updateChannel) {
_updateChannel = updateChannel;
}
if (config != null && config != _config) _config = config;
if (xFilcId != null && xFilcId != _xFilcId) _xFilcId = xFilcId;
if (graphClassAvg != null && graphClassAvg != _graphClassAvg)
if (graphClassAvg != null && graphClassAvg != _graphClassAvg) {
_graphClassAvg = graphClassAvg;
}
if (goodStudent != null) _goodStudent = goodStudent;
if (presentationMode != null && presentationMode != _presentationMode)
if (presentationMode != null && presentationMode != _presentationMode) {
_presentationMode = presentationMode;
}
if (bellDelay != null && bellDelay != _bellDelay) _bellDelay = bellDelay;
if (bellDelayEnabled != null && bellDelayEnabled != _bellDelayEnabled)
if (bellDelayEnabled != null && bellDelayEnabled != _bellDelayEnabled) {
_bellDelayEnabled = bellDelayEnabled;
if (gradeOpeningFun != null && gradeOpeningFun != _gradeOpeningFun)
}
if (gradeOpeningFun != null && gradeOpeningFun != _gradeOpeningFun) {
_gradeOpeningFun = gradeOpeningFun;
}
if (iconPack != null && iconPack != _iconPack) _iconPack = iconPack;
if (customAccentColor != null && customAccentColor != _customAccentColor)
if (customAccentColor != null && customAccentColor != _customAccentColor) {
_customAccentColor = customAccentColor;
}
if (customBackgroundColor != null &&
customBackgroundColor != _customBackgroundColor)
customBackgroundColor != _customBackgroundColor) {
_customBackgroundColor = customBackgroundColor;
}
if (customHighlightColor != null &&
customHighlightColor != _customHighlightColor)
customHighlightColor != _customHighlightColor) {
_customHighlightColor = customHighlightColor;
if (premiumScopes != null && premiumScopes != _premiumScopes)
}
if (premiumScopes != null && premiumScopes != _premiumScopes) {
_premiumScopes = premiumScopes;
if (premiumAccessToken != null && premiumAccessToken != _premiumAccessToken)
}
if (premiumAccessToken != null &&
premiumAccessToken != _premiumAccessToken) {
_premiumAccessToken = premiumAccessToken;
if (premiumLogin != null && premiumLogin != _premiumLogin)
}
if (premiumLogin != null && premiumLogin != _premiumLogin) {
_premiumLogin = premiumLogin;
if (lastAccountId != null && lastAccountId != _lastAccountId)
}
if (lastAccountId != null && lastAccountId != _lastAccountId) {
_lastAccountId = lastAccountId;
}
if (renamedSubjectsEnabled != null &&
renamedSubjectsEnabled != _renamedSubjectsEnabled)
renamedSubjectsEnabled != _renamedSubjectsEnabled) {
_renamedSubjectsEnabled = renamedSubjectsEnabled;
}
if (renamedSubjectsItalics != null &&
renamedSubjectsItalics != _renamedSubjectsItalics) {
_renamedSubjectsItalics = renamedSubjectsItalics;
}
if (store) await _database?.store.storeSettings(this);
notifyListeners();
}

View File

@@ -10,6 +10,8 @@ enum AccentColor {
red,
pink,
purple,
none,
ogfilc,
adaptive,
custom
}
@@ -24,6 +26,8 @@ Map<AccentColor, Color> accentColorMap = {
AccentColor.red: Colors.red.shade300,
AccentColor.pink: Colors.pink.shade300,
AccentColor.purple: Colors.purple.shade300,
//AccentColor.none: Colors.black,
AccentColor.ogfilc: const Color(0xff20AC9B),
AccentColor.adaptive: const Color(0xFF3D7BF4),
AccentColor.custom: const Color(0xFF3D7BF4),
};

View File

@@ -38,6 +38,7 @@ class DarkMobileAppColors implements ThemeAppColors {
final gradeTwo = const Color(0xFFAE3DF4);
@override
final gradeOne = const Color(0xFFF43DAB);
@override
final purple = const Color(0xffBF5AF2);
@override
final pink = const Color(0xffFF375F);

View File

@@ -89,6 +89,7 @@ Future<List<DateWidget>> getFilterWidgets(FilterType activeData,
// Grades
case FilterType.grades:
gradeProvider.seenAll();
items = grade_filter.getWidgets(
gradeProvider.grades, gradeProvider.lastSeenDate);
if (settingsProvider.gradeOpeningFun) {

View File

@@ -23,6 +23,8 @@ class GradeTile extends StatelessWidget {
Widget build(BuildContext context) {
String title;
String subtitle;
bool isTitleItalic = false;
bool isSubtitleItalic = false;
EdgeInsets leadingPadding = EdgeInsets.zero;
bool isSubjectView = SubjectGradesContainer.of(context) != null;
String subjectName =
@@ -32,7 +34,8 @@ class GradeTile extends StatelessWidget {
GradeCalculatorProvider calculatorProvider =
Provider.of<GradeCalculatorProvider>(context, listen: false);
SettingsProvider settingsProvider =
Provider.of<SettingsProvider>(context);
// Test order:
// description
// mode
@@ -47,6 +50,7 @@ class GradeTile extends StatelessWidget {
}
} else {
title = subjectName;
isTitleItalic = grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics;
}
// Test order:
@@ -58,6 +62,7 @@ class GradeTile extends StatelessWidget {
? modeDescription
: ""
: subjectName;
isSubtitleItalic = isSubjectView ? false : grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics;
} else {
subtitle = grade.value.valueName.split("(")[0];
}
@@ -122,7 +127,7 @@ class GradeTile extends StatelessWidget {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.w600,
fontStyle: grade.subject.isRenamed && title == subjectName
fontStyle: isTitleItalic
? FontStyle.italic
: null),
),
@@ -144,7 +149,7 @@ class GradeTile extends StatelessWidget {
subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w500),
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: isSubtitleItalic ? FontStyle.italic : null),
)
: null,
trailing: isSubjectView

View File

@@ -1,3 +1,4 @@
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo_kreta_api/providers/exam_provider.dart';
import 'package:filcnaplo_kreta_api/providers/homework_provider.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
@@ -15,7 +16,8 @@ import 'package:provider/provider.dart';
import 'lesson_tile.i18n.dart';
class LessonTile extends StatelessWidget {
const LessonTile(this.lesson, {Key? key, this.onTap, this.swapDesc = false}) : super(key: key);
const LessonTile(this.lesson, {Key? key, this.onTap, this.swapDesc = false})
: super(key: key);
final Lesson lesson;
final bool swapDesc;
@@ -29,12 +31,16 @@ class LessonTile extends StatelessWidget {
bool fill = false;
bool fillLeading = false;
String lessonIndexTrailing = "";
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
// Only put a trailing . if its a digit
if (RegExp(r'\d').hasMatch(lesson.lessonIndex)) lessonIndexTrailing = ".";
var now = DateTime.now();
if (lesson.start.isBefore(now) && lesson.end.isAfter(now) && lesson.status?.name != "Elmaradt") {
if (lesson.start.isBefore(now) &&
lesson.end.isAfter(now) &&
lesson.status?.name != "Elmaradt") {
fillLeading = true;
}
@@ -62,7 +68,8 @@ class LessonTile extends StatelessWidget {
if (lesson.homeworkId != "") {
Homework homework = Provider.of<HomeworkProvider>(context, listen: false)
.homework
.firstWhere((h) => h.id == lesson.homeworkId, orElse: () => Homework.fromJson({}));
.firstWhere((h) => h.id == lesson.homeworkId,
orElse: () => Homework.fromJson({}));
if (homework.id != "") {
subtiles.add(LessonSubtile(
@@ -74,11 +81,16 @@ class LessonTile extends StatelessWidget {
}
if (lesson.exam != "") {
Exam exam = Provider.of<ExamProvider>(context, listen: false).exams.firstWhere((t) => t.id == lesson.exam, orElse: () => Exam.fromJson({}));
Exam exam = Provider.of<ExamProvider>(context, listen: false)
.exams
.firstWhere((t) => t.id == lesson.exam,
orElse: () => Exam.fromJson({}));
if (exam.id != "") {
subtiles.add(LessonSubtile(
type: LessonSubtileType.exam,
title: exam.description != "" ? exam.description : exam.mode?.description ?? "exam".i18n,
title: exam.description != ""
? exam.description
: exam.mode?.description ?? "exam".i18n,
onPressed: () => ExamView.show(exam, context: context),
));
}
@@ -87,7 +99,10 @@ class LessonTile extends StatelessWidget {
String description = '';
String room = '';
final cleanDesc = lesson.description.specialChars().toLowerCase().replaceAll(lesson.subject.name.specialChars().toLowerCase(), '');
final cleanDesc = lesson.description
.specialChars()
.toLowerCase()
.replaceAll(lesson.subject.name.specialChars().toLowerCase(), '');
if (!swapDesc) {
if (cleanDesc != "") {
@@ -131,16 +146,23 @@ class LessonTile extends StatelessWidget {
// onLongPress: kDebugMode ? () => log(jsonEncode(lesson.json)) : null,
visualDensity: VisualDensity.compact,
contentPadding: const EdgeInsets.symmetric(horizontal: 4.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: Text(
!lesson.isEmpty ? lesson.subject.renamedTo ?? lesson.subject.name.capital() : "empty".i18n,
!lesson.isEmpty
? lesson.subject.renamedTo ??
lesson.subject.name.capital()
: "empty".i18n,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 15.5,
color: AppColors.of(context).text.withOpacity(!lesson.isEmpty ? 1.0 : 0.5),
fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null),
color: AppColors.of(context)
.text
.withOpacity(!lesson.isEmpty ? 1.0 : 0.5),
fontStyle:
lesson.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: description != ""
? Text(
@@ -175,12 +197,20 @@ class LessonTile extends StatelessWidget {
offset: const Offset(-12.0, -2.0),
child: Container(
decoration: BoxDecoration(
color: fillLeading ? Theme.of(context).colorScheme.secondary.withOpacity(.3) : const Color(0x00000000),
color: fillLeading
? Theme.of(context)
.colorScheme
.secondary
.withOpacity(.3)
: const Color(0x00000000),
borderRadius: BorderRadius.circular(12.0),
boxShadow: [
if (fillLeading)
BoxShadow(
color: Theme.of(context).colorScheme.secondary.withOpacity(.25),
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.25),
blurRadius: 6.0,
)
],
@@ -210,7 +240,9 @@ class LessonTile extends StatelessWidget {
maxLines: 2,
style: TextStyle(
fontWeight: FontWeight.w500,
color: AppColors.of(context).text.withOpacity(.75),
color: AppColors.of(context)
.text
.withOpacity(.75),
),
),
),
@@ -225,7 +257,9 @@ class LessonTile extends StatelessWidget {
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w500,
color: AppColors.of(context).text.withOpacity(.9),
color: AppColors.of(context)
.text
.withOpacity(.9),
),
),
],
@@ -249,7 +283,9 @@ class LessonTile extends StatelessWidget {
enum LessonSubtileType { homework, exam, absence }
class LessonSubtile extends StatelessWidget {
const LessonSubtile({Key? key, this.onPressed, required this.title, required this.type}) : super(key: key);
const LessonSubtile(
{Key? key, this.onPressed, required this.title, required this.type})
: super(key: key);
final Function()? onPressed;
final String title;
@@ -285,7 +321,8 @@ class LessonSubtile extends StatelessWidget {
Center(
child: SizedBox(
width: 30.0,
child: Icon(icon, color: iconColor.withOpacity(.75), size: 20.0),
child:
Icon(icon, color: iconColor.withOpacity(.75), size: 20.0),
),
),
Expanded(
@@ -295,7 +332,9 @@ class LessonSubtile extends StatelessWidget {
title.escapeHtml(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w500, color: AppColors.of(context).text.withOpacity(.65)),
style: TextStyle(
fontWeight: FontWeight.w500,
color: AppColors.of(context).text.withOpacity(.65)),
),
),
),

View File

@@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h"
#include <dynamic_color/dynamic_color_plugin.h>
#include <file_selector_linux/file_selector_plugin.h>
#include <flutter_acrylic/flutter_acrylic_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
@@ -14,6 +15,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) flutter_acrylic_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterAcrylicPlugin");
flutter_acrylic_plugin_register_with_registrar(flutter_acrylic_registrar);

View File

@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
file_selector_linux
flutter_acrylic
url_launcher_linux
)

View File

@@ -7,19 +7,21 @@ import Foundation
import connectivity_plus
import dynamic_color
import flutter_acrylic
import file_selector_macos
import flutter_local_notifications
import macos_window_utils
import package_info_plus
import path_provider_foundation
import share_plus_macos
import share_plus
import sqflite
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
FlutterAcrylicPlugin.register(with: registry.registrar(forPlugin: "FlutterAcrylicPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))

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.0.1+205
version: 4.1.0+213
environment:
sdk: ">=2.17.0 <3.0.0"
@@ -38,7 +38,7 @@ dependencies:
ref: master
path_provider: ^2.0.2
permission_handler: ^10.2.0
share_plus: ^5.0.0
share_plus: ^7.0.2
connectivity_plus: ^4.0.1
flutter_displaymode: ^0.6.0
quick_actions: ^1.0.1
@@ -48,17 +48,14 @@ dependencies:
crypto: ^3.0.2
elegant_notification: ^1.6.1
flutter_feather_icons: ^2.0.0+1
live_activities: ^1.0.0
live_activities: ^1.7.4
animated_flip_counter: ^0.2.5
lottie: ^1.4.3
rive: ^0.9.1
animated_background: ^2.0.0
dropdown_button2: ^1.8.9
home_widget: ^0.1.6
flutter_expandable_fab:
git:
url: https://github.com/filc/flutter_expandable_fab
ref: master
flutter_expandable_fab: ^1.8.1
uni_links: ^0.5.1
url_launcher: ^6.1.6
workmanager: ^0.5.1
@@ -67,7 +64,7 @@ dependencies:
animations: ^2.0.1
background_fetch: ^1.1.5
flutter_local_notifications: ^14.1.0
package_info_plus: ^3.1.2
package_info_plus: ^4.0.2
dev_dependencies:
flutter_lints: ^2.0.1
@@ -148,12 +145,12 @@ flutter:
style: italic
flutter_launcher_icons:
image_path: assets/icons/ic_launcher.png
image_path: assets/icons/ic_android.png
android: true
adaptive_icon_background: "#3D7BF4"
adaptive_icon_foreground: assets/icons/ic_launcher_foreground.png
ios: true
remove_alpha_ios: true
adaptive_icon_foreground: assets/icons/ic_android.png
ios: false
remove_alpha_ios: false
flutter_native_splash:
color: "#3D7BF4"

View File

@@ -16,13 +16,15 @@ import 'package:filcnaplo_kreta_api/client/client.dart';
class NavigationScreen extends StatefulWidget {
const NavigationScreen({Key? key}) : super(key: key);
static NavigationScreenState? of(BuildContext context) => context.findAncestorStateOfType<NavigationScreenState>();
static NavigationScreenState? of(BuildContext context) =>
context.findAncestorStateOfType<NavigationScreenState>();
@override
State<NavigationScreen> createState() => NavigationScreenState();
}
class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingObserver {
class NavigationScreenState extends State<NavigationScreen>
with WidgetsBindingObserver {
final _navigatorState = GlobalKey<NavigatorState>();
late NavigationRoute selected;
late SettingsProvider settings;
@@ -40,7 +42,8 @@ class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingO
WidgetsBinding.instance.addObserver(this);
// set client User-Agent
Provider.of<KretaClient>(context, listen: false).userAgent = settings.config.userAgent;
Provider.of<KretaClient>(context, listen: false).userAgent =
settings.config.userAgent;
// Get news
newsProvider = Provider.of<NewsProvider>(context, listen: false);
@@ -54,7 +57,11 @@ class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingO
await Window.initialize();
} catch (_) {}
// Transparent sidebar
await Window.setEffect(effect: WindowEffect.acrylic, color: Platform.isMacOS ? Colors.transparent : const Color.fromARGB(27, 27, 27, 27));
await Window.setEffect(
effect: WindowEffect.acrylic,
color: Platform.isMacOS
? Colors.transparent
: const Color.fromARGB(27, 27, 27, 27));
// todo: do for windows
if (Platform.isMacOS) {
@@ -75,8 +82,12 @@ class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingO
@override
void didChangePlatformBrightness() {
if (settings.theme == ThemeMode.system) {
Brightness? brightness = WidgetsBinding.instance.window.platformBrightness;
Provider.of<ThemeModeObserver>(context, listen: false).changeTheme(brightness == Brightness.light ? ThemeMode.light : ThemeMode.dark);
// ignore: deprecated_member_use
Brightness? brightness =
// ignore: deprecated_member_use
WidgetsBinding.instance.window.platformBrightness;
Provider.of<ThemeModeObserver>(context, listen: false).changeTheme(
brightness == Brightness.light ? ThemeMode.light : ThemeMode.dark);
}
super.didChangePlatformBrightness();
}
@@ -103,8 +114,12 @@ class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingO
if (_navigatorState.currentState != null)
Container(
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor.withOpacity(.5),
border: Border(right: BorderSide(color: AppColors.of(context).shadow.withOpacity(.7), width: 1.0)),
color:
Theme.of(context).scaffoldBackgroundColor.withOpacity(.5),
border: Border(
right: BorderSide(
color: AppColors.of(context).shadow.withOpacity(.7),
width: 1.0)),
),
child: Padding(
padding: EdgeInsets.only(top: topInset),
@@ -125,7 +140,8 @@ class NavigationScreenState extends State<NavigationScreen> with WidgetsBindingO
child: Navigator(
key: _navigatorState,
initialRoute: selected.name,
onGenerateRoute: (settings) => navigationRouteHandler(settings),
onGenerateRoute: (settings) =>
navigationRouteHandler(settings),
),
),
),

View File

@@ -23,10 +23,7 @@ dependencies:
animations: ^2.0.1
confetti: ^0.6.0
auto_size_text: ^3.0.0
flutter_acrylic:
git:
url: https://github.com/filc/flutter_acrylic
ref: master
flutter_acrylic: ^1.1.3
elegant_notification: ^1.6.1
dev_dependencies:

View File

@@ -1,5 +1,7 @@
import 'package:filcnaplo_kreta_api/client/api.dart';
import 'subject.dart';
class Homework {
Map? json;
DateTime date;
@@ -9,7 +11,7 @@ class Homework {
bool homeworkEnabled;
String teacher;
String content;
String subjectName;
Subject subject;
String group;
List<HomeworkAttachment> attachments;
String id;
@@ -22,7 +24,7 @@ class Homework {
required this.homeworkEnabled,
required this.teacher,
required this.content,
required this.subjectName,
required this.subject,
required this.group,
required this.attachments,
required this.id,
@@ -32,16 +34,27 @@ class Homework {
factory Homework.fromJson(Map json) {
return Homework(
id: json["Uid"] ?? "",
date: json["RogzitesIdopontja"] != null ? DateTime.parse(json["RogzitesIdopontja"]).toLocal() : DateTime(0),
lessonDate: json["FeladasDatuma"] != null ? DateTime.parse(json["FeladasDatuma"]).toLocal() : DateTime(0),
deadline: json["HataridoDatuma"] != null ? DateTime.parse(json["HataridoDatuma"]).toLocal() : DateTime(0),
date: json["RogzitesIdopontja"] != null
? DateTime.parse(json["RogzitesIdopontja"]).toLocal()
: DateTime(0),
lessonDate: json["FeladasDatuma"] != null
? DateTime.parse(json["FeladasDatuma"]).toLocal()
: DateTime(0),
deadline: json["HataridoDatuma"] != null
? DateTime.parse(json["HataridoDatuma"]).toLocal()
: DateTime(0),
byTeacher: json["IsTanarRogzitette"] ?? true,
homeworkEnabled: json["IsTanuloHaziFeladatEnabled"] ?? false,
teacher: (json["RogzitoTanarNeve"] ?? "").trim(),
content: (json["Szoveg"] ?? "").trim(),
subjectName: json["TantargyNeve"] ?? "",
group: json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] ?? "" : "",
attachments: ((json["Csatolmanyok"] ?? []) as List).cast<Map>().map((Map json) => HomeworkAttachment.fromJson(json)).toList(),
subject: Subject.fromJson(json["Tantargy"] ?? {}),
group: json["OsztalyCsoport"] != null
? json["OsztalyCsoport"]["Uid"] ?? ""
: "",
attachments: ((json["Csatolmanyok"] ?? []) as List)
.cast<Map>()
.map((Map json) => HomeworkAttachment.fromJson(json))
.toList(),
json: json,
);
}
@@ -53,7 +66,8 @@ class HomeworkAttachment {
String name;
String type;
HomeworkAttachment({required this.id, this.name = "", this.type = "", this.json});
HomeworkAttachment(
{required this.id, this.name = "", this.type = "", this.json});
factory HomeworkAttachment.fromJson(Map json) {
return HomeworkAttachment(
@@ -64,6 +78,8 @@ class HomeworkAttachment {
);
}
String downloadUrl(String iss) => KretaAPI.downloadHomeworkAttachments(iss, id, type);
bool get isImage => name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".png");
String downloadUrl(String iss) =>
KretaAPI.downloadHomeworkAttachments(iss, id, type);
bool get isImage =>
name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".png");
}

View File

@@ -6,6 +6,7 @@ import 'package:filcnaplo_kreta_api/client/api.dart';
import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo_kreta_api/models/group_average.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.i18n.dart';
import 'package:flutter/material.dart';
class GradeProvider with ChangeNotifier {
@@ -21,7 +22,8 @@ class GradeProvider with ChangeNotifier {
// Public
List<Grade> get grades => _grades;
DateTime get lastSeenDate => _settings.gradeOpeningFun ? _lastSeen : DateTime(3000);
DateTime get lastSeenDate =>
_settings.gradeOpeningFun ? _lastSeen : DateTime(3000);
String get groups => _groups;
List<GroupAverage> get groupAverages => _groupAvg;
@@ -49,7 +51,6 @@ class GradeProvider with ChangeNotifier {
final userStore = _database.userStore;
userStore.storeLastSeenGrade(DateTime.now(), userId: userId);
_lastSeen = DateTime.now();
notifyListeners();
}
}
@@ -65,7 +66,9 @@ class GradeProvider with ChangeNotifier {
_groupAvg = await userQuery.getGroupAverages(userId: userId);
notifyListeners();
DateTime lastSeenDB = await userQuery.lastSeenGrade(userId: userId);
if (lastSeenDB.millisecondsSinceEpoch == 0 || lastSeenDB.year == 0 || !_settings.gradeOpeningFun) {
if (lastSeenDB.millisecondsSinceEpoch == 0 ||
lastSeenDB.year == 0 ||
!_settings.gradeOpeningFun) {
_lastSeen = DateTime.now();
await seenAll();
} else {
@@ -77,13 +80,30 @@ class GradeProvider with ChangeNotifier {
// good student mode, renamed subjects
Future<void> convertBySettings() async {
Map<String, String> renamedSubjects = _settings.renamedSubjectsEnabled ? await _database.userQuery.renamedSubjects(userId: _user.user!.id) : {};
Map<String, String> renamedSubjects = _settings.renamedSubjectsEnabled
? await _database.userQuery.renamedSubjects(userId: _user.user!.id)
: {};
for (Grade grade in _grades) {
grade.subject.renamedTo = renamedSubjects.isNotEmpty ? renamedSubjects[grade.subject.id] : null;
grade.value.value = _settings.goodStudent ? 5 : grade.json!["SzamErtek"] ?? 0;
grade.value.valueName = _settings.goodStudent ? "Példás" : grade.json!["SzovegesErtek"] ?? "";
grade.value.shortName = _settings.goodStudent ? "Példás" : grade.json!["SzovegesErtekelesRovidNev"] ?? "";
grade.subject.renamedTo =
renamedSubjects.isNotEmpty ? renamedSubjects[grade.subject.id] : null;
grade.value.value =
_settings.goodStudent ? 5 : grade.json!["SzamErtek"] ?? 0;
grade.value.valueName = _settings.goodStudent
? "Jeles".i18n
: '${grade.json!["SzovegesErtek"]}'
.replaceAll(RegExp(r'[(]+[12345]?[)]'), '')
.i18n;
grade.value.shortName = _settings.goodStudent
? "Jeles".i18n
: '${grade.json!["SzovegesErtekelesRovidNev"]}' != "null" &&
'${grade.json!["SzovegesErtekelesRovidNev"]}' != "-" &&
'${grade.json!["SzovegesErtekelesRovidNev"]}'
.replaceAll(RegExp(r'[0123456789]+[%]?'), '') !=
""
? '${grade.json!["SzovegesErtekelesRovidNev"]}'.i18n
: grade.value.valueName;
}
notifyListeners();
@@ -102,12 +122,18 @@ class GradeProvider with ChangeNotifier {
if (grades.isNotEmpty || _grades.isNotEmpty) await store(grades);
List? groupsJson = await _kreta.getAPI(KretaAPI.groups(iss));
if (groupsJson == null || groupsJson.isEmpty) throw "Cannot fetch Groups for User ${user.id}";
if (groupsJson == null || groupsJson.isEmpty) {
throw "Cannot fetch Groups for User ${user.id}";
}
_groups = (groupsJson[0]["OktatasNevelesiFeladat"] ?? {})["Uid"] ?? "";
List? groupAvgJson = await _kreta.getAPI(KretaAPI.groupAverages(iss, _groups));
if (groupAvgJson == null) throw "Cannot fetch Class Averages for User ${user.id}";
final groupAvgs = groupAvgJson.map((e) => GroupAverage.fromJson(e)).toList();
List? groupAvgJson =
await _kreta.getAPI(KretaAPI.groupAverages(iss, _groups));
if (groupAvgJson == null) {
throw "Cannot fetch Class Averages for User ${user.id}";
}
final groupAvgs =
groupAvgJson.map((e) => GroupAverage.fromJson(e)).toList();
await storeGroupAvg(groupAvgs);
}

View File

@@ -0,0 +1,33 @@
import 'package:i18n_extension/i18n_extension.dart';
extension Localization on String {
static final _t = Translations.byLocale("hu_hu") +
{
"en_en": {
"Elégtelen": "Fail",
"Elégséges": "Warning but passing",
"Közepes": "Passed",
"": "Good",
"Jeles": "Excellent"
},
"hu_hu": {
"Elégtelen": "Elégtelen",
"Elégséges": "Elégséges",
"Közepes": "Közepes",
"": "",
"Jeles": "Jeles"
},
"de_de": {
"Elégtelen": "Ungenügend",
"Elégséges": "Mangelhaft",
"Közepes": "Ausreichend",
"": "Befriedigend",
"Jeles": "Gut"
},
};
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

@@ -1,5 +1,6 @@
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/models/user.dart';
import 'package:filcnaplo_kreta_api/client/api.dart';
import 'package:filcnaplo_kreta_api/client/client.dart';
@@ -8,6 +9,12 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class HomeworkProvider with ChangeNotifier {
// Private
late final SettingsProvider _settings;
late final UserProvider _user;
late final DatabaseProvider _database;
// Public
late List<Homework> _homework;
late BuildContext _context;
List<Homework> get homework => _homework;
@@ -27,27 +34,56 @@ class HomeworkProvider with ChangeNotifier {
// Load homework from the database
if (userId != null) {
var dbHomework = await Provider.of<DatabaseProvider>(_context, listen: false).userQuery.getHomework(userId: userId);
var dbHomework =
await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getHomework(userId: userId);
_homework = dbHomework;
notifyListeners();
await convertBySettings();
}
}
Future<void> convertBySettings() async {
Map<String, String> renamedSubjects =
(await _database.query.getSettings(_database)).renamedSubjectsEnabled
? await _database.userQuery.renamedSubjects(userId: _user.id!)
: {};
for (Homework homework in _homework) {
homework.subject.renamedTo = renamedSubjects.isNotEmpty
? renamedSubjects[homework.subject.id]
: null;
}
notifyListeners();
}
// Fetches Homework from the Kreta API then stores them in the database
Future<void> fetch({DateTime? from, bool db = true}) async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot fetch Homework for User null";
String iss = user.instituteCode;
List? homeworkJson = await Provider.of<KretaClient>(_context, listen: false).getAPI(KretaAPI.homework(iss, start: from));
List? homeworkJson = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.homework(iss, start: from));
if (homeworkJson == null) throw "Cannot fetch Homework for User ${user.id}";
List<Homework> homework = [];
await Future.forEach(homeworkJson.cast<Map>(), (Map hw) async {
Map? e = await Provider.of<KretaClient>(_context, listen: false).getAPI(KretaAPI.homework(iss, id: hw["Uid"]));
if (e != null) homework.add(Homework.fromJson(e));
Map? e = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.homework(iss, id: hw["Uid"]));
Map<String, String> renamedSubjects = _settings.renamedSubjectsEnabled
? await _database.userQuery.renamedSubjects(userId: _user.user!.id)
: {};
if (e != null) {
Homework hw = Homework.fromJson(e);
hw.subject.renamedTo =
renamedSubjects.isNotEmpty ? renamedSubjects[hw.subject.id] : null;
homework.add(hw);
}
});
if (homework.isEmpty && _homework.isEmpty) return;
if (db) await store(homework);
@@ -60,6 +96,8 @@ class HomeworkProvider with ChangeNotifier {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot store Homework for User null";
String userId = user.id;
await Provider.of<DatabaseProvider>(_context, listen: false).userStore.storeHomework(homework, userId: userId);
await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeHomework(homework, userId: userId);
}
}

View File

@@ -37,10 +37,14 @@ class TimetableProvider with ChangeNotifier {
// for renamed subjects
Future<void> convertBySettings() async {
Map<String, String> renamedSubjects =
(await _database.query.getSettings(_database)).renamedSubjectsEnabled ? await _database.userQuery.renamedSubjects(userId: _user.id!) : {};
(await _database.query.getSettings(_database)).renamedSubjectsEnabled
? await _database.userQuery.renamedSubjects(userId: _user.id!)
: {};
for (Lesson lesson in _lessons.values.expand((e) => e)) {
lesson.subject.renamedTo = renamedSubjects.isNotEmpty ? renamedSubjects[lesson.subject.id] : null;
lesson.subject.renamedTo = renamedSubjects.isNotEmpty
? renamedSubjects[lesson.subject.id]
: null;
}
notifyListeners();
@@ -54,7 +58,8 @@ class TimetableProvider with ChangeNotifier {
User? user = _user.user;
if (user == null) throw "Cannot fetch Lessons for User null";
String iss = user.instituteCode;
List? lessonsJson = await _kreta.getAPI(KretaAPI.timetable(iss, start: week.start, end: week.end));
List? lessonsJson = await _kreta
.getAPI(KretaAPI.timetable(iss, start: week.start, end: week.end));
if (lessonsJson == null) throw "Cannot fetch Lessons for User ${user.id}";
List<Lesson> lessons = lessonsJson.map((e) => Lesson.fromJson(e)).toList();
@@ -72,7 +77,7 @@ class TimetableProvider with ChangeNotifier {
if (user == null) throw "Cannot store Lessons for User null";
String userId = user.id;
// TODO: clear indexes with weeks outside of the current school year
// -TODO: clear indexes with weeks outside of the current school year
await _database.userStore.storeLessons(_lessons, userId: userId);
}

View File

@@ -11,10 +11,7 @@ dependencies:
path: ../filcnaplo/
http: ^0.13.3
provider: ^5.0.0
file_picker:
git:
url: https://github.com/filc/flutter_file_picker.git
ref: master
file_picker: ^5.3.2
dev_dependencies:
flutter_lints: ^1.0.0

View File

@@ -1,9 +1,11 @@
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_kreta_api/models/subject.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_display.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class AbsenceSubjectTile extends StatelessWidget {
const AbsenceSubjectTile(this.subject, {Key? key, this.percentage = 0.0, this.excused = 0, this.unexcused = 0, this.pending = 0, this.onTap})
@@ -19,6 +21,7 @@ class AbsenceSubjectTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
return Material(
type: MaterialType.transparency,
child: ListTile(
@@ -33,7 +36,7 @@ class AbsenceSubjectTile extends StatelessWidget {
subject.renamedTo ?? subject.name.capital(),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15.0, fontStyle: subject.isRenamed ? FontStyle.italic : null),
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15.0, fontStyle: subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: AbsenceDisplay(excused, unexcused, pending),
trailing: Row(

View File

@@ -1,9 +1,11 @@
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_kreta_api/models/absence.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_container.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'absence_tile.i18n.dart';
class AbsenceTile extends StatelessWidget {
@@ -18,6 +20,7 @@ class AbsenceTile extends StatelessWidget {
Widget build(BuildContext context) {
Color color = justificationColor(absence.state, context: context);
bool group = AbsenceGroupContainer.of(context) != null;
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
return Container(
decoration: BoxDecoration(
@@ -66,7 +69,7 @@ class AbsenceTile extends StatelessWidget {
(absence.lessonIndex != null ? "${absence.lessonIndex}. " : "") + (absence.subject.renamedTo ?? absence.subject.name.capital()),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 14.0, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 14.0, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: !group
? Text(
@@ -74,7 +77,7 @@ class AbsenceTile extends StatelessWidget {
maxLines: 2,
overflow: TextOverflow.ellipsis,
// DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date),
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
)
: null,
),

View File

@@ -1,5 +1,6 @@
// ignore_for_file: empty_catches
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_kreta_api/models/absence.dart';
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
@@ -12,6 +13,7 @@ import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:filcnaplo/utils/reverse_search.dart';
import 'package:provider/provider.dart';
import 'absence_view.i18n.dart';
class AbsenceView extends StatelessWidget {
@@ -28,6 +30,7 @@ class AbsenceView extends StatelessWidget {
@override
Widget build(BuildContext context) {
Color color = AbsenceTile.justificationColor(absence.state, context: context);
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
return Padding(
padding: const EdgeInsets.only(bottom: 12.0),
@@ -57,7 +60,7 @@ class AbsenceView extends StatelessWidget {
absence.subject.renamedTo ?? absence.subject.name.capital(),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w700, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
style: TextStyle(fontWeight: FontWeight.w700, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: Text(
absence.teacher,

View File

@@ -1,4 +1,5 @@
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
@@ -6,6 +7,7 @@ import 'package:filcnaplo_mobile_ui/pages/grades/subject_grades_container.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:provider/provider.dart';
import 'certification_tile.i18n.dart';
class CertificationTile extends StatelessWidget {
@@ -20,6 +22,8 @@ class CertificationTile extends StatelessWidget {
bool isSubjectView = SubjectGradesContainer.of(context) != null;
String certificationName;
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
switch (grade.type) {
case GradeType.endYear:
certificationName = "final".i18n;
@@ -78,7 +82,7 @@ class CertificationTile extends StatelessWidget {
title: Text(isSubjectView ? certificationName : grade.subject.renamedTo ?? grade.subject.name.capital(),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 18.0, fontStyle: grade.subject.isRenamed ? FontStyle.italic : null)),
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 18.0, fontStyle: grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null)),
subtitle: Text(grade.value.valueName, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16.0)),
),
),

View File

@@ -1,9 +1,11 @@
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_kreta_api/models/subject.dart';
import 'package:filcnaplo_mobile_ui/common/average_display.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class GradeSubjectTile extends StatelessWidget {
const GradeSubjectTile(this.subject,
@@ -19,10 +21,10 @@ class GradeSubjectTile extends StatelessWidget {
final double average;
final double groupAverage;
final double averageBefore;
@override
Widget build(BuildContext context) {
Color textColor = AppColors.of(context).text;
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
// Failing indicator
if (average < 2.0 && average >= 1.0) {
@@ -54,7 +56,7 @@ class GradeSubjectTile extends StatelessWidget {
fontWeight: FontWeight.w600,
fontSize: 14.0,
color: textColor,
fontStyle: subject.isRenamed ? FontStyle.italic : null),
fontStyle: settingsProvider.renamedSubjectsItalics && subject.isRenamed ? FontStyle.italic : null),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,

View File

@@ -17,6 +17,7 @@ class GradeView extends StatelessWidget {
@override
Widget build(BuildContext context) {
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
return Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: Column(
@@ -29,7 +30,7 @@ class GradeView extends StatelessWidget {
grade.subject.renamedTo ?? grade.subject.name.capital(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: grade.subject.isRenamed ? FontStyle.italic : null),
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: Text(
!Provider.of<SettingsProvider>(context, listen: false).presentationMode ? grade.teacher : "Tanár",

View File

@@ -62,7 +62,9 @@ class NewGradesSurprise extends StatelessWidget {
],
)
: Text(
"new_grades".i18n,
grades.length == 1 ?
"new_grade".i18n :
"new_grades".i18n,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600),

View File

@@ -9,6 +9,7 @@ extension Localization on String {
"rare": "Rare",
"epic": "Epic",
"legendary": "Legendary",
"new_grade": "New grade",
"new_grades": "New grades",
"tap_to_open": "Tap to open now!",
"open_subtitle": "Tap to open...",
@@ -19,6 +20,7 @@ extension Localization on String {
"rare": "Ritka",
"epic": "Epikus",
"legendary": "Legendás",
"new_grade": "Új jegy",
"new_grades": "Új jegyek",
"tap_to_open": "Nyisd ki őket!",
"open_subtitle": "Nyomd meg a kinyitáshoz...",
@@ -29,6 +31,7 @@ extension Localization on String {
"rare": "Selten",
"epic": "Episch",
"legendary": "Legendär",
"new_grade": "Neue Note",
"new_grades": "Neue Noten",
"tap_to_open": "Tippen, um jetzt zu öffnen!",
"open_subtitle": "Antippen zum Öffnen...",

View File

@@ -1,12 +1,16 @@
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_kreta_api/models/homework.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
class HomeworkTile extends StatelessWidget {
const HomeworkTile(this.homework, {Key? key, this.onTap, this.padding, this.censored = false}) : super(key: key);
const HomeworkTile(this.homework,
{Key? key, this.onTap, this.padding, this.censored = false})
: super(key: key);
final Homework homework;
final void Function()? onTap;
@@ -15,6 +19,8 @@ class HomeworkTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
return Material(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(8.0),
@@ -24,7 +30,8 @@ class HomeworkTile extends StatelessWidget {
visualDensity: VisualDensity.compact,
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
onTap: onTap,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
leading: SizedBox(
width: 44,
height: 44,
@@ -38,7 +45,8 @@ class HomeworkTile extends StatelessWidget {
: Padding(
padding: const EdgeInsets.only(top: 2.0),
child: Icon(
SubjectIcon.resolveVariant(subjectName: homework.subjectName, context: context),
SubjectIcon.resolveVariant(
subjectName: homework.subject.name, context: context),
size: 28.0,
color: AppColors.of(context).text.withOpacity(.75),
),
@@ -58,10 +66,10 @@ class HomeworkTile extends StatelessWidget {
],
)
: Text(
homework.subjectName.capital(),
homework.subject.renamedTo ?? homework.subject.name.capital(),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600),
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: homework.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: censored
? Wrap(

View File

@@ -1,4 +1,5 @@
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo_kreta_api/models/homework.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_mobile_ui/common/detail.dart';
@@ -7,6 +8,7 @@ import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_attachment_
import 'package:flutter/material.dart';
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:provider/provider.dart';
import 'homework_view.i18n.dart';
class HomeworkView extends StatelessWidget {
@@ -21,6 +23,7 @@ class HomeworkView extends StatelessWidget {
@override
Widget build(BuildContext context) {
List<Widget> attachmentTiles = [];
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
for (var attachment in homework.attachments) {
attachmentTiles.add(Padding(
@@ -40,14 +43,15 @@ class HomeworkView extends StatelessWidget {
// Header
ListTile(
leading: Icon(
SubjectIcon.resolveVariant(subjectName: homework.subjectName, context: context),
SubjectIcon.resolveVariant(
subjectName: homework.subject.name, context: context),
size: 36.0,
),
title: Text(
homework.subjectName.capital(),
homework.subject.renamedTo ?? homework.subject.name.capital(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600),
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: homework.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: Text(
homework.teacher,
@@ -62,9 +66,13 @@ class HomeworkView extends StatelessWidget {
),
// Details
if (homework.deadline.year != 0) Detail(title: "deadline".i18n, description: homework.deadline.format(context)),
if (homework.deadline.year != 0)
Detail(
title: "deadline".i18n,
description: homework.deadline.format(context)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 6.0),
padding:
const EdgeInsets.symmetric(horizontal: 18.0, vertical: 6.0),
child: SelectableLinkify(
text: homework.content.escapeHtml(),
options: const LinkifyOptions(looseUrl: true, removeWww: true),

View File

@@ -1,9 +1,11 @@
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_kreta_api/models/lesson.dart';
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
import 'package:filcnaplo_mobile_ui/common/detail.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'lesson_view.i18n.dart';
class LessonView extends StatelessWidget {
@@ -16,6 +18,8 @@ class LessonView extends StatelessWidget {
Color accent = Theme.of(context).colorScheme.secondary;
String lessonIndexTrailing = "";
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
if (RegExp(r'\d').hasMatch(lesson.lessonIndex)) lessonIndexTrailing = ".";
if (lesson.substituteTeacher != "") {
@@ -50,7 +54,7 @@ class LessonView extends StatelessWidget {
lesson.subject.renamedTo ?? lesson.subject.name.capital(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null),
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: Text(
lesson.substituteTeacher == "" ? lesson.teacher : lesson.substituteTeacher,

View File

@@ -22,16 +22,22 @@ class MessageViewTile extends StatelessWidget {
UserProvider user = Provider.of<UserProvider>(context, listen: false);
String recipientLabel = "";
if (message.recipients.any((r) => r.name == user.student?.name)) recipientLabel = "me".i18n;
if (message.recipients.any((r) => r.name == user.student?.name)) {
recipientLabel = "me".i18n;
}
if (recipientLabel != "" && message.recipients.length > 1) {
recipientLabel += " +";
recipientLabel += message.recipients.where((r) => r.name != user.student?.name).length.toString();
recipientLabel += message.recipients
.where((r) => r.name != user.student?.name)
.length
.toString();
}
if (recipientLabel == "") {
// note: convertint to set to remove duplicates
recipientLabel += message.recipients.map((r) => r.name).toSet().join(", ");
recipientLabel +=
message.recipients.map((r) => r.name).toSet().join(", ");
}
List<Widget> attachments = [];
@@ -75,9 +81,9 @@ class MessageViewTile extends StatelessWidget {
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
trailing: Row(
trailing: const Row(
mainAxisSize: MainAxisSize.min,
children: const [
children: [
// IconButton(
// onPressed: () {},
// icon: Icon(FeatherIcons.cornerUpLeft, color: AppColors.of(context).text),

View File

@@ -1,4 +1,5 @@
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_kreta_api/models/lesson.dart';
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart';
@@ -6,6 +7,7 @@ import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:provider/provider.dart';
import 'missed_exam_tile.i18n.dart';
class MissedExamView extends StatelessWidget {
@@ -30,6 +32,7 @@ class MissedExamViewTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
return Material(
type: MaterialType.transparency,
child: Padding(
@@ -43,7 +46,7 @@ class MissedExamViewTile extends StatelessWidget {
),
title: Text(
"${lesson.subject.renamedTo ?? lesson.subject.name.capital()}${lesson.date.format(context)}",
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null),
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: Text(
"missed_exam_contact".i18n.fill([lesson.teacher]),

View File

@@ -1,4 +1,5 @@
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo/utils/reverse_search.dart';
@@ -15,6 +16,7 @@ import 'package:flutter/material.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_view.i18n.dart';
import 'package:provider/provider.dart';
class AbsenceSubjectView extends StatelessWidget {
const AbsenceSubjectView(this.subject, {Key? key, this.absences = const []}) : super(key: key);
@@ -54,10 +56,12 @@ class AbsenceSubjectView extends StatelessWidget {
.toList();
List<Widget> absenceTiles = sortDateWidgets(context, dateWidgets: dateWidgets, padding: EdgeInsets.zero, hasShadow: true);
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
return Scaffold(
body: HeroScrollView(
title: subject.renamedTo ?? subject.name.capital(),
italic: subject.isRenamed,
italic: subject.isRenamed && settingsProvider.renamedSubjectsItalics,
icon: SubjectIcon.resolveVariant(subject: subject, context: context),
child: AbsenceSubjectViewContainer(
child: CupertinoScrollbar(

View File

@@ -182,11 +182,15 @@ class _AbsencesPageState extends State<AbsencesPage>
fontWeight: FontWeight.bold),
),
),
bottom: FilterBar(items: [
Tab(text: "Absences".i18n),
Tab(text: "Delays".i18n),
Tab(text: "Misses".i18n),
], controller: _tabController, disableFading: true),
bottom: FilterBar(
items: [
Tab(text: "Absences".i18n),
Tab(text: "Delays".i18n),
Tab(text: "Misses".i18n),
],
controller: _tabController,
disableFading: true,
),
),
],
body: TabBarView(

View File

@@ -1,6 +1,7 @@
import 'dart:math';
import 'package:animations/animations.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:filcnaplo/helpers/average_helper.dart';
@@ -20,9 +21,6 @@ import 'package:filcnaplo_mobile_ui/pages/grades/calculator/grade_calculator_pro
import 'package:filcnaplo_mobile_ui/pages/grades/grades_count.dart';
import 'package:filcnaplo_mobile_ui/pages/grades/graph.dart';
import 'package:filcnaplo_mobile_ui/pages/grades/subject_grades_container.dart';
import 'package:filcnaplo_premium/models/premium_scopes.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
@@ -59,6 +57,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
// Providers
late GradeProvider gradeProvider;
late GradeCalculatorProvider calculatorProvider;
late SettingsProvider settingsProvider;
late double average;
late Widget gradeGraph;
@@ -75,8 +74,9 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
final gradeDates = subjectGrades.map((e) => e.date.millisecondsSinceEpoch);
final maxGradeDate = gradeDates.fold(0, max);
final minGradeDate = gradeDates.fold(0, min);
if (maxGradeDate - minGradeDate < const Duration(days: 5).inMilliseconds)
if (maxGradeDate - minGradeDate < const Duration(days: 5).inMilliseconds) {
return false; // naplo/#78
}
return subjectGrades.where((e) => e.type == GradeType.midYear).length > 1;
}
@@ -151,6 +151,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
Widget build(BuildContext context) {
gradeProvider = Provider.of<GradeProvider>(context);
calculatorProvider = Provider.of<GradeCalculatorProvider>(context);
settingsProvider = Provider.of<SettingsProvider>(context);
List<Grade> subjectGrades = getSubjectGrades(widget.subject).toList();
average = AverageHelper.averageEvals(subjectGrades);
@@ -214,25 +215,25 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
gradeCalc(context);
},
),
FloatingActionButton.small(
child: const Icon(FeatherIcons.flag, size: 20.0),
backgroundColor: Theme.of(context).colorScheme.secondary,
onPressed: () {
if (!Provider.of<PremiumProvider>(context, listen: false)
.hasScope(PremiumScopes.goalPlanner)) {
PremiumLockedFeatureUpsell.show(
context: context, feature: PremiumFeature.goalplanner);
return;
}
// FloatingActionButton.small(
// child: const Icon(FeatherIcons.flag, size: 20.0),
// backgroundColor: Theme.of(context).colorScheme.secondary,
// onPressed: () {
// if (!Provider.of<PremiumProvider>(context, listen: false)
// .hasScope(PremiumScopes.goalPlanner)) {
// PremiumLockedFeatureUpsell.show(
// context: context, feature: PremiumFeature.goalplanner);
// return;
// }
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Hamarosan...")));
// ScaffoldMessenger.of(context).showSnackBar(
// const SnackBar(content: Text("Hamarosan...")));
//Navigator.of(context).push(CupertinoPageRoute(
//builder: (context) => PremiumGoalplannerNewGoalScreen(
// subject: widget.subject)));
},
),
// Navigator.of(context).push(CupertinoPageRoute(
// builder: (context) => PremiumGoalplannerNewGoalScreen(
// subject: widget.subject)));
// },
// ),
],
),
),
@@ -262,7 +263,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
subject: widget.subject, context: context),
scrollController: _scrollController,
title: widget.subject.renamedTo ?? widget.subject.name.capital(),
italic: widget.subject.isRenamed,
italic: settingsProvider.renamedSubjectsItalics && widget.subject.isRenamed,
child: SubjectGradesContainer(
child: CupertinoScrollbar(
child: ListView.builder(
@@ -279,7 +280,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
void gradeCalc(BuildContext context) {
// Scroll to the top of the page
_scrollController.animateTo(75,
_scrollController.animateTo(100,
duration: const Duration(milliseconds: 500), curve: Curves.ease);
calculatorProvider.clear();

View File

@@ -270,6 +270,7 @@ class _GradeGraphState extends State<GradeGraph> {
tData.sort((a, b) => a.writeDate.compareTo(b.writeDate));
return tData.first.writeDate.add(const Duration(days: 120)).isBefore(tData.last.writeDate) ? 2.0 : 1.0;
}(),
checkToShowTitle: (double minValue, double maxValue, SideTitles sideTitles, double appliedInterval, double value) { if (value == maxValue || value == minValue) return false; return true; },
),
leftTitles: SideTitles(
showTitles: true,

View File

@@ -249,6 +249,7 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
Tab(text: "Absences".i18n),
],
controller: _tabController,
disableFading: true,
onTap: (i) async {
int selectedPage =
_pageController.page!.round();

View File

@@ -11,7 +11,7 @@ extension Localization on String {
"happybirthday": "🎂 Happy birthday, %s!",
"merryxmas": "🎄 Merry Christmas, %s!",
"happynewyear": "🎉 Happy New Year, %s!",
"refilcopen": "🎈 Welcome in reFilc, %s!",
"refilcopen": "🎈 reFilc is 1 year old, %s!",
"empty": "Nothing to see here.",
"All": "All",
"Grades": "Grades",
@@ -30,7 +30,7 @@ extension Localization on String {
"happybirthday": "🎂 Boldog születésnapot, %s!",
"merryxmas": "🎄 Boldog Karácsonyt, %s!",
"happynewyear": "🎉 Boldog új évet, %s!",
"refilcopen": "🎈 Üdv a reFilc-ben, %s!",
"refilcopen": "🎈 1 éves a reFilc, %s!",
"empty": "Nincs itt semmi látnivaló.",
"All": "Összes",
"Grades": "Jegyek",
@@ -49,7 +49,7 @@ extension Localization on String {
"happybirthday": "🎂 Alles Gute zum Geburtstag, %s!",
"merryxmas": "🎄 Frohe Weihnachten, %s!",
"happynewyear": "🎉 Frohes neues Jahr, %s!",
"refilcopen": "🎈 Willkommen bei reFilc, %s!",
"refilcopen": "🎈 reFilc ist 1 Jahr alt, %s!",
"empty": "Hier gibt es nichts zu sehen.",
"All": "Alles",
"Grades": "Noten",

View File

@@ -2,7 +2,9 @@ import 'package:animations/animations.dart';
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/icons/filc_icons.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo_mobile_ui/pages/home/live_card/heads_up_countdown.dart';
import 'package:filcnaplo_mobile_ui/screens/summary/summary_screen.dart';
import 'package:flutter/material.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo/api/providers/live_card_provider.dart';
@@ -43,6 +45,7 @@ class _LiveCardState extends State<LiveCard> {
@override
Widget build(BuildContext context) {
liveCard = Provider.of<LiveCardProvider>(context);
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
if (!liveCard.show) return Container();
@@ -50,10 +53,27 @@ class _LiveCardState extends State<LiveCard> {
Duration bellDelay = liveCard.delay;
switch (liveCard.currentState) {
case LiveCardState.summary:
child = LiveCardWidget(
key: const Key('livecard.summary'),
title: '',
icon: FeatherIcons.arrowRight,
description: const Text(''),
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => const SummaryScreen(
currentPage: 'grades',
),
),
),
);
break;
case LiveCardState.morning:
child = LiveCardWidget(
key: const Key('livecard.morning'),
title: DateFormat("EEEE", I18n.of(context).locale.toString()).format(DateTime.now()).capital(),
title: DateFormat("EEEE", I18n.of(context).locale.toString())
.format(DateTime.now())
.capital(),
icon: FeatherIcons.sun,
description: liveCard.nextLesson != null
? Text.rich(
@@ -61,26 +81,40 @@ class _LiveCardState extends State<LiveCard> {
children: [
TextSpan(text: "first_lesson_1".i18n),
TextSpan(
text: liveCard.nextLesson!.subject.renamedTo ?? liveCard.nextLesson!.subject.name.capital(),
text: liveCard.nextLesson!.subject.renamedTo ??
liveCard.nextLesson!.subject.name.capital(),
style: TextStyle(
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary.withOpacity(.85),
fontStyle: liveCard.nextLesson!.subject.isRenamed ? FontStyle.italic : null),
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.85),
fontStyle: liveCard.nextLesson!.subject.isRenamed &&
settingsProvider.renamedSubjectsItalics
? FontStyle.italic
: null),
),
TextSpan(text: "first_lesson_2".i18n),
TextSpan(
text: liveCard.nextLesson!.room.capital(),
style: TextStyle(
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary.withOpacity(.85),
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.85),
),
),
TextSpan(text: "first_lesson_3".i18n),
TextSpan(
text: DateFormat('H:mm').format(liveCard.nextLesson!.start),
text: DateFormat('H:mm')
.format(liveCard.nextLesson!.start),
style: TextStyle(
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary.withOpacity(.85),
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.85),
),
),
TextSpan(text: "first_lesson_4".i18n),
@@ -91,30 +125,48 @@ class _LiveCardState extends State<LiveCard> {
);
break;
case LiveCardState.duringLesson:
final elapsedTime = DateTime.now().difference(liveCard.currentLesson!.start).inSeconds.toDouble() + bellDelay.inSeconds;
final maxTime = liveCard.currentLesson!.end.difference(liveCard.currentLesson!.start).inSeconds.toDouble();
final elapsedTime = DateTime.now()
.difference(liveCard.currentLesson!.start)
.inSeconds
.toDouble() +
bellDelay.inSeconds;
final maxTime = liveCard.currentLesson!.end
.difference(liveCard.currentLesson!.start)
.inSeconds
.toDouble();
final showMinutes = maxTime - elapsedTime > 60;
child = LiveCardWidget(
key: const Key('livecard.duringLesson'),
leading: liveCard.currentLesson!.lessonIndex + (RegExp(r'\d').hasMatch(liveCard.currentLesson!.lessonIndex) ? "." : ""),
title: liveCard.currentLesson!.subject.renamedTo ?? liveCard.currentLesson!.subject.name.capital(),
leading: liveCard.currentLesson!.lessonIndex +
(RegExp(r'\d').hasMatch(liveCard.currentLesson!.lessonIndex)
? "."
: ""),
title: liveCard.currentLesson!.subject.renamedTo ??
liveCard.currentLesson!.subject.name.capital(),
titleItalic: liveCard.currentLesson!.subject.isRenamed,
subtitle: liveCard.currentLesson!.room,
icon: SubjectIcon.resolveVariant(subject: liveCard.currentLesson!.subject, context: context),
description: liveCard.currentLesson!.description != "" ? Text(liveCard.currentLesson!.description) : null,
nextSubject: liveCard.nextLesson?.subject.renamedTo ?? liveCard.nextLesson?.subject.name.capital(),
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed ?? false,
icon: SubjectIcon.resolveVariant(
subject: liveCard.currentLesson!.subject, context: context),
description: liveCard.currentLesson!.description != ""
? Text(liveCard.currentLesson!.description)
: null,
nextSubject: liveCard.nextLesson?.subject.renamedTo ??
liveCard.nextLesson?.subject.name.capital(),
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed == true &&
settingsProvider.renamedSubjectsItalics,
nextRoom: liveCard.nextLesson?.room,
progressMax: showMinutes ? maxTime / 60 : maxTime,
progressCurrent: showMinutes ? elapsedTime / 60 : elapsedTime,
progressAccuracy: showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
progressAccuracy:
showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
onProgressTap: () {
showDialog(
barrierColor: Colors.black,
context: context,
builder: (context) => HeadsUpCountdown(maxTime: maxTime, elapsedTime: elapsedTime),
builder: (context) =>
HeadsUpCountdown(maxTime: maxTime, elapsedTime: elapsedTime),
);
},
);
@@ -129,8 +181,15 @@ class _LiveCardState extends State<LiveCard> {
final diff = liveCard.getFloorDifference();
final maxTime = liveCard.nextLesson!.start.difference(liveCard.prevLesson!.end).inSeconds.toDouble();
final elapsedTime = DateTime.now().difference(liveCard.prevLesson!.end).inSeconds.toDouble() + bellDelay.inSeconds.toDouble();
final maxTime = liveCard.nextLesson!.start
.difference(liveCard.prevLesson!.end)
.inSeconds
.toDouble();
final elapsedTime = DateTime.now()
.difference(liveCard.prevLesson!.end)
.inSeconds
.toDouble() +
bellDelay.inSeconds.toDouble();
final showMinutes = maxTime - elapsedTime > 60;
@@ -139,14 +198,21 @@ class _LiveCardState extends State<LiveCard> {
title: "break".i18n,
icon: iconFloorMap[diff],
description: liveCard.nextLesson!.room != liveCard.prevLesson!.room
? Text("go $diff".i18n.fill([diff != "to room" ? (liveCard.nextLesson!.getFloor() ?? 0) : liveCard.nextLesson!.room]))
? Text("go $diff".i18n.fill([
diff != "to room"
? (liveCard.nextLesson!.getFloor() ?? 0)
: liveCard.nextLesson!.room
]))
: Text("stay".i18n),
nextSubject: liveCard.nextLesson?.subject.renamedTo ?? liveCard.nextLesson?.subject.name.capital(),
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed ?? false,
nextSubject: liveCard.nextLesson?.subject.renamedTo ??
liveCard.nextLesson?.subject.name.capital(),
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed == true &&
settingsProvider.renamedSubjectsItalics,
nextRoom: diff != "to room" ? liveCard.nextLesson?.room : null,
progressMax: showMinutes ? maxTime / 60 : maxTime,
progressCurrent: showMinutes ? elapsedTime / 60 : elapsedTime,
progressAccuracy: showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
progressAccuracy:
showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
onProgressTap: () {
showDialog(
barrierColor: Colors.black,
@@ -162,14 +228,18 @@ class _LiveCardState extends State<LiveCard> {
case LiveCardState.afternoon:
child = LiveCardWidget(
key: const Key('livecard.afternoon'),
title: DateFormat("EEEE", I18n.of(context).locale.toString()).format(DateTime.now()).capital(),
title: DateFormat("EEEE", I18n.of(context).locale.toString())
.format(DateTime.now())
.capital(),
icon: FeatherIcons.coffee,
);
break;
case LiveCardState.night:
child = LiveCardWidget(
key: const Key('livecard.night'),
title: DateFormat("EEEE", I18n.of(context).locale.toString()).format(DateTime.now()).capital(),
title: DateFormat("EEEE", I18n.of(context).locale.toString())
.format(DateTime.now())
.capital(),
icon: FeatherIcons.moon,
);
break;

View File

@@ -9,6 +9,7 @@ enum ProgressAccuracy { minutes, seconds }
class LiveCardWidget extends StatefulWidget {
const LiveCardWidget({
Key? key,
this.isEvent = false,
this.leading,
this.title,
this.titleItalic = false,
@@ -22,8 +23,10 @@ class LiveCardWidget extends StatefulWidget {
this.progressMax,
this.progressAccuracy = ProgressAccuracy.minutes,
this.onProgressTap,
this.onTap,
}) : super(key: key);
final bool isEvent;
final String? leading;
final String? title;
final bool titleItalic;
@@ -37,6 +40,7 @@ class LiveCardWidget extends StatefulWidget {
final double? progressMax;
final ProgressAccuracy? progressAccuracy;
final Function()? onProgressTap;
final Function()? onTap;
@override
State<LiveCardWidget> createState() => _LiveCardWidgetState();
@@ -51,6 +55,7 @@ class _LiveCardWidgetState extends State<LiveCardWidget> {
onLongPressDown: (_) => setState(() => hold = true),
onLongPressEnd: (_) => setState(() => hold = false),
onLongPressCancel: () => setState(() => hold = false),
onTap: widget.onTap,
child: AnimatedScale(
scale: hold ? 1.03 : 1.0,
curve: Curves.easeInOutBack,
@@ -73,171 +78,292 @@ class _LiveCardWidgetState extends State<LiveCardWidget> {
padding: const EdgeInsets.symmetric(horizontal: 6.0),
child: OverflowBox(
maxHeight: 96.0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
child: widget.isEvent
? Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
if (widget.leading != null)
Padding(
padding: const EdgeInsets.only(right: 12.0, top: 8.0),
child: Text(
widget.leading!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary,
),
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
children: [
if (widget.title != null)
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(text: widget.title!, style: TextStyle(fontStyle: widget.titleItalic ? FontStyle.italic : null)),
if (widget.subtitle != null)
WidgetSpan(
child: Container(
margin: const EdgeInsets.only(left: 6.0, bottom: 3.0),
padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 2.0),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary.withOpacity(.3),
borderRadius: BorderRadius.circular(4.0),
),
child: Text(
widget.subtitle!,
style: TextStyle(
height: 1.2,
fontSize: 14.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary,
),
),
),
),
],
),
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 22.0),
maxLines: 1,
softWrap: false,
overflow: TextOverflow.ellipsis,
),
),
if (widget.title != null) const SizedBox(width: 6.0),
if (widget.icon != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Icon(
widget.icon,
size: 26.0,
color: AppColors.of(context).text.withOpacity(.75),
),
),
],
),
if (widget.description != null)
DefaultTextStyle(
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16.0,
height: 1.0,
color: AppColors.of(context).text.withOpacity(.75),
),
maxLines: !(widget.nextSubject == null && widget.progressCurrent == null && widget.progressMax == null) ? 1 : 2,
softWrap: false,
overflow: TextOverflow.ellipsis,
child: widget.description!,
),
],
Text(
widget.title ?? 'Esemény',
style: TextStyle(
fontSize: 32,
fontStyle:
widget.titleItalic ? FontStyle.italic : null,
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
widget.description ??
const Text('Nincs leírás megadva.'),
SizedBox(
height: 15,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(0.5),
borderRadius: const BorderRadius.all(
Radius.circular(10),
),
),
child: Padding(
padding: const EdgeInsets.all(4),
child: Icon(widget.icon),
),
),
),
],
),
],
),
),
if (!(widget.nextSubject == null && widget.progressCurrent == null && widget.progressMax == null))
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Row(
children: [
if (widget.nextSubject != null) const Icon(FeatherIcons.arrowRight, size: 12.0),
if (widget.nextSubject != null) const SizedBox(width: 4.0),
if (widget.nextSubject != null)
Expanded(
child: Text.rich(
TextSpan(
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.leading != null)
Padding(
padding: const EdgeInsets.only(
right: 12.0, top: 8.0),
child: Text(
widget.leading!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.colorScheme
.secondary,
),
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
TextSpan(
text: widget.nextSubject!, style: TextStyle(fontStyle: widget.nextSubjectItalic ? FontStyle.italic : null)),
if (widget.nextRoom != null)
WidgetSpan(
child: Container(
margin: const EdgeInsets.only(left: 4.0),
padding: const EdgeInsets.symmetric(horizontal: 3.0, vertical: 1.5),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary.withOpacity(.25),
borderRadius: BorderRadius.circular(4.0),
),
child: Text(
widget.nextRoom!,
style: TextStyle(
height: 1.1,
fontSize: 11.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary.withOpacity(.9),
Row(
children: [
if (widget.title != null)
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: widget.title!,
style: TextStyle(
fontStyle: widget
.titleItalic
? FontStyle.italic
: null)),
if (widget.subtitle != null)
WidgetSpan(
child: Container(
margin: const EdgeInsets
.only(
left: 6.0,
bottom: 3.0),
padding:
const EdgeInsets
.symmetric(
horizontal: 4.0,
vertical: 2.0),
decoration:
BoxDecoration(
color: Theme.of(
context)
.colorScheme
.secondary
.withOpacity(.3),
borderRadius:
BorderRadius
.circular(
4.0),
),
child: Text(
widget.subtitle!,
style: TextStyle(
height: 1.2,
fontSize: 14.0,
fontWeight:
FontWeight.w600,
color: Theme.of(
context)
.colorScheme
.secondary,
),
),
),
),
],
),
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 22.0),
maxLines: 1,
softWrap: false,
overflow: TextOverflow.ellipsis,
),
),
),
if (widget.title != null)
const SizedBox(width: 6.0),
if (widget.icon != null)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 4.0),
child: Icon(
widget.icon,
size: 26.0,
color: AppColors.of(context)
.text
.withOpacity(.75),
),
),
],
),
if (widget.description != null)
DefaultTextStyle(
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16.0,
height: 1.0,
color: AppColors.of(context)
.text
.withOpacity(.75),
),
maxLines:
!(widget.nextSubject == null &&
widget.progressCurrent ==
null &&
widget.progressMax == null)
? 1
: 2,
softWrap: false,
overflow: TextOverflow.ellipsis,
child: widget.description!,
),
],
),
style: TextStyle(
color: AppColors.of(context).text.withOpacity(.8),
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: false,
),
),
if (widget.nextRoom == null && widget.nextSubject == null) const Spacer(),
if (widget.progressCurrent != null && widget.progressMax != null)
GestureDetector(
onTap: widget.onProgressTap,
child: Container(
color: Colors.transparent,
child: Text(
"remaining ${widget.progressAccuracy == ProgressAccuracy.minutes ? 'min' : 'sec'}"
.plural((widget.progressMax! - widget.progressCurrent!).round()),
maxLines: 1,
style: TextStyle(
fontWeight: FontWeight.w500,
color: AppColors.of(context).text.withOpacity(.75),
],
),
),
if (!(widget.nextSubject == null &&
widget.progressCurrent == null &&
widget.progressMax == null))
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Row(
children: [
if (widget.nextSubject != null)
const Icon(FeatherIcons.arrowRight,
size: 12.0),
if (widget.nextSubject != null)
const SizedBox(width: 4.0),
if (widget.nextSubject != null)
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: widget.nextSubject!,
style: TextStyle(
fontStyle:
widget.nextSubjectItalic
? FontStyle.italic
: null)),
if (widget.nextRoom != null)
WidgetSpan(
child: Container(
margin: const EdgeInsets.only(
left: 4.0),
padding:
const EdgeInsets.symmetric(
horizontal: 3.0,
vertical: 1.5),
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.25),
borderRadius:
BorderRadius.circular(
4.0),
),
child: Text(
widget.nextRoom!,
style: TextStyle(
height: 1.1,
fontSize: 11.0,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.9),
),
),
),
),
],
),
style: TextStyle(
color: AppColors.of(context)
.text
.withOpacity(.8),
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: false,
),
),
),
),
)
],
),
if (widget.nextRoom == null &&
widget.nextSubject == null)
const Spacer(),
if (widget.progressCurrent != null &&
widget.progressMax != null)
GestureDetector(
onTap: widget.onProgressTap,
child: Container(
color: Colors.transparent,
child: Text(
"remaining ${widget.progressAccuracy == ProgressAccuracy.minutes ? 'min' : 'sec'}"
.plural((widget.progressMax! -
widget.progressCurrent!)
.round()),
maxLines: 1,
style: TextStyle(
fontWeight: FontWeight.w500,
color: AppColors.of(context)
.text
.withOpacity(.75),
),
),
),
)
],
),
),
if (widget.progressCurrent != null &&
widget.progressMax != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: ProgressBar(
value: widget.progressCurrent! /
widget.progressMax!),
)
],
),
if (widget.progressCurrent != null && widget.progressMax != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: ProgressBar(value: widget.progressCurrent! / widget.progressMax!),
)
],
),
),
),
),

View File

@@ -90,12 +90,16 @@ class _MessagesPageState extends State<MessagesPage>
fontWeight: FontWeight.bold),
),
),
bottom: FilterBar(items: [
Tab(text: "Inbox".i18n),
Tab(text: "Sent".i18n),
Tab(text: "Trash".i18n),
Tab(text: "Draft".i18n),
], controller: tabController),
bottom: FilterBar(
items: [
Tab(text: "Inbox".i18n),
Tab(text: "Sent".i18n),
Tab(text: "Trash".i18n),
Tab(text: "Draft".i18n),
],
controller: tabController,
disableFading: true,
),
),
],
body: TabBarView(

View File

@@ -199,7 +199,7 @@ class _TimetablePageState extends State<TimetablePage>
snap: false,
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
actions: [
PremiumFSTimetableButton(controller: _controller),
PremiumFSTimetableButton(controller: _controller, tabcontroller: _tabController),
// Profile Icon
Padding(

View File

@@ -8,18 +8,21 @@ extension Localization on String {
"empty": "No school this week!",
"week": "Week",
"error": "Failed to fetch timetable!",
"empty_timetable": "Timetable is empty!",
},
"hu_hu": {
"timetable": "Órarend",
"empty": "Ezen a héten nincs iskola.",
"week": "Hét",
"error": "Nem sikerült lekérni az órarendet!",
"empty_timetable": "Az órarend üres!",
},
"de_de": {
"timetable": "Zeitplan",
"empty": "Keine Schule diese Woche.",
"week": "Woche",
"error": "Der Fahrplan konnte nicht abgerufen werden!",
"empty_timetable": "Der Zeitplan ist blank!",
},
};

View File

@@ -12,14 +12,16 @@ class PremiumButton extends StatefulWidget {
State<PremiumButton> createState() => _PremiumButtonState();
}
class _PremiumButtonState extends State<PremiumButton> with TickerProviderStateMixin {
class _PremiumButtonState extends State<PremiumButton>
with TickerProviderStateMixin {
late final AnimationController _animation;
bool _heldDown = false;
@override
void initState() {
super.initState();
_animation = AnimationController(vsync: this, duration: const Duration(seconds: 3));
_animation =
AnimationController(vsync: this, duration: const Duration(seconds: 3));
_animation.repeat();
}
@@ -38,7 +40,8 @@ class _PremiumButtonState extends State<PremiumButton> with TickerProviderStateM
transitionType: ContainerTransitionType.fadeThrough,
openElevation: 0,
closedElevation: 0,
closedShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
closedShape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
openBuilder: (context, _) => const PremiumScreen(),
closedBuilder: (context, action) => GestureDetector(
onTapDown: (_) => setState(() => _heldDown = true),
@@ -57,16 +60,20 @@ class _PremiumButtonState extends State<PremiumButton> with TickerProviderStateM
child: ClipRRect(
borderRadius: BorderRadius.circular(14.0),
child: ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
imageFilter:
ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
height: 70,
decoration: BoxDecoration(
gradient: SweepGradient(colors: const [
Colors.blue,
Colors.orange,
Colors.purple,
Colors.blue,
], transform: GradientRotation(_animation.value * 6.283185)),
gradient: SweepGradient(
colors: const [
Colors.blue,
Colors.orange,
Colors.purple,
Colors.blue,
],
transform: GradientRotation(
_animation.value * 6.283185)),
),
),
),
@@ -92,9 +99,9 @@ class _PremiumButtonState extends State<PremiumButton> with TickerProviderStateM
Color(0xff1EA18F),
]),
),
child: Row(
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
children: [
Icon(FilcIcons.premium, color: Colors.white),
SizedBox(width: 12.0),
Text(

View File

@@ -21,8 +21,9 @@ class PremiumScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final middleColor =
Theme.of(context).brightness == Brightness.dark ? const Color.fromARGB(255, 20, 57, 46) : const Color.fromARGB(255, 10, 140, 123);
final middleColor = Theme.of(context).brightness == Brightness.dark
? const Color.fromARGB(255, 20, 57, 46)
: const Color.fromARGB(255, 10, 140, 123);
final future = FilcAPI.getSupporters();
@@ -78,7 +79,8 @@ class PremiumScreen extends StatelessWidget {
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
padding:
const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -87,16 +89,25 @@ class PremiumScreen extends StatelessWidget {
const SizedBox(height: 12.0),
const Text(
"Még több filc.",
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 25.0, color: Colors.white),
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 25.0,
color: Colors.white),
),
const Text(
"reFilc Premium.",
style: TextStyle(fontWeight: FontWeight.w800, fontSize: 35.0, color: Colors.white),
style: TextStyle(
fontWeight: FontWeight.w800,
fontSize: 35.0,
color: Colors.white),
),
const SizedBox(height: 15.0),
Text(
"Támogasd a filcet, és szerezz cserébe pár kényelmes jutalmat!",
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20, color: Colors.white.withOpacity(.8)),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20,
color: Colors.white.withOpacity(.8)),
),
const SizedBox(height: 25.0),
SupportersButton(supporters: future),
@@ -110,43 +121,61 @@ class PremiumScreen extends StatelessWidget {
),
),
SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 24.0).add(const EdgeInsets.only(bottom: 100)),
padding: const EdgeInsets.symmetric(horizontal: 24.0)
.add(const EdgeInsets.only(bottom: 100)),
sliver: SliverToBoxAdapter(
child: Column(
children: [
PremiumPlanCard(
icon: const Icon(FilcIcons.kupak),
title: Text("Kupak", style: TextStyle(foreground: GradientStyles.kupakPaint)),
title: Text("Kupak",
style: TextStyle(
foreground: GradientStyles.kupakPaint)),
gradient: GradientStyles.kupak,
price: 2,
description: const Text("Szabd személyre a filcet és láss részletesebb statisztikákat."),
url: "https://github.com/sponsors/filc/sponsorships?tier_id=238453&preview=true",
active: ActiveSponsorCard.estimateLevel(context.watch<PremiumProvider>().scopes) == PremiumFeatureLevel.kupak,
description: const Text(
"Szabd személyre a filcet és láss részletesebb statisztikákat."),
url:
"https://github.com/sponsors/filc/sponsorships?tier_id=238453&preview=true",
active: ActiveSponsorCard.estimateLevel(
context.watch<PremiumProvider>().scopes) ==
PremiumFeatureLevel.kupak,
),
const SizedBox(height: 8.0),
PremiumPlanCard(
icon: const Icon(FilcIcons.tinta),
title: Text("Tinta", style: TextStyle(foreground: GradientStyles.tintaPaint)),
title: Text("Tinta",
style: TextStyle(
foreground: GradientStyles.tintaPaint)),
gradient: GradientStyles.tinta,
price: 5,
description: const Text("Kényelmesebb órarend, asztali alkalmazás és célok kitűzése."),
url: "https://github.com/sponsors/filc/sponsorships?tier_id=238454&preview=true",
active: ActiveSponsorCard.estimateLevel(context.watch<PremiumProvider>().scopes) == PremiumFeatureLevel.tinta,
description: const Text(
"Kényelmesebb órarend, asztali alkalmazás és célok kitűzése."),
url:
"https://github.com/sponsors/filc/sponsorships?tier_id=238454&preview=true",
active: ActiveSponsorCard.estimateLevel(
context.watch<PremiumProvider>().scopes) ==
PremiumFeatureLevel.tinta,
),
const SizedBox(height: 12.0),
PremiumGoalCard(progress: snapshot.data?.progress ?? 0, target: snapshot.data?.max ?? 1),
PremiumGoalCard(
progress: snapshot.data?.progress ?? 0,
target: snapshot.data?.max ?? 1),
const SizedBox(height: 12.0),
const GithubConnectButton(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 14.0).add(const EdgeInsets.only(top: 12.0)),
child: Row(
children: const [
padding: const EdgeInsets.symmetric(vertical: 14.0)
.add(const EdgeInsets.only(top: 12.0)),
child: const Row(
children: [
Icon(FilcIcons.kupak),
SizedBox(width: 12.0),
Expanded(
child: Text(
"Kupak jutalmak",
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20),
),
),
],
@@ -154,40 +183,50 @@ class PremiumScreen extends StatelessWidget {
),
PremiumRewardCard(
imageKey: "premium_nickname_showcase",
icon: SvgPicture.asset("assets/images/nickname_icon.svg", color: Theme.of(context).iconTheme.color),
icon: SvgPicture.asset(
"assets/images/nickname_icon.svg",
color: Theme.of(context).iconTheme.color),
title: const Text("Profil személyre szabás"),
description: const Text("Állíts be egy saját becenevet és egy profilképet (akár animáltat is!)"),
description: const Text(
"Állíts be egy saját becenevet és egy profilképet (akár animáltat is!)"),
),
const SizedBox(height: 14.0),
PremiumRewardCard(
imageKey: "premium_theme_showcase",
icon: SvgPicture.asset("assets/images/theme_icon.svg", color: Theme.of(context).iconTheme.color),
icon: SvgPicture.asset("assets/images/theme_icon.svg",
color: Theme.of(context).iconTheme.color),
title: const Text("Téma+"),
description: const Text("Válassz saját háttérszínt és kártyaszínt is, akár saját HEX-kóddal!"),
description: const Text(
"Válassz saját háttérszínt és kártyaszínt is, akár saját HEX-kóddal!"),
),
const SizedBox(height: 14.0),
PremiumRewardCard(
imageKey: "premium_stats_showcase",
icon: SvgPicture.asset("assets/images/stats_icon.svg", color: Theme.of(context).iconTheme.color),
icon: SvgPicture.asset("assets/images/stats_icon.svg",
color: Theme.of(context).iconTheme.color),
title: const Text("Részletes jegy statisztika"),
description: const Text("Válassz heti, havi és háromhavi időtartam közül, és pontosan lásd, mennyi jegyed van."),
description: const Text(
"Válassz heti, havi és háromhavi időtartam közül, és pontosan lásd, mennyi jegyed van."),
),
const SizedBox(height: 14.0),
const PremiumRewardCard(
title: Text("Még pár dolog..."),
description:
Text("🔣\tVálassz ikon témát\n\tPrémium rang és csevegő a discord szerverünkön\n📬\tElsőbbségi segítségnyújtás"),
description: Text(
"🔣\tVálassz ikon témát\n\tPrémium rang és csevegő a discord szerverünkön\n📬\tElsőbbségi segítségnyújtás"),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 14.0).add(const EdgeInsets.only(top: 12.0)),
child: Row(
children: const [
padding: const EdgeInsets.symmetric(vertical: 14.0)
.add(const EdgeInsets.only(top: 12.0)),
child: const Row(
children: [
Icon(FilcIcons.tinta),
SizedBox(width: 12.0),
Expanded(
child: Text(
"Tinta jutalmak",
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20),
),
),
],
@@ -195,48 +234,62 @@ class PremiumScreen extends StatelessWidget {
),
PremiumRewardCard(
imageKey: "premium_timetable_showcase",
icon: SvgPicture.asset("assets/images/timetable_icon.svg", color: Theme.of(context).iconTheme.color),
icon: SvgPicture.asset(
"assets/images/timetable_icon.svg",
color: Theme.of(context).iconTheme.color),
title: const Text("Heti órarend nézet"),
description:
const Text("Egy órarend, ami a teljes képernyődet kihasználja, csak nem olyan idegesítő, mint az eKRÉTA féle."),
description: const Text(
"Egy órarend, ami a teljes képernyődet kihasználja, csak nem olyan idegesítő, mint az eKRÉTA féle."),
),
const SizedBox(height: 14.0),
PremiumRewardCard(
imageKey: "premium_widget_showcase",
icon: SvgPicture.asset("assets/images/widget_icon.svg", color: Theme.of(context).iconTheme.color),
icon: SvgPicture.asset(
"assets/images/widget_icon.svg",
color: Theme.of(context).iconTheme.color),
title: const Text("Widget"),
description: const Text("Mindig lásd, milyen órád lesz, a kezdőképernyőd kényelméből."),
description: const Text(
"Mindig lásd, milyen órád lesz, a kezdőképernyőd kényelméből."),
),
const SizedBox(height: 14.0),
PremiumRewardCard(
soon: true,
imageKey: "premium_goal_showcase",
icon: SvgPicture.asset("assets/images/goal_icon.svg", color: Theme.of(context).iconTheme.color),
icon: SvgPicture.asset("assets/images/goal_icon.svg",
color: Theme.of(context).iconTheme.color),
title: const Text("Cél követés"),
description: const Text("Add meg, mi a célod, és mi majd kiszámoljuk, hogyan juthatsz oda!"),
description: const Text(
"Add meg, mi a célod, és mi majd kiszámoljuk, hogyan juthatsz oda!"),
),
const SizedBox(height: 14.0),
PremiumRewardCard(
soon: true,
imageKey: "premium_desktop_showcase",
icon: SvgPicture.asset("assets/images/desktop_icon.svg", color: Theme.of(context).iconTheme.color),
icon: SvgPicture.asset(
"assets/images/desktop_icon.svg",
color: Theme.of(context).iconTheme.color),
title: const Text("Asztali verzió"),
description: const Text("Érd el a reFilcet a gépeden is, és menekülj meg a csúnya felhasználói felületektől!"),
description: const Text(
"Érd el a reFilcet a gépeden is, és menekülj meg a csúnya felhasználói felületektől!"),
),
const SizedBox(height: 14.0),
const PremiumRewardCard(
title: Text("Még pár dolog..."),
description: Text("🖋️\tMinden kupak jutalom\n\tKorai hozzáférés új verziókhoz"),
description: Text(
"🖋️\tMinden kupak jutalom\n\tKorai hozzáférés új verziókhoz"),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 14.0).add(const EdgeInsets.only(top: 12.0)),
child: Row(
children: const [
padding: const EdgeInsets.symmetric(vertical: 14.0)
.add(const EdgeInsets.only(top: 12.0)),
child: const Row(
children: [
SizedBox(width: 12.0),
Expanded(
child: Text(
"Mire vársz még?",
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20),
),
),
],
@@ -244,20 +297,24 @@ class PremiumScreen extends StatelessWidget {
),
GithubCard(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return const PremiumActivationView();
}));
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 14.0).add(const EdgeInsets.only(top: 12.0)),
child: Row(
children: const [
padding: const EdgeInsets.symmetric(vertical: 14.0)
.add(const EdgeInsets.only(top: 12.0)),
child: const Row(
children: [
SizedBox(width: 12.0),
Expanded(
child: Text(
"Gyakori kérdések",
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20),
),
),
],

View File

@@ -2,7 +2,8 @@ import 'package:filcnaplo_kreta_api/models/school.dart';
import 'package:flutter/material.dart';
class SchoolInputTile extends StatelessWidget {
const SchoolInputTile({Key? key, required this.school, this.onTap}) : super(key: key);
const SchoolInputTile({Key? key, required this.school, this.onTap})
: super(key: key);
final School school;
final Function()? onTap;

View File

@@ -110,7 +110,9 @@ class NavigationScreenState extends State<NavigationScreen>
requiredNetworkType: NetworkType.ANY), (String taskId) async {
// <-- Event handler
// This is the fetch-event callback.
print("[BackgroundFetch] Event received $taskId");
if (kDebugMode) {
print("[BackgroundFetch] Event received $taskId");
}
// IMPORTANT: You must signal completion of your task or the OS can punish your app
// for taking too long in the background.
@@ -118,10 +120,14 @@ class NavigationScreenState extends State<NavigationScreen>
}, (String taskId) async {
// <-- Task timeout handler.
// This task has exceeded its allowed running-time. You must stop what you're doing and immediately .finish(taskId)
print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
if (kDebugMode) {
print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
}
BackgroundFetch.finish(taskId);
});
print('[BackgroundFetch] configure success: $status');
if (kDebugMode) {
print('[BackgroundFetch] configure success: $status');
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
@@ -133,7 +139,6 @@ class NavigationScreenState extends State<NavigationScreen>
void initState() {
super.initState();
initPlatformState();
HomeWidget.setAppGroupId('hu.refilc.naplo.group');
@@ -173,7 +178,9 @@ class NavigationScreenState extends State<NavigationScreen>
@override
void didChangePlatformBrightness() {
if (settings.theme == ThemeMode.system) {
// ignore: deprecated_member_use
Brightness? brightness =
// ignore: deprecated_member_use
WidgetsBinding.instance.window.platformBrightness;
Provider.of<ThemeModeObserver>(context, listen: false).changeTheme(
brightness == Brightness.light ? ThemeMode.light : ThemeMode.dark);

View File

@@ -6,6 +6,7 @@ import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
import 'package:filcnaplo_mobile_ui/screens/news/news_tile.dart';
import 'package:filcnaplo/models/news.dart';
import 'package:filcnaplo_mobile_ui/screens/news/news_view.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:filcnaplo/api/providers/news_provider.dart';
@@ -24,24 +25,28 @@ class NewsScreen extends StatelessWidget {
appBar: AppBar(
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
leading: BackButton(color: AppColors.of(context).text),
title: Text("News", style: TextStyle(color: AppColors.of(context).text)),
title: Text("news".i18n,
style: TextStyle(color: AppColors.of(context).text)),
),
body: SafeArea(
child: RefreshIndicator(
onRefresh: () => newsProvider.fetch(),
child: ListView.builder(
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
itemCount: max(news.length, 1),
itemBuilder: (context, index) {
if (news.isNotEmpty) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 12.0),
child: Panel(
child: Material(
type: MaterialType.transparency,
child: NewsTile(
news[index],
onTap: () => NewsView.show(news[index], context: context, force: true),
onTap: () => NewsView.show(news[index],
context: context, force: true),
),
),
),

View File

@@ -30,7 +30,11 @@ import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/theme.dart';
class SettingsHelper {
static const Map<String, String> langMap = {"en": "🇬🇧 English", "hu": "🇭🇺 Magyar", "de": "🇩🇪 Deutsch"};
static const Map<String, String> langMap = {
"en": "🇬🇧 English",
"hu": "🇭🇺 Magyar",
"de": "🇩🇪 Deutsch"
};
static const Map<Pages, String> pageTitle = {
Pages.home: "home",
@@ -47,9 +51,11 @@ class SettingsHelper {
VibrationStrength.strong: "vstrong",
};
static Map<Pages, String> localizedPageTitles() => pageTitle.map((key, value) => MapEntry(key, ScreensLocalization(value).i18n));
static Map<Pages, String> localizedPageTitles() => pageTitle
.map((key, value) => MapEntry(key, ScreensLocalization(value).i18n));
static Map<VibrationStrength, String> localizedVibrationTitles() =>
vibrationTitle.map((key, value) => MapEntry(key, SettingsLocalization(value).i18n));
vibrationTitle
.map((key, value) => MapEntry(key, SettingsLocalization(value).i18n));
static void language(BuildContext context) {
showBottomSheetMenu(
@@ -58,7 +64,8 @@ class SettingsHelper {
String lang = langMap.keys.toList()[index];
return BottomSheetMenuItem(
onPressed: () {
Provider.of<SettingsProvider>(context, listen: false).update(language: lang);
Provider.of<SettingsProvider>(context, listen: false)
.update(language: lang);
I18n.of(context).locale = Locale(lang, lang.toUpperCase());
Navigator.of(context).maybePop();
if (Platform.isAndroid || Platform.isIOS) {
@@ -81,6 +88,19 @@ class SettingsHelper {
);
}
static void uwuMode(BuildContext context, value) {
final settings = Provider.of<SettingsProvider>(context, listen: false);
if (value) {
I18n.of(context).locale = const Locale('uw', 'UW');
} else {
I18n.of(context).locale =
Locale(settings.language, settings.language.toUpperCase());
}
if (Platform.isAndroid || Platform.isIOS) {
setupQuickActions();
}
}
static void iconPack(BuildContext context) {
final settings = Provider.of<SettingsProvider>(context, listen: false);
showBottomSheetMenu(
@@ -122,16 +142,20 @@ class SettingsHelper {
items: List.generate(Pages.values.length, (index) {
return BottomSheetMenuItem(
onPressed: () {
Provider.of<SettingsProvider>(context, listen: false).update(startPage: Pages.values[index]);
Provider.of<SettingsProvider>(context, listen: false)
.update(startPage: Pages.values[index]);
Navigator.of(context).maybePop();
},
title: Row(
children: [
Icon(pageIcons[Pages.values[index]], size: 20.0, color: Theme.of(context).colorScheme.secondary),
Icon(pageIcons[Pages.values[index]],
size: 20.0, color: Theme.of(context).colorScheme.secondary),
const SizedBox(width: 16.0),
Text(localizedPageTitles()[Pages.values[index]] ?? ""),
const Spacer(),
if (Pages.values[index] == Provider.of<SettingsProvider>(context, listen: false).startPage)
if (Pages.values[index] ==
Provider.of<SettingsProvider>(context, listen: false)
.startPage)
Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.secondary,
@@ -165,7 +189,8 @@ class SettingsHelper {
children: [
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(FeatherIcons.smartphone, size: 20.0, color: Theme.of(context).colorScheme.secondary),
child: Icon(FeatherIcons.smartphone,
size: 20.0, color: Theme.of(context).colorScheme.secondary),
),
Text(SettingsLocalization("system").i18n),
const Spacer(),
@@ -183,7 +208,8 @@ class SettingsHelper {
children: [
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(FeatherIcons.sun, size: 20.0, color: Theme.of(context).colorScheme.secondary),
child: Icon(FeatherIcons.sun,
size: 20.0, color: Theme.of(context).colorScheme.secondary),
),
Text(SettingsLocalization("light").i18n),
const Spacer(),
@@ -201,7 +227,8 @@ class SettingsHelper {
children: [
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(FeatherIcons.moon, size: 20.0, color: Theme.of(context).colorScheme.secondary),
child: Icon(FeatherIcons.moon,
size: 20.0, color: Theme.of(context).colorScheme.secondary),
),
Text(SettingsLocalization("dark").i18n),
const Spacer(),
@@ -219,7 +246,8 @@ class SettingsHelper {
static void accentColor(BuildContext context) {
Navigator.of(context, rootNavigator: true).push(
PageRouteBuilder(
pageBuilder: (context, _, __) => const PremiumCustomAccentColorSetting(),
pageBuilder: (context, _, __) =>
const PremiumCustomAccentColorSetting(),
transitionDuration: Duration.zero,
reverseTransitionDuration: Duration.zero,
),
@@ -241,7 +269,8 @@ class SettingsHelper {
return BottomSheetMenuItem(
onPressed: () {
Provider.of<SettingsProvider>(context, listen: false).update(vibrate: value);
Provider.of<SettingsProvider>(context, listen: false)
.update(vibrate: value);
Navigator.of(context).maybePop();
},
title: Row(
@@ -250,14 +279,18 @@ class SettingsHelper {
width: 12.0,
height: 12.0,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary.withOpacity((index + 1) / (vibrationTitle.length + 1)),
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity((index + 1) / (vibrationTitle.length + 1)),
shape: BoxShape.circle,
),
),
const SizedBox(width: 16.0),
Text(localizedVibrationTitles()[value] ?? "?"),
const Spacer(),
if (value == Provider.of<SettingsProvider>(context, listen: false).vibrate)
if (value ==
Provider.of<SettingsProvider>(context, listen: false).vibrate)
Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.secondary,
@@ -291,7 +324,8 @@ class _RoundingSettingState extends State<RoundingSetting> {
@override
void initState() {
super.initState();
rounding = Provider.of<SettingsProvider>(context, listen: false).rounding / 10;
rounding =
Provider.of<SettingsProvider>(context, listen: false).rounding / 10;
}
@override
@@ -336,12 +370,14 @@ class _RoundingSettingState extends State<RoundingSetting> {
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("4.5", style: TextStyle(fontSize: 26.0, fontWeight: FontWeight.w500)),
const Text("4.5",
style: TextStyle(fontSize: 26.0, fontWeight: FontWeight.w500)),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Icon(FeatherIcons.arrowRight, color: Colors.grey),
),
GradeValueWidget(GradeValue(roundingResult, "", "", 100), fill: true, size: 32.0),
GradeValueWidget(GradeValue(roundingResult, "", "", 100),
fill: true, size: 32.0),
],
),
Padding(
@@ -349,7 +385,8 @@ class _RoundingSettingState extends State<RoundingSetting> {
child: MaterialActionButton(
child: Text(SettingsLocalization("done").i18n),
onPressed: () {
Provider.of<SettingsProvider>(context, listen: false).update(rounding: (rounding * 10).toInt());
Provider.of<SettingsProvider>(context, listen: false)
.update(rounding: (rounding * 10).toInt());
Navigator.of(context).maybePop();
},
),
@@ -367,15 +404,24 @@ class BellDelaySetting extends StatefulWidget {
State<BellDelaySetting> createState() => _BellDelaySettingState();
}
class _BellDelaySettingState extends State<BellDelaySetting> with SingleTickerProviderStateMixin {
class _BellDelaySettingState extends State<BellDelaySetting>
with SingleTickerProviderStateMixin {
late TabController _tabController;
late Duration currentDelay;
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this, initialIndex: Provider.of<SettingsProvider>(context, listen: false).bellDelay > 0 ? 1 : 0);
currentDelay = Duration(seconds: Provider.of<SettingsProvider>(context, listen: false).bellDelay);
_tabController = TabController(
length: 2,
vsync: this,
initialIndex:
Provider.of<SettingsProvider>(context, listen: false).bellDelay > 0
? 1
: 0);
currentDelay = Duration(
seconds:
Provider.of<SettingsProvider>(context, listen: false).bellDelay);
}
@override
@@ -416,7 +462,10 @@ class _BellDelaySettingState extends State<BellDelaySetting> with SingleTickerPr
),
Text(SettingsLocalization("sync_help").i18n,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12.0, fontWeight: FontWeight.w500, color: AppColors.of(context).text.withOpacity(.75))),
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w500,
color: AppColors.of(context).text.withOpacity(.75))),
Padding(
padding: const EdgeInsets.only(bottom: 12.0, top: 6.0),
child: Column(
@@ -425,21 +474,26 @@ class _BellDelaySettingState extends State<BellDelaySetting> with SingleTickerPr
backgroundColor: AppColors.of(context).filc,
child: Text(SettingsLocalization("sync").i18n),
onPressed: () {
final lessonProvider = Provider.of<TimetableProvider>(context, listen: false);
final lessonProvider =
Provider.of<TimetableProvider>(context, listen: false);
Duration? closest;
DateTime now = DateTime.now();
for (var lesson in lessonProvider.getWeek(Week.current()) ?? []) {
for (var lesson
in lessonProvider.getWeek(Week.current()) ?? []) {
Duration sdiff = lesson.start.difference(now);
Duration ediff = lesson.end.difference(now);
if (closest == null || sdiff.abs() < closest.abs()) closest = sdiff;
if (closest == null || sdiff.abs() < closest.abs()) {
closest = sdiff;
}
if (ediff.abs() < closest.abs()) closest = ediff;
}
if (closest != null) {
if (closest.inHours.abs() >= 1) return;
currentDelay = closest;
Provider.of<SettingsProvider>(context, listen: false).update(bellDelay: currentDelay.inSeconds);
Provider.of<SettingsProvider>(context, listen: false)
.update(bellDelay: currentDelay.inSeconds);
_tabController.index = currentDelay.inSeconds > 0 ? 1 : 0;
setState(() {});
}
@@ -449,7 +503,8 @@ class _BellDelaySettingState extends State<BellDelaySetting> with SingleTickerPr
child: Text(SettingsLocalization("done").i18n),
onPressed: () {
//Provider.of<SettingsProvider>(context, listen: false).update(context, rounding: (r * 10).toInt());
Provider.of<SettingsProvider>(context, listen: false).update(bellDelay: currentDelay.inSeconds);
Provider.of<SettingsProvider>(context, listen: false)
.update(bellDelay: currentDelay.inSeconds);
Navigator.of(context).maybePop();
},
),
@@ -513,8 +568,11 @@ class _GradeColorsSettingState extends State<GradeColorsSetting> {
children: [
MaterialActionButton(
onPressed: () {
List<Color> colors = List.castFrom(settings.gradeColors);
var defaultColors = SettingsProvider.defaultSettings().gradeColors;
List<Color> colors =
List.castFrom(settings.gradeColors);
var defaultColors =
SettingsProvider.defaultSettings()
.gradeColors;
colors[index] = defaultColors[index];
settings.update(gradeColors: colors);
Navigator.of(context).maybePop();
@@ -523,9 +581,11 @@ class _GradeColorsSettingState extends State<GradeColorsSetting> {
),
MaterialActionButton(
onPressed: () {
List<Color> colors = List.castFrom(settings.gradeColors);
List<Color> colors =
List.castFrom(settings.gradeColors);
colors[index] = currentColor.withAlpha(255);
settings.update(gradeColors: settings.gradeColors);
settings.update(
gradeColors: settings.gradeColors);
Navigator.of(context).maybePop();
},
child: Text(SettingsLocalization("done").i18n),
@@ -536,7 +596,8 @@ class _GradeColorsSettingState extends State<GradeColorsSetting> {
]),
).then((value) => setState(() {}));
},
child: GradeValueWidget(GradeValue(index + 1, "", "", 0), fill: true, size: 36.0),
child: GradeValueWidget(GradeValue(index + 1, "", "", 0),
fill: true, size: 36.0),
),
),
);

View File

@@ -51,11 +51,10 @@ class SettingsScreen extends StatefulWidget {
class _SettingsScreenState extends State<SettingsScreen>
with SingleTickerProviderStateMixin {
int devmodeCountdown = 3;
int devmodeCountdown = 5;
bool __ss = false; // secret settings
late Future<Map> futureRelease =
Provider.of<UpdateProvider>(context).installedVersion();
Future<Map>? futureRelease;
late UserProvider user;
late UpdateProvider updateProvider;
@@ -85,34 +84,37 @@ class _SettingsScreenState extends State<SettingsScreen>
String _firstName;
List<String> _nameParts = user.displayName?.split(" ") ?? ["?"];
List<String> _nameParts = account.displayName.split(" ");
if (!settings.presentationMode) {
_firstName = _nameParts.length > 1 ? _nameParts[1] : _nameParts[0];
} else {
_firstName = "János";
}
accountTiles.add(AccountTile(
name: Text(!settings.presentationMode ? account.name : "János",
style: const TextStyle(fontWeight: FontWeight.w500)),
username:
Text(!settings.presentationMode ? account.username : "01234567890"),
profileImage: ProfileImage(
name: _firstName,
backgroundColor: Theme.of(context)
.colorScheme
.primary, //!settings.presentationMode
//? ColorUtils.stringToColor(account.name)
//: Theme.of(context).colorScheme.secondary,
role: account.role,
accountTiles.add(
AccountTile(
name: Text(!settings.presentationMode ? account.name : "János",
style: const TextStyle(fontWeight: FontWeight.w500)),
username: Text(
!settings.presentationMode ? account.username : "01234567890"),
profileImage: ProfileImage(
name: _firstName,
role: account.role,
profilePictureString: account.picture,
backgroundColor: Theme.of(context)
.colorScheme
.primary, //!settings.presentationMode
//? ColorUtils.stringToColor(account.name)
//: Theme.of(context).colorScheme.secondary,
),
onTap: () {
user.setUser(account.id);
restore().then((_) => user.setUser(account.id));
Navigator.of(context).pop();
},
onTapMenu: () => _showBottomSheet(account),
),
onTap: () {
user.setUser(account.id);
restore().then((_) => user.setUser(account.id));
Navigator.of(context).pop();
},
onTapMenu: () => _showBottomSheet(account),
));
);
});
}
@@ -153,7 +155,9 @@ class _SettingsScreenState extends State<SettingsScreen>
@override
void initState() {
super.initState();
futureRelease = Provider.of<UpdateProvider>(context).installedVersion();
Future.delayed(Duration.zero, () {
futureRelease = Provider.of<UpdateProvider>(context, listen: false).installedVersion();
});
_hideContainersController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 200));
}
@@ -433,6 +437,62 @@ class _SettingsScreenState extends State<SettingsScreen>
activeColor: Theme.of(context).colorScheme.secondary,
),
),
Material(
type: MaterialType.transparency,
child: SwitchListTile(
value: settings.notificationsEnabled,
activeColor: Theme.of(context).colorScheme.secondary,
contentPadding: const EdgeInsets.only(left: 12.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: Row(children: [
Icon(FeatherIcons.messageSquare,
color: settings.notificationsEnabled ? Theme.of(context).colorScheme.secondary : AppColors.of(context).text.withOpacity(.25)),
const SizedBox(width: 14.0),
Text(
"notifications".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(
settings.notificationsEnabled ? 1.0 : .5),
fontWeight: FontWeight.w600,
fontSize: 16.0,
),
),
const SizedBox(
width: 5,
),
SizedBox(
height: 30,
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
child: Padding(
padding:
const EdgeInsets.only(left: 10, right: 10),
child: Center(
child: Text("BETA",
style: TextStyle(
fontSize: 9.1,
color: AppColors.of(context)
.text
.withOpacity(
settings.notificationsEnabled
? 1.0
: .5),
fontWeight: FontWeight.w600,
overflow: TextOverflow.ellipsis))),
),
decoration: BoxDecoration(
color: settings.notificationsEnabled
? Theme.of(context).colorScheme.secondary
: AppColors.of(context).text.withOpacity(.25),
borderRadius: BorderRadius.circular(40)),
),
)
]),
onChanged: (value) =>
settings.update(notificationsEnabled: value),
),
),
],
),
),
@@ -528,14 +588,34 @@ class _SettingsScreenState extends State<SettingsScreen>
contentPadding: const EdgeInsets.only(left: 12.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: const Text("Presentation Mode",
style: TextStyle(fontWeight: FontWeight.w500)),
title: Text("presentation".i18n,
style:
const TextStyle(fontWeight: FontWeight.w500)),
onChanged: (v) =>
settings.update(presentationMode: v),
value: settings.presentationMode,
activeColor: Theme.of(context).colorScheme.secondary,
),
),
// UwU-fied mode (why????)
// Material(
// type: MaterialType.transparency,
// child: SwitchListTile(
// contentPadding: const EdgeInsets.only(left: 12.0),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(12.0)),
// title: Text("uwufymode".i18n,
// style:
// const TextStyle(fontWeight: FontWeight.w500)),
// onChanged: (v) {
// SettingsHelper.uwuMode(context, v);
// setState(() {});
// },
// value: settings.presentationMode,
// activeColor: Theme.of(context).colorScheme.secondary,
// ),
// ),
],
),
),
@@ -613,7 +693,7 @@ class _SettingsScreenState extends State<SettingsScreen>
? Theme.of(context).colorScheme.secondary
: AppColors.of(context).text.withOpacity(.25),
),
const SizedBox(width: 24.0),
const SizedBox(width: 14.0),
Expanded(
child: Text(
"graph_class_avg".i18n,
@@ -658,7 +738,7 @@ class _SettingsScreenState extends State<SettingsScreen>
? Theme.of(context).colorScheme.secondary
: AppColors.of(context).text.withOpacity(.25),
),
const SizedBox(width: 24.0),
const SizedBox(width: 14.0),
Expanded(
child: Text(
"news".i18n,
@@ -703,7 +783,7 @@ class _SettingsScreenState extends State<SettingsScreen>
? Theme.of(context).colorScheme.secondary
: AppColors.of(context).text.withOpacity(.25),
),
const SizedBox(width: 24.0),
const SizedBox(width: 14.0),
Expanded(
child: Text(
"surprise_grades".i18n,
@@ -833,7 +913,7 @@ class _SettingsScreenState extends State<SettingsScreen>
padding: const EdgeInsets.symmetric(
vertical: 12.0, horizontal: 24.0),
child: Panel(
title: const Text("Developer Settings"),
title: Text("devsettings".i18n),
child: Column(
children: [
Material(
@@ -842,8 +922,8 @@ class _SettingsScreenState extends State<SettingsScreen>
contentPadding: const EdgeInsets.only(left: 12.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: const Text("Developer Mode",
style: TextStyle(fontWeight: FontWeight.w500)),
title: Text("devmode".i18n,
style: const TextStyle(fontWeight: FontWeight.w500)),
onChanged: (v) =>
settings.update(developerMode: false),
value: settings.developerMode,
@@ -852,7 +932,7 @@ class _SettingsScreenState extends State<SettingsScreen>
),
PanelButton(
leading: const Icon(FeatherIcons.copy),
title: const Text("Copy JWT"),
title: Text("copy_jwt".i18n),
onPressed: () => Clipboard.setData(ClipboardData(
text:
Provider.of<KretaClient>(context, listen: false)
@@ -897,6 +977,9 @@ class _SettingsScreenState extends State<SettingsScreen>
child: Text("v${release.data!['version']}"),
);
} else {
String envAppVer = const String.fromEnvironment(
"APPVER",
defaultValue: "?");
return DefaultTextStyle(
style: Theme.of(context)
.textTheme
@@ -906,7 +989,7 @@ class _SettingsScreenState extends State<SettingsScreen>
color: AppColors.of(context)
.text
.withOpacity(0.65)),
child: const Text("v?"),
child: Text("v$envAppVer"),
);
}
},
@@ -916,13 +999,13 @@ class _SettingsScreenState extends State<SettingsScreen>
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
duration: const Duration(milliseconds: 200),
content: Text(
"You are $devmodeCountdown taps away from Developer Mode."),
"devmoretaps".i18n.fill([devmodeCountdown])),
));
setState(() => devmodeCountdown--);
} else if (devmodeCountdown == 0) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text("Developer Mode successfully activated."),
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("devactivated".i18n),
));
settings.update(developerMode: true);

Some files were not shown because too many files have changed in this diff Show More