Compare commits

...

125 Commits

Author SHA1 Message Date
kima
9d9f99a955 fixed long subject names in grades summary 2023-06-25 14:55:01 +02:00
kima
2c5939fab4 fixed misaligned text on final sum page 2023-06-25 14:50:54 +02:00
kima
95ed503e53 fixed 0 min delay thingies 2023-06-25 14:47:06 +02:00
kima
51e2c63134 fixed navigation and ui bugs in summary 2023-06-25 14:36:18 +02:00
kima
50bc03f403 updated version string 2023-06-25 00:11:44 +02:00
kima
f5bc16ba42 created translation for personality card 2023-06-24 23:58:06 +02:00
kima
f4501ce251 finished translation for summary 2023-06-24 21:41:18 +02:00
kima
3317472773 fixed end-year average 2023-06-23 11:20:18 +02:00
kima
acdd47a49a fixed style bug and added reveal check to buttons 2023-06-23 01:17:26 +02:00
kima
fa8be89aaf fixed some issues and added gallery save feature 2023-06-23 01:06:48 +02:00
kima
5034af2fb4 finished animations and started reveal/share 2023-06-23 00:54:56 +02:00
kima
87842de421 finished personality card style 2023-06-22 23:33:28 +02:00
kima
1f46a0509f fixed summary crash error 2023-06-22 21:55:57 +02:00
kima
8e9713e436 almost finished personality page 2023-06-22 21:12:34 +02:00
kima
fe0a7d81ae finished personalities map 2023-06-22 20:52:45 +02:00
kima
f9e14349b7 Merge branch 'master' of github.com:refilc/naplo 2023-06-22 19:44:09 +02:00
kima
a1f087758f started summary personality page 2023-06-22 19:15:02 +02:00
kima
4068abdb95 finished final summary style and fixed thingies 2023-06-22 16:45:57 +02:00
kima
9314c613bc added lessons/misses page and final summary page 2023-06-22 15:59:57 +02:00
Márton Kiss
458e93e19f Merge pull request #25 from Monke14/master
fix italics toggle bugs
2023-06-20 17:44:42 +02:00
hihihaha
ba8d26d250 fix italics toggle bugs 2023-06-20 16:19:49 +02:00
Márton Kiss
33e3495d9f Merge pull request #23 from Monke14/master
fix high network usage
2023-06-20 10:28:27 +02:00
Márton Kiss
f71e72e443 Merge pull request #24 from PredatorPotatoX/master
Contribution guide update
2023-06-20 10:26:28 +02:00
hihihaha
c615a33bd2 make variable private 2023-06-19 22:34:53 +02:00
PredatorPotatoX
7ad0ea26e2 Contributing guide update 2023-06-19 21:04:32 +02:00
hihihaha
0ad663beb3 fix network activity 2023-06-18 17:36:01 +02:00
kima
1366984c15 added start page to summary 2023-06-17 21:30:48 +02:00
kima
62d3895373 added next page button to summary 2023-06-17 20:41:30 +02:00
kima
3579c4e821 fixed grades page ui in summary 2023-06-17 20:04:11 +02:00
Kima
5c39865d40 commit 2023-06-17 16:37:24 +02:00
Kima
551b2849fe grade page done in summary 2023-06-16 01:30:17 +02:00
Márton Kiss
ce1c5eb0d8 fixed nonce login error 2023-06-15 09:33:36 +02:00
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
125 changed files with 4275 additions and 819 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,39 +1,36 @@
# Contributing Guide
# Contribution guide
Köszönjük, ha programozással segíted a munkánkat!
A folytatáshoz szükséged lesz egy Linuxot vagy Windowst futtató számítógépre, minimális programozási tapasztalatra és egy kis angoltudásra.
Segít, ha nem csak kicsit tudsz programozni, és ha ismered a Gitet és a GitHubot ;)
A folytatáshoz szükséged lesz egy Linux-ot vagy Windows-t futtató számítógépre, minimális programozási tapasztalatra és egy kis angoltudásra.
Segít, ha már gyakorlottabb vagy a programozásban, és ha ismered a [Git](https://git-scm.com/) és a [GitHub](https://github.com/) működését. ;)
## Miben segítsek?
Kérünk, **olyan dologgal járulj hozzá** a reFilchez, ami valószínűleg **sok embernek hasznos lesz** majd. Szeretnénk egy minél teljeskörűbb iskolai asszisztenst létrehozni, de az iskolaspecifikus, vagy külön neked hasznos funkciók helye inkább legyen a saját forkod.
Kérünk, **olyan dologgal járulj hozzá** a **reFilc**hez, ami valószínűleg **sok embernek hasznos lehet**. Szeretnénk egy minél teljeskörűbb iskolai asszisztenst létrehozni, de az iskolaspecifikus, vagy külön neked hasznos funkciók helye inkább legyen a saját Fork-od.
Fontos, hogy **mielőtt egy nagy volumenű projektbe belekezdesz, futtasd meg ötletedet a [Discord szerverünkön](https://dc.refilc.hu/),** ahol még azelőtt tudunk tanácsot adni, mielőtt sok-sok órát beleöltél volna egy esetleg felesleges dologba.
Fontos, hogy **mielőtt egy nagyobb méretű projektbe belekezdenél, futtasd meg ötletedet a [Discord szerverünkön](https://dc.refilc.hu/)**, ahol még azelőtt tudunk tanácsot adni, hogy sok-sok órát beleöltél volna egy esetleg felesleges dologba.
A legjobban annak örülünk, ha az [Issues](https://github.com/refilc/filcnaplo/issues) oldalról szemezgetsz, **ha lehet, a [priority taggel megjelöltekkel kezdd](https://github.com/refilc/filcnaplo/issues?q=is%3Aissue+is%3Aopen+label%3Apriority),** vagy ha új vagy a Flutterhez, ajánljuk figyelmedbe [ezeket a viszonylag könnyen javítható hibákat](https://github.com/refilc/filcnaplo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) (ha épp van ilyen).
A legjobban annak örülünk, ha az [Issues](https://github.com/refilc/naplo/issues) oldalról szemezgetsz. Ha még új vagy a Flutterben, ajánljuk figyelmedbe ezeket a [viszonylag könnyen javítható hibákat](https://github.com/refilc/naplo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22), ha éppen van ilyen.
## Hogyan segítsek?
Nem ígérhetünk itt sem programozás-, sem Git-kurzust, de a projektspecifikus dolgokat leírjuk, és segítünk a Flutter telepítésében.
Nem ígérhetünk itt sem programozás-, sem git-kurzust, de a projektspecifikus dolgokat leírjuk, és segítünk a Flutter feltelepítésében.
A **reFilc** a Google által pár éve létrehozott **[Flutter](https://flutter.dev/)** keretrendszert használja, aminek nyelve a **[Dart](https://dart.dev/)**. Ha ismered a C#, Java, C++, vagy egyéb hasonló programnyelvek működését, **nem fog nagy gondot okozni a használata.** A felhasználói felület létrehozásában az is segíthet, ha foglalkoztál már korábban weboldalakkal vagy alkalmazásfejlesztéssel.
Ha még nem használtad a Flutter-t, mindenképp böngészd át a [YouTube csatornájukat](https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw).
Kód vagy UI teszteléséhez Flutter telepítése nélkül is használhatod a [DartPad](https://dartpad.dev/)-et.
A reFilc a Google által pár éve létrehozott **[Fluttert](https://flutter.dev/)** használja, aminek nyelve a **[Dart](https://dart.dev/)**. Ha ismered a C#-ot, Javát, C++t, vagy egyéb hasonló nyelvet, **nem fog gondot okozni a használata.** A felhasználói felület létrehozásában az is segíthet, ha foglalkoztál már korábban weboldalakkal.
Ha még nem használtál Fluttert, mindenképp böngészd át a [YouTube csatornájukat](https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw).
Könnyen tudsz kódot, vagy akár UI-t is tesztelni a [DartPad](https://dartpad.dev/) oldalon.
#### [Segítség a Flutter telepítéséhez](https://docs.flutter.dev/get-started/install)
**Használd a Flutter stable verzióját!** Írd be a terminálba: `flutter channel stable`
#### [Segítség a Flutter telepítéséhez és a forráskód futtatásához](/.github/SETUP.md)
Fontos: **Legyél a flutter beta verzióján!** Írd be: `flutter channel beta`
Ha nem értessz a Git-hez vagy a GitHub-hoz, ajánljuk figyelmedbe [ezt a cikket](https://medium.com/envienta-magyarorsz%C3%A1g/git-%C3%A9s-github-gyorstalpal%C3%B3-f2d78a732deb), viszont arra kérünk, hogy a használatukat ne a **reFilc**en próbáld ki először. Hozz létre egy saját Repo-t és abban tesztelgess. Ha már nagyjából kitapasztaltad, várjuk hozzájárulásodat.
Ha nem értesz a Githez, ajánljuk figyelmedbe [ezt a cikket](https://medium.com/envienta-magyarorsz%C3%A1g/git-%C3%A9s-github-gyorstalpal%C3%B3-f2d78a732deb). Viszont arra kérünk, a Git használatát ne a reFilcen próbáld ki először, hozz létre előbb egy saját Repót, és abba tesztelgess. Ha már nagyjából kitapasztaltad, várjuk hozzájárulásodat.
Készíts egy forkot a saját fiókod alá.
A reFilc legfrissebb, épp fejlesztés alatt álló verzióját a [dev brancen](https://github.com/refilc/filcnaplo/tree/dev) találod, kérjük ide commitolj, és ide célozd a forkodból a Pull Requested. Írd le benne, mit változtattál, és ha lehet, csatolj képernyőképet is.
Minél gyakrabban készíts minél részletesebben elnevezett commitokat, hogy el tudjunk tájékozódni az általad beküldött kódon.
Készíts egy Fork-ot a saját GitHub fiókod alá.
A **reFilc** legfrissebb, **épp fejlesztés alatt álló verzióját a [master branch](https://github.com/refilc/naplo/tree/master)-en találod**. Kérjük ide Commit-olj és ide célozd a Fork-odból a Pull Request-edet. Írd le benne, hogy mit változtattál és ha lehet, csatolj képernyőképet is.
Minél gyakrabban készíts minél részletesebben elnevezett Commit-okat, hogy mások is el tudjanak igazodni az általad beküldött kódban.
---
Az általad fejlesztett funkciók mellé a changelogban odakerül GitHub felhasználóneved.
Ha jelentős és rendszeres hozzájáruló vagy, Discordon megkapod a `DEV` rangot.
Az általad fejlesztett funkciók mellé a Changelog-ba odakerül a GitHub felhasználóneved.
Ha jelentős és rendszeres hozzájáruló vagy, Discord-on megkaphatod a `DEV` rangot.
Ha bárhol elakadtál, keress minket Discordon.
Jó fejlesztést kívánunk!
Ha bárhol elakadtál vagy kérdésed van, keress bátran Discordon!
**Jó fejlesztést kívánunk!**

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?&logo=github&label=Downloads)](https://github.com/refilc/naplo/releases) &nbsp; [![Discord](https://img.shields.io/discord/1111649116020285532?logo=discord&label=Discord)](https://dc.refilc.hu)
## Setup
@@ -29,14 +29,16 @@ flutter run
### Contribution
**Nézd meg a [Contribution guide](CONTRIBUTING.md)-ot!**
Az összes (ugyan azon verzióhoz tartozó) contribution meg fog jelenni a release-nél. Kérjük, írd le a Discord nevedet a Description-be, hogy adhassunk rangot.
-------
# Kudo
# Developers
**annon:** a Filc napló készítője (ez az app a Filcen alapul)
**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

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 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

@@ -0,0 +1,15 @@
// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=/Users/kima/development/flutter
FLUTTER_APPLICATION_PATH=/Users/kima/Documents/refilc/app/naplo/filcnaplo
COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_TARGET=/Users/kima/Documents/refilc/app/naplo/filcnaplo/lib/main.dart
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=4.1.0
FLUTTER_BUILD_NUMBER=213
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386
EXCLUDED_ARCHS[sdk=iphoneos*]=armv7
DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9XRUJfQ0FOVkFTS0lUX1VSTD1odHRwczovL3d3dy5nc3RhdGljLmNvbS9mbHV0dGVyLWNhbnZhc2tpdC8yYTM0MDFjOWJiYjVhOWE5YWVjNzRkNGY3MzVkMThhOWRkM2ViZjJkLw==
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=true
TREE_SHAKE_ICONS=false
PACKAGE_CONFIG=/Users/kima/Documents/refilc/app/naplo/filcnaplo/.dart_tool/package_config.json

View File

@@ -0,0 +1,14 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/kima/development/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/kima/Documents/refilc/app/naplo/filcnaplo"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_TARGET=/Users/kima/Documents/refilc/app/naplo/filcnaplo/lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=4.1.0"
export "FLUTTER_BUILD_NUMBER=213"
export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9XRUJfQ0FOVkFTS0lUX1VSTD1odHRwczovL3d3dy5nc3RhdGljLmNvbS9mbHV0dGVyLWNhbnZhc2tpdC8yYTM0MDFjOWJiYjVhOWE5YWVjNzRkNGY3MzVkMThhOWRkM2ViZjJkLw=="
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=/Users/kima/Documents/refilc/app/naplo/filcnaplo/.dart_tool/package_config.json"

View File

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

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

View File

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

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

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

@@ -49,14 +49,14 @@ Future loginApi({
}) async {
Provider.of<KretaClient>(context, listen: false).userAgent =
Provider.of<SettingsProvider>(context, listen: false).config.userAgent;
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded",
};
String nonceStr = await Provider.of<KretaClient>(context, listen: false)
.getAPI(KretaAPI.nonce, json: false);
Nonce nonce = getNonce(nonceStr, username, instituteCode);
headers.addAll(nonce.header());

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;
@@ -32,24 +40,48 @@ class LiveCardProvider extends ChangeNotifier {
String? _latestActivityId;
Map<String, String> _lastActivity = {};
bool _hasCheckedTimetable = false;
LiveCardProvider({
required TimetableProvider timetable,
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 +110,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 +144,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,42 +168,66 @@ 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);
if (today.isEmpty) {
if (today.isEmpty && !_hasCheckedTimetable) {
_hasCheckedTimetable = true;
await _timetable.fetch(week: Week.current());
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 +235,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 +245,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;
@@ -177,7 +255,10 @@ class LiveCardProvider extends ChangeNotifier {
}
}
if (currentLesson != null) {
if (now.isBefore(DateTime(now.year, DateTime.august, 31)) &&
now.isAfter(DateTime(now.year, DateTime.june, 14))) {
currentState = LiveCardState.summary;
} else if (currentLesson != null) {
currentState = LiveCardState.duringLesson;
} else if (nextLesson != null && prevLesson != null) {
currentState = LiveCardState.duringBreak;
@@ -198,7 +279,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

@@ -0,0 +1,21 @@
class Personality {
PersonalityType type;
Personality({
this.type = PersonalityType.npc,
});
}
enum PersonalityType {
geek,
sick,
late,
quitter,
healthy,
acceptable,
fallible,
average,
diligent,
cheater,
npc
}

View File

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

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.1+215
environment:
sdk: ">=2.17.0 <3.0.0"
@@ -38,7 +38,7 @@ dependencies:
ref: master
path_provider: ^2.0.2
permission_handler: ^10.2.0
share_plus: ^5.0.0
share_plus: ^7.0.2
connectivity_plus: ^4.0.1
flutter_displaymode: ^0.6.0
quick_actions: ^1.0.1
@@ -48,17 +48,14 @@ dependencies:
crypto: ^3.0.2
elegant_notification: ^1.6.1
flutter_feather_icons: ^2.0.0+1
live_activities: ^1.0.0
live_activities: ^1.7.4
animated_flip_counter: ^0.2.5
lottie: ^1.4.3
rive: ^0.9.1
animated_background: ^2.0.0
dropdown_button2: ^1.8.9
home_widget: ^0.1.6
flutter_expandable_fab:
git:
url: https://github.com/filc/flutter_expandable_fab
ref: master
flutter_expandable_fab: ^1.8.1
uni_links: ^0.5.1
url_launcher: ^6.1.6
workmanager: ^0.5.1
@@ -67,7 +64,8 @@ dependencies:
animations: ^2.0.1
background_fetch: ^1.1.5
flutter_local_notifications: ^14.1.0
package_info_plus: ^3.1.2
package_info_plus: ^4.0.2
screenshot: ^2.1.0
dev_dependencies:
flutter_lints: ^2.0.1
@@ -148,12 +146,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

@@ -23,7 +23,6 @@ class Empty extends StatelessWidget {
@override
Widget build(BuildContext context) {
// make the face randomness a bit more constant (to avoid strokes)
int index = Random(DateTime.now().minute).nextInt(faces.length);
return Center(
@@ -32,9 +31,19 @@ class Empty extends StatelessWidget {
child: Text.rich(
TextSpan(
text: faces[index],
style: TextStyle(fontSize: 32.0, fontWeight: FontWeight.w500, color: AppColors.of(context).text.withOpacity(.75)),
style: TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.w500,
color: AppColors.of(context).text.withOpacity(.75)),
children: subtitle != null
? [TextSpan(text: "\n" + subtitle!, style: TextStyle(fontSize: 18.0, height: 2.0, color: AppColors.of(context).text.withOpacity(.5)))]
? [
TextSpan(
text: "\n" + subtitle!,
style: TextStyle(
fontSize: 18.0,
height: 2.0,
color: AppColors.of(context).text.withOpacity(.5)))
]
: [],
),
textAlign: TextAlign.center,

View File

@@ -0,0 +1,68 @@
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart';
class EmptyCard extends StatefulWidget {
const EmptyCard({
Key? key,
required this.text,
}) : super(key: key);
final String text;
@override
State<EmptyCard> createState() => _EmptyCardState();
}
class _EmptyCardState extends State<EmptyCard> {
bool hold = false;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onLongPressDown: (_) => setState(() => hold = true),
onLongPressEnd: (_) => setState(() => hold = false),
onLongPressCancel: () => setState(() => hold = false),
child: AnimatedScale(
scale: hold ? 1.018 : 1.0,
curve: Curves.easeInOutBack,
duration: const Duration(milliseconds: 300),
child: Container(
height: 444,
padding:
const EdgeInsets.only(top: 12, bottom: 12, left: 12, right: 12),
decoration: BoxDecoration(
color: const Color(0x280008FF),
borderRadius: const BorderRadius.all(Radius.circular(5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
offset: const Offset(0, 5),
blurRadius: 20,
spreadRadius: 10,
),
],
),
child: DottedBorder(
color: Colors.black.withOpacity(0.9),
dashPattern: const [12, 12],
padding:
const EdgeInsets.only(top: 20, bottom: 20, left: 20, right: 20),
child: Center(
child: Text(
widget.text,
style: TextStyle(
color: Colors.white.withOpacity(0.9),
),
),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,374 @@
import 'package:dotted_border/dotted_border.dart';
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/helpers/average_helper.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/models/personality.dart';
import 'package:filcnaplo_kreta_api/models/absence.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo_kreta_api/models/lesson.dart';
import 'package:filcnaplo_kreta_api/models/subject.dart';
import 'package:filcnaplo_kreta_api/models/week.dart';
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'personality_card.i18n.dart';
class PersonalityCard extends StatefulWidget {
const PersonalityCard({
Key? key,
required this.user,
}) : super(key: key);
final UserProvider user;
@override
State<PersonalityCard> createState() => _PersonalityCardState();
}
class _PersonalityCardState extends State<PersonalityCard> {
late GradeProvider gradeProvider;
late AbsenceProvider absenceProvider;
late TimetableProvider timetableProvider;
late SettingsProvider settings;
late List<int> subjectAvgsList = [];
late Map<Subject, double> subjectAvgs = {};
late double subjectAvg;
late List<Grade> classWorkGrades;
late Map<int, int> mostCommonGrade;
late List<Absence> absences = [];
final Map<Subject, Lesson> _lessonCount = {};
late int totalDelays;
late int unexcusedAbsences;
late PersonalityType finalPersonality;
bool hold = false;
List<Grade> getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider
.grades
.where((e) =>
e.subject == subject &&
e.type == GradeType.midYear &&
(days == 0 ||
e.date.isBefore(DateTime.now().subtract(Duration(days: days)))))
.toList();
@override
void initState() {
super.initState();
gradeProvider = Provider.of<GradeProvider>(context, listen: false);
absenceProvider = Provider.of<AbsenceProvider>(context, listen: false);
timetableProvider = Provider.of<TimetableProvider>(context, listen: false);
settings = Provider.of<SettingsProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
for (final lesson in timetableProvider.getWeek(Week.current()) ?? []) {
if (!lesson.isEmpty &&
lesson.subject.id != '' &&
lesson.lessonYearIndex != null) {
_lessonCount.update(
lesson.subject,
(value) {
if (lesson.lessonYearIndex! > value.lessonYearIndex!) {
return lesson;
} else {
return value;
}
},
ifAbsent: () => lesson,
);
}
}
setState(() {});
});
}
void getGrades() {
List<Subject> subjects = gradeProvider.grades
.map((e) => e.subject)
.toSet()
.toList()
..sort((a, b) => a.name.compareTo(b.name));
for (Subject subject in subjects) {
List<Grade> subjectGrades = getSubjectGrades(subject);
double avg = AverageHelper.averageEvals(subjectGrades);
if (avg != 0) subjectAvgs[subject] = avg;
subjectAvgsList.add(avg.round());
}
subjectAvg = subjectAvgs.isNotEmpty
? subjectAvgs.values.fold(0.0, (double a, double b) => a + b) /
subjectAvgs.length
: 0.0;
classWorkGrades =
gradeProvider.grades.where((a) => a.value.weight <= 75).toList();
}
void getMostCommonGrade() {
Map<int, int> counts = {};
subjectAvgsList.map((e) {
if (counts.containsKey(e)) {
counts.update(e, (value) => value++);
} else {
counts[e] = 1;
}
});
var maxValue = 0;
var maxKey = 0;
counts.forEach((k, v) {
if (v > maxValue) {
maxValue = v;
maxKey = k;
}
});
mostCommonGrade = {maxKey: maxValue};
}
void getAbsences() {
absences = absenceProvider.absences.where((a) => a.delay == 0).toList();
unexcusedAbsences = absences
.where((a) => a.state == Justification.unexcused && a.delay == 0)
.length;
}
void getAndSortDelays() {
Iterable<int> unexcusedDelays = absences
.where((a) => a.state == Justification.unexcused && a.delay > 0)
.map((e) => e.delay);
totalDelays = unexcusedDelays.isNotEmpty
? unexcusedDelays.reduce((a, b) => a + b)
: 0;
}
void doEverything() {
getGrades();
getMostCommonGrade();
getAbsences();
getAndSortDelays();
}
void getPersonality() {
if (settings.goodStudent) {
finalPersonality = PersonalityType.cheater;
} else if (subjectAvg > 4.7) {
finalPersonality = PersonalityType.geek;
} else if (mostCommonGrade.keys.toList()[0] == 1 &&
mostCommonGrade.values.toList()[0] > 1) {
finalPersonality = PersonalityType.fallible;
} else if (absences.length < 10) {
finalPersonality = PersonalityType.healthy;
} else if (unexcusedAbsences >= 10) {
finalPersonality = PersonalityType.quitter;
} else if (totalDelays > 50) {
finalPersonality = PersonalityType.late;
} else if (absences.length >= 100) {
finalPersonality = PersonalityType.sick;
} else if (mostCommonGrade.keys.toList()[0] == 2) {
finalPersonality = PersonalityType.acceptable;
} else if (mostCommonGrade.keys.toList()[0] == 3) {
finalPersonality = PersonalityType.average;
} else if (classWorkGrades.length >= 5) {
finalPersonality = PersonalityType.diligent;
} else {
finalPersonality = PersonalityType.npc;
}
}
Widget cardInnerBuilder() {
Map<PersonalityType, Map<String, String>> personality = {
PersonalityType.geek: {
'emoji': '🤓',
'title': 't_geek',
'description': 'd_geek',
'subtitle': 's_geek',
'subvalue': subjectAvg.toStringAsFixed(2),
},
PersonalityType.sick: {
'emoji': '🤒',
'title': 't_sick',
'description': 'd_sick',
'subtitle': 's_sick',
'subvalue': absences.length.toString(),
},
PersonalityType.late: {
'emoji': '',
'title': 't_late',
'description': 'd_late',
'subtitle': 's_late',
'subvalue': totalDelays.toString(),
},
PersonalityType.quitter: {
'emoji': '',
'title': 't_quitter',
'description': 'd_quitter',
'subtitle': 's_quitter',
'subvalue': unexcusedAbsences.toString(),
},
PersonalityType.healthy: {
'emoji': '😷',
'title': 't_healthy',
'description': 'd_healthy',
'subtitle': 's_healthy',
'subvalue': absences.length.toString(),
},
PersonalityType.acceptable: {
'emoji': '🤏',
'title': 't_acceptable',
'description': 'd_acceptable',
'subtitle': 's_acceptable',
'subvalue': mostCommonGrade.values.toList()[0].toString(),
},
PersonalityType.fallible: {
'emoji': '📉',
'title': 't_fallible',
'description': 'd_fallible',
'subtitle': 's_fallible',
'subvalue': mostCommonGrade.values.toList()[0].toString(),
},
PersonalityType.average: {
'emoji': '👌',
'title': 't_average',
'description': 'd_average',
'subtitle': 's_average',
'subvalue': mostCommonGrade.values.toList()[0].toString(),
},
PersonalityType.diligent: {
'emoji': '💫',
'title': 't_diligent',
'description': 'd_diligent',
'subtitle': 's_diligent',
'subvalue': classWorkGrades.length.toString(),
},
PersonalityType.cheater: {
'emoji': '‍🧑‍💻',
'title': 't_cheater',
'description': 'd_cheater',
'subtitle': 's_cheater',
'subvalue': '0',
},
PersonalityType.npc: {
'emoji': '⛰️',
'title': 't_npc',
'description': 'd_npc',
'subtitle': 's_npc',
'subvalue': '69420',
}
};
Map<PersonalityType, Widget> personalityWidgets = {};
for (var i in personality.keys) {
Widget w = Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
personality[i]?['emoji'] ?? '',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 128.0,
height: 1.2,
),
),
Text(
(personality[i]?['title'] ?? 'unknown').i18n,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 38.0,
color: Colors.white,
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 5),
Text(
(personality[i]?['description'] ?? 'unknown_personality').i18n,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 16,
height: 1.2,
color: Colors.white.withOpacity(0.8),
),
),
const SizedBox(height: 25),
Text(
(personality[i]?['subtitle'] ?? 'unknown').i18n,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
Text(
personality[i]?['subvalue'] ?? '0',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 69.0,
height: 1.15,
color: Colors.white,
fontWeight: FontWeight.w800,
),
),
],
);
personalityWidgets.addAll({i: w});
}
return personalityWidgets[finalPersonality] ?? Container();
}
@override
Widget build(BuildContext context) {
doEverything();
getPersonality();
return GestureDetector(
onLongPressDown: (_) => setState(() => hold = true),
onLongPressEnd: (_) => setState(() => hold = false),
onLongPressCancel: () => setState(() => hold = false),
child: AnimatedScale(
scale: hold ? 1.018 : 1.0,
curve: Curves.easeInOutBack,
duration: const Duration(milliseconds: 300),
child: Container(
padding:
const EdgeInsets.only(top: 12, bottom: 12, left: 12, right: 12),
decoration: BoxDecoration(
color: const Color(0x280008FF),
borderRadius: const BorderRadius.all(Radius.circular(5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
offset: const Offset(0, 5),
blurRadius: 20,
spreadRadius: 10,
),
],
),
child: DottedBorder(
color: Colors.black.withOpacity(0.9),
dashPattern: const [12, 12],
padding:
const EdgeInsets.only(top: 20, bottom: 20, left: 20, right: 20),
child: cardInnerBuilder(),
),
),
),
);
}
}

View File

@@ -0,0 +1,154 @@
import 'package:i18n_extension/i18n_extension.dart';
extension Localization on String {
static final _t = Translations.byLocale("hu_hu") +
{
"en_en": {
// main
"unknown": "???",
"unknown_personality": "Unknown personality...",
// personalities
"t_geek": "Know-It-All",
"d_geek":
"You learn a lot, but don't worry - Being a know-it-all is a blessing in disguise. You'll be successful in life.",
"s_geek": "Year-end average",
"t_sick": "Sick",
"d_sick":
"Get well soon, bro. Even if you lied about being sick to skip school.",
"s_sick": "Absences",
"t_late": "Late",
"d_late":
"The tram's wheel got punctured. The airplane was derailed. Your dog ate your shoe. We believe you.",
"s_late": "Delays (minutes)",
"t_quitter": "Skipper",
"d_quitter": "Supplementary exam incoming.",
"s_quitter": "Igazolatlan hiányzások",
"t_healthy": "Healthy",
"d_healthy":
"As cool as a cucumber! You almost never missed a class.",
"s_healthy": "Absences",
"t_acceptable": "Acceptable",
"d_acceptable":
"Final exams are D. But who cares? It's still a grade. Not a good one, but it's definitely a grade.",
"s_acceptable": "D's",
"t_fallible": "Failed",
"d_fallible": "Good luck next year.",
"s_fallible": "F's",
"t_average": "It's okay",
"d_average": "Not good, not bad. The golden mean, if you will...",
"s_average": "C's",
"t_diligent": "Hard-worker",
"d_diligent":
"You noted everything, you made that presentation, and you lead the group project.",
"s_diligent": "Class work A's",
"t_cheater": "Cheater",
"d_cheater":
"You enabled the \"Good Student\" mode. Wow. You may have outsmarted me, but I have outsmarted your outsmarting.",
"s_cheater": "Bitches",
"t_npc": "NPC",
"d_npc":
"You're such a non-player character, we couldn't give you a personality.",
"s_npc": "In-game playtime (hours)",
},
"hu_hu": {
// main
"unknown": "???",
"unknown_personality": "Ismeretlen személyiség...",
// personalities
"t_geek": "Stréber",
"d_geek":
"Sokat tanulsz, de ezzel semmi baj! Ez egyben áldás és átok, de legalább az életben sikeres leszel.",
"s_geek": "Év végi átlagod",
"t_sick": "Beteges",
"d_sick":
"Jobbulást, tesó. Még akkor is, ha hazudtál arról, hogy beteg vagy, hogy ne kelljen suliba menned.",
"s_sick": "Hiányzásaid",
"t_late": "Késős",
"d_late":
"Kilyukadt a villamos kereke. Kisiklott a repülő. A kutyád megette a cipőd. Elhisszük.",
"s_late": "Késések (perc)",
"t_quitter": "Lógós",
"d_quitter": "Osztályzóvizsga incoming.",
"s_quitter": "Igazolatlan hiányzások",
"t_healthy": "Makk",
"d_healthy":
"...egészséges vagy! Egész évben alig hiányoztál az iskolából.",
"s_healthy": "Hiányzásaid",
"t_acceptable": "Elmegy",
"d_acceptable":
"A kettes érettségi is érettségi. Nem egy jó érettségi, de biztos, hogy egy érettségi.",
"s_acceptable": "Kettesek",
"t_fallible": "Bukós",
"d_fallible": "Jövőre több sikerrel jársz.",
"s_fallible": "Karók",
"t_average": "Közepes",
"d_average": "Se jó, se rossz. Az arany középút, ha akarsz...",
"s_average": "Hármasok",
"t_diligent": "Szorgalmas",
"d_diligent":
"Leírtad a jegyzetet, megcsináltad a prezentációt, és te vezetted a projektmunkát.",
"s_diligent": "Órai munka ötösök",
"t_cheater": "Csaló",
"d_cheater":
"Bekapcsoltad a “Jó Tanuló” módot. Wow. Azt hitted, túl járhatsz az eszemen, de kijátszottam a kijátszásod.",
"s_cheater": "Bitches",
"t_npc": "NPC",
"d_npc":
"Egy akkora nagy non-player character vagy, hogy neked semmilyen személyiség nem jutott ezen kívül.",
"s_npc": "In-game playtime (óra)",
},
"de_de": {
// main
"unknown": "???",
"unknown_personality": "Unbekannte Persönlichkeit...",
// personalities
"t_geek": "Besserwisser",
"d_geek":
"Du lernst eine Menge, aber sorge dich nicht - ein Besserwisser zu sein wird sich letzten Endes doch als Segen erweisen. Du wirst erfolgreich sein im Leben.",
"s_geek": "Durchschnittsschüler",
"t_sick": "Krank",
"d_sick":
"Werd schnell wieder gesund, Brudi. Selbst wenn du gelogen hast, nur um Schule zu schwänzen zu können.",
"s_sick": "Abwesenheiten",
"t_late": "Verspätet",
"d_late":
"Die Straßenbahn hat eine Reifenpanne. Das Flugzeug ist entgleist. Dein Hund hat deinen Schuh gefressen. Klar, wir glauben dir.",
"s_late": "Verspätung (Minuten)",
"t_quitter": "Schulschwänzer",
"d_quitter": "Ein zusätzlicher Test wird anstehen.",
"s_quitter": "Unentschuldigte Abwesenheiten",
"t_healthy": "Gesund",
"d_healthy":
"Du bist die Ruhe selbst! Du hast fast nie eine Unterrichtsstunde verpasst.",
"s_healthy": "Abwesenheiten",
"t_acceptable": "Akzeptabel",
"d_acceptable":
"Die Abschlussprüfungen waren gerade einmal eine 4. Aber wen juckt's? Es ist immer noch positiv. Nicht allzu gut, aber definitiv positiv.",
"s_acceptable": "4er",
"t_fallible": "Durchgefallen",
"d_fallible": "Viel Glück im nächsten Jahr.",
"s_fallible": "5er",
"t_average": "Es ist in Ordnung",
"d_average":
"Nicht gut, nicht schlecht. Der goldene Durchschnitt, wenn du so willst...",
"s_average": "3er",
"t_diligent": "Ein Fleißiger",
"d_diligent":
"Du hast bei allem mitgeschrieben, du hast den Vortrag gehalten, und du hast die Gruppenarbeit geleitet.",
"s_diligent": "1er Schüler",
"t_cheater": "Geschummelt",
"d_cheater":
"Du hast den „Guter Schüler“ Modus aktiviert. Wow. Du magst mich zwar vielleicht überlistet haben, aber ich habe deine Überlistung überlistet.",
"s_cheater": "Bitches",
"t_npc": "COM",
"d_npc":
"Du bist einfach so sehr wie ein Computer, dass wir dir nicht einmal eine Persönlichkeit geben konnten.",
"s_npc": "Spielzeit (Stunden)",
}
};
String get i18n => localize(this, _t);
String fill(List<Object> params) => localizeFill(this, params);
String plural(int value) => localizePlural(value, this, _t);
String version(Object modifier) => localizeVersion(modifier, this, _t);
}

View File

@@ -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,54 @@ class _LiveCardState extends State<LiveCard> {
Duration bellDelay = liveCard.delay;
switch (liveCard.currentState) {
case LiveCardState.summary:
child = LiveCardWidget(
key: const Key('livecard.summary'),
title: 'Vége a tanévnek! 🥳',
icon: FeatherIcons.arrowRight,
description: Text(
'Irány az összefoglaláshoz',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18.0,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
onTap: () {
// showSlidingBottomSheet(
// context,
// useRootNavigator: true,
// builder: (context) => SlidingSheetDialog(
// color: Colors.black.withOpacity(0.99),
// duration: const Duration(milliseconds: 400),
// scrollSpec: const ScrollSpec.bouncingScroll(),
// snapSpec: const SnapSpec(
// snap: true,
// snappings: [1.0],
// initialSnap: 1.0,
// positioning: SnapPositioning.relativeToAvailableSpace,
// ),
// minHeight: MediaQuery.of(context).size.height,
// cornerRadius: 16,
// cornerRadiusOnFullscreen: 0,
// builder: (context, state) => const Material(
// color: Colors.black,
// child: SummaryScreen(
// currentPage: 'start',
// ),
// ),
// ),
// );
SummaryScreen.show(context: context, currentPage: 'start');
},
);
break;
case LiveCardState.morning:
child = LiveCardWidget(
key: const Key('livecard.morning'),
title: DateFormat("EEEE", I18n.of(context).locale.toString()).format(DateTime.now()).capital(),
title: DateFormat("EEEE", I18n.of(context).locale.toString())
.format(DateTime.now())
.capital(),
icon: FeatherIcons.sun,
description: liveCard.nextLesson != null
? Text.rich(
@@ -61,26 +108,40 @@ class _LiveCardState extends State<LiveCard> {
children: [
TextSpan(text: "first_lesson_1".i18n),
TextSpan(
text: liveCard.nextLesson!.subject.renamedTo ?? liveCard.nextLesson!.subject.name.capital(),
text: liveCard.nextLesson!.subject.renamedTo ??
liveCard.nextLesson!.subject.name.capital(),
style: TextStyle(
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary.withOpacity(.85),
fontStyle: liveCard.nextLesson!.subject.isRenamed ? FontStyle.italic : null),
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.85),
fontStyle: liveCard.nextLesson!.subject.isRenamed &&
settingsProvider.renamedSubjectsItalics
? FontStyle.italic
: null),
),
TextSpan(text: "first_lesson_2".i18n),
TextSpan(
text: liveCard.nextLesson!.room.capital(),
style: TextStyle(
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary.withOpacity(.85),
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.85),
),
),
TextSpan(text: "first_lesson_3".i18n),
TextSpan(
text: DateFormat('H:mm').format(liveCard.nextLesson!.start),
text: DateFormat('H:mm')
.format(liveCard.nextLesson!.start),
style: TextStyle(
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary.withOpacity(.85),
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.85),
),
),
TextSpan(text: "first_lesson_4".i18n),
@@ -91,30 +152,48 @@ class _LiveCardState extends State<LiveCard> {
);
break;
case LiveCardState.duringLesson:
final elapsedTime = DateTime.now().difference(liveCard.currentLesson!.start).inSeconds.toDouble() + bellDelay.inSeconds;
final maxTime = liveCard.currentLesson!.end.difference(liveCard.currentLesson!.start).inSeconds.toDouble();
final elapsedTime = DateTime.now()
.difference(liveCard.currentLesson!.start)
.inSeconds
.toDouble() +
bellDelay.inSeconds;
final maxTime = liveCard.currentLesson!.end
.difference(liveCard.currentLesson!.start)
.inSeconds
.toDouble();
final showMinutes = maxTime - elapsedTime > 60;
child = LiveCardWidget(
key: const Key('livecard.duringLesson'),
leading: liveCard.currentLesson!.lessonIndex + (RegExp(r'\d').hasMatch(liveCard.currentLesson!.lessonIndex) ? "." : ""),
title: liveCard.currentLesson!.subject.renamedTo ?? liveCard.currentLesson!.subject.name.capital(),
leading: liveCard.currentLesson!.lessonIndex +
(RegExp(r'\d').hasMatch(liveCard.currentLesson!.lessonIndex)
? "."
: ""),
title: liveCard.currentLesson!.subject.renamedTo ??
liveCard.currentLesson!.subject.name.capital(),
titleItalic: liveCard.currentLesson!.subject.isRenamed,
subtitle: liveCard.currentLesson!.room,
icon: SubjectIcon.resolveVariant(subject: liveCard.currentLesson!.subject, context: context),
description: liveCard.currentLesson!.description != "" ? Text(liveCard.currentLesson!.description) : null,
nextSubject: liveCard.nextLesson?.subject.renamedTo ?? liveCard.nextLesson?.subject.name.capital(),
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed ?? false,
icon: SubjectIcon.resolveVariant(
subject: liveCard.currentLesson!.subject, context: context),
description: liveCard.currentLesson!.description != ""
? Text(liveCard.currentLesson!.description)
: null,
nextSubject: liveCard.nextLesson?.subject.renamedTo ??
liveCard.nextLesson?.subject.name.capital(),
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed == true &&
settingsProvider.renamedSubjectsItalics,
nextRoom: liveCard.nextLesson?.room,
progressMax: showMinutes ? maxTime / 60 : maxTime,
progressCurrent: showMinutes ? elapsedTime / 60 : elapsedTime,
progressAccuracy: showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
progressAccuracy:
showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
onProgressTap: () {
showDialog(
barrierColor: Colors.black,
context: context,
builder: (context) => HeadsUpCountdown(maxTime: maxTime, elapsedTime: elapsedTime),
builder: (context) =>
HeadsUpCountdown(maxTime: maxTime, elapsedTime: elapsedTime),
);
},
);
@@ -129,8 +208,15 @@ class _LiveCardState extends State<LiveCard> {
final diff = liveCard.getFloorDifference();
final maxTime = liveCard.nextLesson!.start.difference(liveCard.prevLesson!.end).inSeconds.toDouble();
final elapsedTime = DateTime.now().difference(liveCard.prevLesson!.end).inSeconds.toDouble() + bellDelay.inSeconds.toDouble();
final maxTime = liveCard.nextLesson!.start
.difference(liveCard.prevLesson!.end)
.inSeconds
.toDouble();
final elapsedTime = DateTime.now()
.difference(liveCard.prevLesson!.end)
.inSeconds
.toDouble() +
bellDelay.inSeconds.toDouble();
final showMinutes = maxTime - elapsedTime > 60;
@@ -139,14 +225,21 @@ class _LiveCardState extends State<LiveCard> {
title: "break".i18n,
icon: iconFloorMap[diff],
description: liveCard.nextLesson!.room != liveCard.prevLesson!.room
? Text("go $diff".i18n.fill([diff != "to room" ? (liveCard.nextLesson!.getFloor() ?? 0) : liveCard.nextLesson!.room]))
? Text("go $diff".i18n.fill([
diff != "to room"
? (liveCard.nextLesson!.getFloor() ?? 0)
: liveCard.nextLesson!.room
]))
: Text("stay".i18n),
nextSubject: liveCard.nextLesson?.subject.renamedTo ?? liveCard.nextLesson?.subject.name.capital(),
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed ?? false,
nextSubject: liveCard.nextLesson?.subject.renamedTo ??
liveCard.nextLesson?.subject.name.capital(),
nextSubjectItalic: liveCard.nextLesson?.subject.isRenamed == true &&
settingsProvider.renamedSubjectsItalics,
nextRoom: diff != "to room" ? liveCard.nextLesson?.room : null,
progressMax: showMinutes ? maxTime / 60 : maxTime,
progressCurrent: showMinutes ? elapsedTime / 60 : elapsedTime,
progressAccuracy: showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
progressAccuracy:
showMinutes ? ProgressAccuracy.minutes : ProgressAccuracy.seconds,
onProgressTap: () {
showDialog(
barrierColor: Colors.black,
@@ -162,14 +255,18 @@ class _LiveCardState extends State<LiveCard> {
case LiveCardState.afternoon:
child = LiveCardWidget(
key: const Key('livecard.afternoon'),
title: DateFormat("EEEE", I18n.of(context).locale.toString()).format(DateTime.now()).capital(),
title: DateFormat("EEEE", I18n.of(context).locale.toString())
.format(DateTime.now())
.capital(),
icon: FeatherIcons.coffee,
);
break;
case LiveCardState.night:
child = LiveCardWidget(
key: const Key('livecard.night'),
title: DateFormat("EEEE", I18n.of(context).locale.toString()).format(DateTime.now()).capital(),
title: DateFormat("EEEE", I18n.of(context).locale.toString())
.format(DateTime.now())
.capital(),
icon: FeatherIcons.moon,
);
break;

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,305 @@ class _LiveCardWidgetState extends State<LiveCardWidget> {
padding: const EdgeInsets.symmetric(horizontal: 6.0),
child: OverflowBox(
maxHeight: 96.0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
child: widget.isEvent
? Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (widget.leading != null)
Padding(
padding: const EdgeInsets.only(right: 12.0, top: 8.0),
child: Text(
widget.leading!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary,
),
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
children: [
if (widget.title != null)
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(text: widget.title!, style: TextStyle(fontStyle: widget.titleItalic ? FontStyle.italic : null)),
if (widget.subtitle != null)
WidgetSpan(
child: Container(
margin: const EdgeInsets.only(left: 6.0, bottom: 3.0),
padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 2.0),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary.withOpacity(.3),
borderRadius: BorderRadius.circular(4.0),
),
child: Text(
widget.subtitle!,
style: TextStyle(
height: 1.2,
fontSize: 14.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary,
),
),
),
),
],
),
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 22.0),
maxLines: 1,
softWrap: false,
overflow: TextOverflow.ellipsis,
),
),
if (widget.title != null) const SizedBox(width: 6.0),
if (widget.icon != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Icon(
widget.icon,
size: 26.0,
color: AppColors.of(context).text.withOpacity(.75),
),
),
],
),
if (widget.description != null)
DefaultTextStyle(
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16.0,
height: 1.0,
color: AppColors.of(context).text.withOpacity(.75),
),
maxLines: !(widget.nextSubject == null && widget.progressCurrent == null && widget.progressMax == null) ? 1 : 2,
softWrap: false,
overflow: TextOverflow.ellipsis,
child: widget.description!,
),
],
Text(
widget.title ?? 'Esemény',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24.0,
color:
Theme.of(context).textTheme.bodyMedium?.color,
fontStyle:
widget.titleItalic ? FontStyle.italic : null,
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
widget.description ??
Text(
'Nincs leírás megadva.',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18.0,
color: Theme.of(context)
.textTheme
.bodyMedium
?.color,
),
),
SizedBox(
height: 15,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(0.5),
borderRadius: const BorderRadius.all(
Radius.circular(10),
),
),
child: Padding(
padding: const EdgeInsets.all(4),
child: Icon(widget.icon),
),
),
),
],
),
],
),
),
if (!(widget.nextSubject == null && widget.progressCurrent == null && widget.progressMax == null))
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Row(
children: [
if (widget.nextSubject != null) const Icon(FeatherIcons.arrowRight, size: 12.0),
if (widget.nextSubject != null) const SizedBox(width: 4.0),
if (widget.nextSubject != null)
Expanded(
child: Text.rich(
TextSpan(
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.leading != null)
Padding(
padding: const EdgeInsets.only(
right: 12.0, top: 8.0),
child: Text(
widget.leading!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.colorScheme
.secondary,
),
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
TextSpan(
text: widget.nextSubject!, style: TextStyle(fontStyle: widget.nextSubjectItalic ? FontStyle.italic : null)),
if (widget.nextRoom != null)
WidgetSpan(
child: Container(
margin: const EdgeInsets.only(left: 4.0),
padding: const EdgeInsets.symmetric(horizontal: 3.0, vertical: 1.5),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary.withOpacity(.25),
borderRadius: BorderRadius.circular(4.0),
),
child: Text(
widget.nextRoom!,
style: TextStyle(
height: 1.1,
fontSize: 11.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.secondary.withOpacity(.9),
Row(
children: [
if (widget.title != null)
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: widget.title!,
style: TextStyle(
fontStyle: widget
.titleItalic
? FontStyle.italic
: null)),
if (widget.subtitle != null)
WidgetSpan(
child: Container(
margin: const EdgeInsets
.only(
left: 6.0,
bottom: 3.0),
padding:
const EdgeInsets
.symmetric(
horizontal: 4.0,
vertical: 2.0),
decoration:
BoxDecoration(
color: Theme.of(
context)
.colorScheme
.secondary
.withOpacity(.3),
borderRadius:
BorderRadius
.circular(
4.0),
),
child: Text(
widget.subtitle!,
style: TextStyle(
height: 1.2,
fontSize: 14.0,
fontWeight:
FontWeight.w600,
color: Theme.of(
context)
.colorScheme
.secondary,
),
),
),
),
],
),
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 22.0),
maxLines: 1,
softWrap: false,
overflow: TextOverflow.ellipsis,
),
),
),
if (widget.title != null)
const SizedBox(width: 6.0),
if (widget.icon != null)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 4.0),
child: Icon(
widget.icon,
size: 26.0,
color: AppColors.of(context)
.text
.withOpacity(.75),
),
),
],
),
if (widget.description != null)
DefaultTextStyle(
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16.0,
height: 1.0,
color: AppColors.of(context)
.text
.withOpacity(.75),
),
maxLines:
!(widget.nextSubject == null &&
widget.progressCurrent ==
null &&
widget.progressMax == null)
? 1
: 2,
softWrap: false,
overflow: TextOverflow.ellipsis,
child: widget.description!,
),
],
),
style: TextStyle(
color: AppColors.of(context).text.withOpacity(.8),
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: false,
),
),
if (widget.nextRoom == null && widget.nextSubject == null) const Spacer(),
if (widget.progressCurrent != null && widget.progressMax != null)
GestureDetector(
onTap: widget.onProgressTap,
child: Container(
color: Colors.transparent,
child: Text(
"remaining ${widget.progressAccuracy == ProgressAccuracy.minutes ? 'min' : 'sec'}"
.plural((widget.progressMax! - widget.progressCurrent!).round()),
maxLines: 1,
style: TextStyle(
fontWeight: FontWeight.w500,
color: AppColors.of(context).text.withOpacity(.75),
],
),
),
if (!(widget.nextSubject == null &&
widget.progressCurrent == null &&
widget.progressMax == null))
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Row(
children: [
if (widget.nextSubject != null)
const Icon(FeatherIcons.arrowRight,
size: 12.0),
if (widget.nextSubject != null)
const SizedBox(width: 4.0),
if (widget.nextSubject != null)
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: widget.nextSubject!,
style: TextStyle(
fontStyle:
widget.nextSubjectItalic
? FontStyle.italic
: null)),
if (widget.nextRoom != null)
WidgetSpan(
child: Container(
margin: const EdgeInsets.only(
left: 4.0),
padding:
const EdgeInsets.symmetric(
horizontal: 3.0,
vertical: 1.5),
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.25),
borderRadius:
BorderRadius.circular(
4.0),
),
child: Text(
widget.nextRoom!,
style: TextStyle(
height: 1.1,
fontSize: 11.0,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.9),
),
),
),
),
],
),
style: TextStyle(
color: AppColors.of(context)
.text
.withOpacity(.8),
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: false,
),
),
),
),
)
],
),
if (widget.nextRoom == null &&
widget.nextSubject == null)
const Spacer(),
if (widget.progressCurrent != null &&
widget.progressMax != null)
GestureDetector(
onTap: widget.onProgressTap,
child: Container(
color: Colors.transparent,
child: Text(
"remaining ${widget.progressAccuracy == ProgressAccuracy.minutes ? 'min' : 'sec'}"
.plural((widget.progressMax! -
widget.progressCurrent!)
.round()),
maxLines: 1,
style: TextStyle(
fontWeight: FontWeight.w500,
color: AppColors.of(context)
.text
.withOpacity(.75),
),
),
),
)
],
),
),
if (widget.progressCurrent != null &&
widget.progressMax != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: ProgressBar(
value: widget.progressCurrent! /
widget.progressMax!),
)
],
),
if (widget.progressCurrent != null && widget.progressMax != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: ProgressBar(value: widget.progressCurrent! / widget.progressMax!),
)
],
),
),
),
),

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(

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