Compare commits

...

46 Commits

Author SHA1 Message Date
Márton Kiss
d929c804df Merge pull request #21 from TMarccci/master
LiveActivity
2023-06-14 23:53:17 +02:00
Tihanyi Marcell
19c128eecd LiveActivity 2023-06-14 23:46:21 +02:00
kima
57cf764804 updated version string 2023-06-14 23:08:54 +02:00
kima
ea812e0b67 fixed project problems 2023-06-13 23:37:35 +02:00
kima
1c517a99f2 fixed project problems 2023-06-13 23:37:25 +02:00
kima
2687cb146b gitignore changes 2023-06-13 23:35:11 +02:00
kima
1a9080dcc2 added event functionality and summary to livecard 2023-06-13 23:27:40 +02:00
Márton Kiss
580c92b13d Merge pull request #20 from TMarccci/master
On empty timetable block fullscreen mode.
2023-06-13 21:10:04 +02:00
Tihanyi Marcell
ae4c4aa89c Merge branch 'refilc:master' into master 2023-06-12 21:54:47 +02:00
Tihanyi Marcell
ded7c51f44 Block empty fullscreen timetable 2023-06-12 21:54:13 +02:00
Márton Kiss
485e85ddaa Merge pull request #19 from TMarccci/master
Fixes
2023-06-12 20:26:48 +02:00
Márton Kiss
3c0082a786 Merge pull request #18 from CroatianHusky/master
Developer Settings i18n + Notifications toggle color fix
2023-06-12 20:25:28 +02:00
Tihanyi Marcell
c474512088 Live Activity version update, build-ipa.sh 2023-06-12 20:11:15 +02:00
Tihanyi Marcell
f78a542be2 iOS notification permission fix 2023-06-12 20:09:48 +02:00
Tihanyi Marcell
131454b99d Notification Capabilitie 2023-06-12 20:09:27 +02:00
Tihanyi Marcell
e255182b93 Removed " " from login 2023-06-12 20:09:11 +02:00
Tihanyi Marcell
3857896d6c Ghost Grade title overflow fix 2023-06-12 20:08:53 +02:00
CroatianHusky
301e8cb638 Updated settings_screen.i18n.dart 2023-06-12 17:35:13 +02:00
CroatianHusky
ae7c724f65 notification toggle color fix 2023-06-12 17:33:00 +02:00
CroatianHusky
751cd04ce2 developer settings i18n 2023-06-12 17:14:42 +02:00
Márton Kiss
a88ccfa3fc Merge pull request #15 from Monke14/notifications
Értesítések
2023-06-10 22:47:03 +02:00
Márton Kiss
9e914974b7 Merge branch 'master' into notifications 2023-06-10 22:46:40 +02:00
Kima
9cfa8296b8 fixed some bugs 2023-06-10 22:38:01 +02:00
hihihaha
27ef942723 fix headless task 2023-06-10 21:46:53 +02:00
hihihaha
fe03554fbf backend changes 2023-06-10 21:19:49 +02:00
hihihaha
8c2227df73 update design 2023-06-10 21:03:01 +02:00
hihihaha
0274c2f070 start on boot 2023-06-10 20:43:03 +02:00
hihihaha
8f85c6a33b add timeout safety to headless task 2023-06-10 20:41:31 +02:00
hihihaha
cf81ca8207 add annotation to function 2023-06-10 20:39:06 +02:00
hihihaha
07bbafe7dd add toggle for notifications in settings 2023-06-10 20:34:01 +02:00
hihihaha
3eee2c7a55 set grade as seen 2023-06-10 19:55:40 +02:00
hihihaha
1cdde3b6ce add notification 2023-06-10 19:50:32 +02:00
Márton Kiss
67aea46c06 Update navigation_screen.dart 2023-06-10 13:59:03 +02:00
Márton Kiss
7adec7dfa5 maybe fixed dark mode bug 2023-06-10 13:57:11 +02:00
Kima
95fa819ed2 updatet things in pubspec 2023-06-10 13:26:28 +02:00
Kima
1e87d344e2 fixed versions in pubspec 2023-06-10 11:25:24 +02:00
Márton Kiss
9223375304 Merge pull request #11 from CroatianHusky/master
"Új jegyek" többesszám fix + verzió "v?" fix
2023-06-10 11:10:04 +02:00
Márton Kiss
bb862f15bd Merge pull request #10 from PredatorPotatoX/master
CONTRIBUTING.md link fix
2023-06-10 11:09:46 +02:00
CroatianHusky
261c94e9bb fixed version counter "v?" 2023-06-10 10:38:26 +02:00
CroatianHusky
06016514e5 new grade plurality fix 2023-06-10 09:32:21 +02:00
CroatianHusky
cead24b65a removed dead filc links from pubspec 2023-06-10 09:29:44 +02:00
PredatorPotatoX
6eaab57468 CONTRIBUTING.md link fix 2023-06-09 23:31:29 +02:00
Márton Kiss
5852fc233d Update AndroidManifest.xml 2023-06-09 21:15:25 +02:00
Márton Kiss
d7e21f6332 Update update_helper.dart 2023-06-09 21:13:38 +02:00
Márton Kiss
c78b8d3b97 Merge pull request #9 from Monke14/bug-fixes
Dőlt betűk toggle
2023-06-09 20:15:31 +02:00
hihihaha
940e3d8ca1 add toggle for italics 2023-06-09 17:29:03 +02:00
67 changed files with 1307 additions and 476 deletions

2
.gitignore vendored
View File

@@ -49,4 +49,4 @@ filcnaplo/linux/flutter/generated_plugin_registrant.h
filcnaplo/linux/flutter/generated_plugins.cmake
filcnaplo/macos/Flutter/GeneratedPluginRegistrant.swift
.vscode/
key.properties

60
.idea/naplo.iml generated
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>

View File

@@ -1,5 +1,5 @@
# Contributing Guide
# Hozzájárulási útmutató
Köszönjük, ha programozással segíted a munkánkat!
A folytatáshoz szükséged lesz egy Linuxot vagy Windowst futtató számítógépre, minimális programozási tapasztalatra és egy kis angoltudásra.
Segít, ha nem csak kicsit tudsz programozni, és ha ismered a Gitet és a GitHubot ;)
@@ -9,7 +9,7 @@ Kérünk, **olyan dologgal járulj hozzá** a reFilchez, ami valószínűleg **s
Fontos, hogy **mielőtt egy nagy volumenű projektbe belekezdesz, futtasd meg ötletedet a [Discord szerverünkön](https://dc.refilc.hu/),** ahol még azelőtt tudunk tanácsot adni, mielőtt sok-sok órát beleöltél volna egy esetleg felesleges dologba.
A legjobban annak örülünk, ha az [Issues](https://github.com/refilc/filcnaplo/issues) oldalról szemezgetsz, **ha lehet, a [priority taggel megjelöltekkel kezdd](https://github.com/refilc/filcnaplo/issues?q=is%3Aissue+is%3Aopen+label%3Apriority),** vagy ha új vagy a Flutterhez, ajánljuk figyelmedbe [ezeket a viszonylag könnyen javítható hibákat](https://github.com/refilc/filcnaplo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) (ha épp van ilyen).
A legjobban annak örülünk, ha az [Issues](https://github.com/refilc/naplo/issues) oldalról szemezgetsz, **ha lehet, a [help wanted taggel megjelöltekkel kezdd](https://github.com/refilc/naplo/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22),** vagy ha új vagy a Flutterhez, ajánljuk figyelmedbe [ezeket a viszonylag könnyen javítható hibákat](https://github.com/refilc/naplo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) (ha épp van ilyen).
## Hogyan segítsek?
@@ -19,7 +19,7 @@ A reFilc a Google által pár éve létrehozott **[Fluttert](https://flutter.dev
Ha még nem használtál Fluttert, mindenképp böngészd át a [YouTube csatornájukat](https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw).
Könnyen tudsz kódot, vagy akár UI-t is tesztelni a [DartPad](https://dartpad.dev/) oldalon.
#### [Segítség a Flutter telepítéséhez és a forráskód futtatásához](/.github/SETUP.md)
#### [Segítség a Flutter telepítéséhez és a forráskód futtatásához](https://docs.flutter.dev/get-started/install)
Fontos: **Legyél a flutter beta verzióján!** Írd be: `flutter channel beta`
@@ -27,7 +27,7 @@ Fontos: **Legyél a flutter beta verzióján!** Írd be: `flutter channel beta`
Ha nem értesz a Githez, ajánljuk figyelmedbe [ezt a cikket](https://medium.com/envienta-magyarorsz%C3%A1g/git-%C3%A9s-github-gyorstalpal%C3%B3-f2d78a732deb). Viszont arra kérünk, a Git használatát ne a reFilcen próbáld ki először, hozz létre előbb egy saját Repót, és abba tesztelgess. Ha már nagyjából kitapasztaltad, várjuk hozzájárulásodat.
Készíts egy forkot a saját fiókod alá.
A reFilc legfrissebb, épp fejlesztés alatt álló verzióját a [dev brancen](https://github.com/refilc/filcnaplo/tree/dev) találod, kérjük ide commitolj, és ide célozd a forkodból a Pull Requested. Írd le benne, mit változtattál, és ha lehet, csatolj képernyőképet is.
A reFilc legfrissebb, épp fejlesztés alatt álló verzióját a [master branch](https://github.com/refilc/naplo/tree/master)-en találod, kérjük ide commitolj, és ide célozd a forkodból a Pull Requested. Írd le benne, mit változtattál, és ha lehet, csatolj képernyőképet is.
Minél gyakrabban készíts minél részletesebben elnevezett commitokat, hogy el tudjunk tájékozódni az általad beküldött kódon.
---

View File

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

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

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

View File

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

View File

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

View File

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

View File

@@ -54,17 +54,11 @@ Future loginApi({
"content-type": "application/x-www-form-urlencoded",
};
String nonceStr = await Provider.of<KretaClient>(context, listen: false)
.getAPI(KretaAPI.nonce, json: false);
Nonce nonce = getNonce(nonceStr, username.replaceAll(' ', '') + ' ', instituteCode);
headers.addAll(nonce.header());
Map? res = await Provider.of<KretaClient>(context, listen: false)
.postAPI(KretaAPI.login,
headers: headers,
body: User.loginBody(
username: username.replaceAll(' ', '') + ' ',
username: username,
password: password,
instituteCode: instituteCode,
));
@@ -83,7 +77,7 @@ Future loginApi({
.getAPI(KretaAPI.student(instituteCode));
Student student = Student.fromJson(studentJson!);
var user = User(
username: username.replaceAll(' ', '') + ' ',
username: username,
password: password,
instituteCode: instituteCode,
name: student.name,

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

@@ -19,7 +19,8 @@ enum LiveCardState {
duringBreak,
morning,
afternoon,
night
night,
summary
}
class LiveCardProvider extends ChangeNotifier {
@@ -47,7 +48,9 @@ class LiveCardProvider extends ChangeNotifier {
// Check if live card is enabled .areActivitiesEnabled()
_liveActivitiesPlugin.areActivitiesEnabled().then((value) {
// Console log
print("Live card enabled: $value");
if (kDebugMode) {
print("Live card enabled: $value");
}
if (value) {
_liveActivitiesPlugin.init(appGroupId: "group.refilc.livecard");

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,7 +19,7 @@ extension UpdateHelper on Release {
updateCallback!(-1, UpdateState.preparing);
String downloads = await StorageHelper.downloadsPath();
File apk = File("$downloads/refilc-$version.apk");
File apk = File("$downloads/refilc-v$version.apk");
if (!await apk.exists()) {
updateCallback(-1, UpdateState.downloading);

View File

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

View File

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

View File

@@ -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';
@@ -30,6 +31,8 @@ class LessonTile extends StatelessWidget {
bool fill = false;
bool fillLeading = false;
String lessonIndexTrailing = "";
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
// Only put a trailing . if its a digit
if (RegExp(r'\d').hasMatch(lesson.lessonIndex)) lessonIndexTrailing = ".";
@@ -159,7 +162,7 @@ class LessonTile extends StatelessWidget {
.text
.withOpacity(!lesson.isEmpty ? 1.0 : 0.5),
fontStyle:
lesson.subject.isRenamed ? FontStyle.italic : null),
lesson.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: description != ""
? Text(

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

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

@@ -169,7 +169,7 @@ class KretaClient {
Map? loginRes = await postAPI(KretaAPI.login,
headers: headers,
body: User.loginBody(
username: loginUser.username.replaceAll(' ', '') + ' ',
username: loginUser.username,
password: loginUser.password,
instituteCode: loginUser.instituteCode,
));

View File

@@ -51,7 +51,6 @@ class GradeProvider with ChangeNotifier {
final userStore = _database.userStore;
userStore.storeLastSeenGrade(DateTime.now(), userId: userId);
_lastSeen = DateTime.now();
notifyListeners();
}
}
@@ -98,7 +97,11 @@ class GradeProvider with ChangeNotifier {
.i18n;
grade.value.shortName = _settings.goodStudent
? "Jeles".i18n
: '${grade.json!["SzovegesErtekelesRovidNev"]}' != "null" && '${grade.json!["SzovegesErtekelesRovidNev"]}' != "-" && '${grade.json!["SzovegesErtekelesRovidNev"]}'.replaceAll(RegExp(r'[0123456789]+[%]?'), '') != ""
: '${grade.json!["SzovegesErtekelesRovidNev"]}' != "null" &&
'${grade.json!["SzovegesErtekelesRovidNev"]}' != "-" &&
'${grade.json!["SzovegesErtekelesRovidNev"]}'
.replaceAll(RegExp(r'[0123456789]+[%]?'), '') !=
""
? '${grade.json!["SzovegesErtekelesRovidNev"]}'.i18n
: grade.value.valueName;
}
@@ -119,14 +122,16 @@ class GradeProvider with ChangeNotifier {
if (grades.isNotEmpty || _grades.isNotEmpty) await store(grades);
List? groupsJson = await _kreta.getAPI(KretaAPI.groups(iss));
if (groupsJson == null || groupsJson.isEmpty)
if (groupsJson == null || groupsJson.isEmpty) {
throw "Cannot fetch Groups for User ${user.id}";
}
_groups = (groupsJson[0]["OktatasNevelesiFeladat"] ?? {})["Uid"] ?? "";
List? groupAvgJson =
await _kreta.getAPI(KretaAPI.groupAverages(iss, _groups));
if (groupAvgJson == null)
if (groupAvgJson == null) {
throw "Cannot fetch Class Averages for User ${user.id}";
}
final groupAvgs =
groupAvgJson.map((e) => GroupAverage.fromJson(e)).toList();
await storeGroupAvg(groupAvgs);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,11 @@
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_kreta_api/models/homework.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
class HomeworkTile extends StatelessWidget {
const HomeworkTile(this.homework,
@@ -17,6 +19,8 @@ class HomeworkTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
return Material(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(8.0),
@@ -65,7 +69,7 @@ class HomeworkTile extends StatelessWidget {
homework.subject.renamedTo ?? homework.subject.name.capital(),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600),
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: homework.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: censored
? Wrap(

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(
@@ -48,7 +51,7 @@ class HomeworkView extends StatelessWidget {
homework.subject.renamedTo ?? homework.subject.name.capital(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600),
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: homework.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
),
subtitle: Text(
homework.teacher,

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

@@ -1,6 +1,7 @@
import 'dart:math';
import 'package:animations/animations.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:filcnaplo/helpers/average_helper.dart';
@@ -56,6 +57,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
// Providers
late GradeProvider gradeProvider;
late GradeCalculatorProvider calculatorProvider;
late SettingsProvider settingsProvider;
late double average;
late Widget gradeGraph;
@@ -149,6 +151,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
Widget build(BuildContext context) {
gradeProvider = Provider.of<GradeProvider>(context);
calculatorProvider = Provider.of<GradeCalculatorProvider>(context);
settingsProvider = Provider.of<SettingsProvider>(context);
List<Grade> subjectGrades = getSubjectGrades(widget.subject).toList();
average = AverageHelper.averageEvals(subjectGrades);
@@ -260,7 +263,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
subject: widget.subject, context: context),
scrollController: _scrollController,
title: widget.subject.renamedTo ?? widget.subject.name.capital(),
italic: widget.subject.isRenamed,
italic: settingsProvider.renamedSubjectsItalics && widget.subject.isRenamed,
child: SubjectGradesContainer(
child: CupertinoScrollbar(
child: ListView.builder(
@@ -277,7 +280,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
void gradeCalc(BuildContext context) {
// Scroll to the top of the page
_scrollController.animateTo(75,
_scrollController.animateTo(100,
duration: const Duration(milliseconds: 500), curve: Curves.ease);
calculatorProvider.clear();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -484,8 +484,9 @@ class _BellDelaySettingState extends State<BellDelaySetting>
Duration sdiff = lesson.start.difference(now);
Duration ediff = lesson.end.difference(now);
if (closest == null || sdiff.abs() < closest.abs())
if (closest == null || sdiff.abs() < closest.abs()) {
closest = sdiff;
}
if (ediff.abs() < closest.abs()) closest = ediff;
}
if (closest != null) {

View File

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

View File

@@ -66,6 +66,11 @@ extension SettingsLocalization on String {
"Adaptive Theme": "Adaptive Theme",
"presentation": "Presentation mode",
"uwufymode": "UwU-fied mode (hungarian)",
"devmoretaps": "You are %s taps away from Developer Mode.",
"devactivated": "Developer Mode successfully activated.",
"devsettings": "Developer Settings",
"devmode": "Developer Mode",
"copy_jwt": "Copy JWT",
},
"hu_hu": {
"personal_details": "Személyes információk",
@@ -130,6 +135,11 @@ extension SettingsLocalization on String {
"Adaptive Theme": "Adaptív téma",
"presentation": "Bemutató mód",
"uwufymode": "UwU mód (magyar)",
"devmoretaps": "Még %s koppintásra vagy a Fejlesztői módtól.",
"devactivated": "Fejlesztői mód sikeresen aktiválva.",
"devsettings": "Fejlesztői Beállítások",
"devmode": "Fejlesztői mód",
"copy_jwt": "JWT másolása",
},
"de_de": {
"personal_details": "Persönliche Angaben",
@@ -193,6 +203,11 @@ extension SettingsLocalization on String {
"Adaptive Theme": "Adaptive Theme",
"presentation": "Präsentationsmodus",
"uwufymode": "UwU-Modus (ungarisch)",
"devmoretaps": "Sie sind %s Taps vom Entwicklermodus entfernt.",
"devactivated": "Entwicklermodus erfolgreich aktiviert.",
"devsettings": "Entwickleroptionen",
"devmode": "Entwicklermodus",
"copy_jwt": "JWT kopieren",
},
};

View File

@@ -5,6 +5,6 @@ class GradesBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column();
return const Column();
}
}

View File

@@ -5,6 +5,6 @@ class PersonalityBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column();
return const Column();
}
}

View File

@@ -39,7 +39,7 @@ dependencies:
flutter_svg: ^1.1.6
background_fetch: ^1.1.5
wtf_sliding_sheet: ^1.0.0
package_info_plus: ^3.1.2
package_info_plus: ^4.0.2
dev_dependencies:
flutter_lints: ^1.0.0

View File

@@ -24,7 +24,8 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
setState(() {
manualActivationLoading = true;
});
final result = await context.read<PremiumProvider>().auth.finishAuth(data.text!);
final result =
await context.read<PremiumProvider>().auth.finishAuth(data.text!);
setState(() {
manualActivationLoading = false;
});
@@ -63,24 +64,27 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
),
const SizedBox(height: 12.0),
Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
child: Padding(
padding: const EdgeInsets.all(20.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14.0)),
child: const Padding(
padding: EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: const [
Icon(FeatherIcons.alertTriangle, size: 20.0, color: Colors.orange),
children: [
Icon(FeatherIcons.alertTriangle,
size: 20.0, color: Colors.orange),
SizedBox(width: 12.0),
Text(
"Figyelem!",
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),
style: TextStyle(
fontSize: 18.0, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 6.0),
const Text(
SizedBox(height: 6.0),
Text(
"Csak akkor érzékeli a Filc a támogatói státuszod, ha nem állítod privátra!",
style: TextStyle(fontSize: 16.0),
),
@@ -90,24 +94,27 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
),
const SizedBox(height: 12.0),
Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
child: Padding(
padding: const EdgeInsets.all(20.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14.0)),
child: const Padding(
padding: EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: const [
Icon(FeatherIcons.alertTriangle, size: 20.0, color: Colors.orange),
children: [
Icon(FeatherIcons.alertTriangle,
size: 20.0, color: Colors.orange),
SizedBox(width: 12.0),
Text(
"Figyelem!",
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),
style: TextStyle(
fontSize: 18.0, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 6.0),
const Text(
SizedBox(height: 6.0),
Text(
"Ha friss támogató vagy, 5-10 percbe telhet az aktiválás. Kérlek gyere vissza később, és próbáld újra!",
style: TextStyle(fontSize: 16.0),
),
@@ -117,7 +124,8 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
),
const SizedBox(height: 12.0),
Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14.0)),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
@@ -125,15 +133,20 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
children: [
const Text(
"Ha bejelentkezés után nem lép vissza az alkalmazásba automatikusan, aktiváld a támogatásod manuálisan",
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w500),
style:
TextStyle(fontSize: 15.0, fontWeight: FontWeight.w500),
),
const SizedBox(height: 6.0),
Center(
child: TextButton.icon(
onPressed: onManualActivation,
style: ButtonStyle(
foregroundColor: MaterialStatePropertyAll(Theme.of(context).colorScheme.secondary),
overlayColor: MaterialStatePropertyAll(Theme.of(context).colorScheme.secondary.withOpacity(.1)),
foregroundColor: MaterialStatePropertyAll(
Theme.of(context).colorScheme.secondary),
overlayColor: MaterialStatePropertyAll(Theme.of(context)
.colorScheme
.secondary
.withOpacity(.1)),
),
icon: manualActivationLoading
? const SizedBox(
@@ -164,8 +177,10 @@ class _ActivationDashboardState extends State<ActivationDashboard> {
Navigator.of(context).pop();
},
style: ButtonStyle(
foregroundColor: MaterialStatePropertyAll(AppColors.of(context).text),
overlayColor: MaterialStatePropertyAll(AppColors.of(context).text.withOpacity(.1)),
foregroundColor:
MaterialStatePropertyAll(AppColors.of(context).text),
overlayColor: MaterialStatePropertyAll(
AppColors.of(context).text.withOpacity(.1)),
),
icon: const Icon(FeatherIcons.arrowLeft, size: 20.0),
label: const Text(

View File

@@ -22,7 +22,8 @@ import 'package:provider/provider.dart';
import 'modify_subject_names.i18n.dart';
class MenuRenamedSubjects extends StatelessWidget {
const MenuRenamedSubjects({Key? key, required this.settings}) : super(key: key);
const MenuRenamedSubjects({Key? key, required this.settings})
: super(key: key);
final SettingsProvider settings;
@@ -31,8 +32,10 @@ class MenuRenamedSubjects extends StatelessWidget {
return PanelButton(
padding: const EdgeInsets.only(left: 14.0),
onPressed: () {
if (!Provider.of<PremiumProvider>(context, listen: false).hasScope(PremiumScopes.renameSubjects)) {
PremiumLockedFeatureUpsell.show(context: context, feature: PremiumFeature.subjectrename);
if (!Provider.of<PremiumProvider>(context, listen: false)
.hasScope(PremiumScopes.renameSubjects)) {
PremiumLockedFeatureUpsell.show(
context: context, feature: PremiumFeature.subjectrename);
return;
}
@@ -42,23 +45,32 @@ class MenuRenamedSubjects extends StatelessWidget {
},
title: Text(
"rename_subjects".i18n,
style: TextStyle(color: AppColors.of(context).text.withOpacity(settings.renamedSubjectsEnabled ? 1.0 : .5)),
style: TextStyle(
color: AppColors.of(context)
.text
.withOpacity(settings.renamedSubjectsEnabled ? 1.0 : .5)),
),
leading: settings.renamedSubjectsEnabled
? const Icon(FeatherIcons.penTool)
: Icon(FeatherIcons.penTool, color: AppColors.of(context).text.withOpacity(.25)),
: Icon(FeatherIcons.penTool,
color: AppColors.of(context).text.withOpacity(.25)),
trailingDivider: true,
trailing: Switch(
onChanged: (v) async {
if (!Provider.of<PremiumProvider>(context, listen: false).hasScope(PremiumScopes.renameSubjects)) {
PremiumLockedFeatureUpsell.show(context: context, feature: PremiumFeature.subjectrename);
if (!Provider.of<PremiumProvider>(context, listen: false)
.hasScope(PremiumScopes.renameSubjects)) {
PremiumLockedFeatureUpsell.show(
context: context, feature: PremiumFeature.subjectrename);
return;
}
settings.update(renamedSubjectsEnabled: v);
await Provider.of<GradeProvider>(context, listen: false).convertBySettings();
await Provider.of<TimetableProvider>(context, listen: false).convertBySettings();
await Provider.of<AbsenceProvider>(context, listen: false).convertBySettings();
await Provider.of<GradeProvider>(context, listen: false)
.convertBySettings();
await Provider.of<TimetableProvider>(context, listen: false)
.convertBySettings();
await Provider.of<AbsenceProvider>(context, listen: false)
.convertBySettings();
},
value: settings.renamedSubjectsEnabled,
activeColor: Theme.of(context).colorScheme.secondary,
@@ -82,11 +94,16 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
late List<Subject> subjects;
late UserProvider user;
late DatabaseProvider dbProvider;
late SettingsProvider settings;
@override
void initState() {
super.initState();
subjects = Provider.of<GradeProvider>(context, listen: false).grades.map((e) => e.subject).toSet().toList()
subjects = Provider.of<GradeProvider>(context, listen: false)
.grades
.map((e) => e.subject)
.toSet()
.toList()
..sort((a, b) => a.name.compareTo(b.name));
user = Provider.of<UserProvider>(context, listen: false);
dbProvider = Provider.of<DatabaseProvider>(context, listen: false);
@@ -101,7 +118,8 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
context: context,
builder: (context) => StatefulBuilder(builder: (context, setS) {
return AlertDialog(
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(14.0))),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(14.0))),
title: Text("rename_subject".i18n),
content: Column(
mainAxisSize: MainAxisSize.min,
@@ -161,13 +179,18 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
border: Border.all(color: Colors.grey, width: 2),
borderRadius: BorderRadius.circular(12.0),
),
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 8.0),
padding: const EdgeInsets.symmetric(
vertical: 12.0, horizontal: 8.0),
child: Text(
selectedSubjectId == null ? "select_subject".i18n : subjects.firstWhere((element) => element.id == selectedSubjectId).name,
style: Theme.of(context)
.textTheme
.titleSmall!
.copyWith(fontWeight: FontWeight.w700, color: AppColors.of(context).text.withOpacity(0.75)),
selectedSubjectId == null
? "select_subject".i18n
: subjects
.firstWhere(
(element) => element.id == selectedSubjectId)
.name,
style: Theme.of(context).textTheme.titleSmall!.copyWith(
fontWeight: FontWeight.w700,
color: AppColors.of(context).text.withOpacity(0.75)),
overflow: TextOverflow.ellipsis,
maxLines: 2,
textAlign: TextAlign.center,
@@ -182,11 +205,13 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
controller: _subjectName,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.grey, width: 1.5),
borderSide:
const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.grey, width: 1.5),
borderSide:
const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
@@ -226,10 +251,14 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
final renamedSubs = await fetchRenamedSubjects();
renamedSubs[selectedSubjectId!] = _subjectName.text;
await dbProvider.userStore.storeRenamedSubjects(renamedSubs, userId: user.id!);
await Provider.of<GradeProvider>(context, listen: false).convertBySettings();
await Provider.of<TimetableProvider>(context, listen: false).convertBySettings();
await Provider.of<AbsenceProvider>(context, listen: false).convertBySettings();
await dbProvider.userStore
.storeRenamedSubjects(renamedSubs, userId: user.id!);
await Provider.of<GradeProvider>(context, listen: false)
.convertBySettings();
await Provider.of<TimetableProvider>(context, listen: false)
.convertBySettings();
await Provider.of<AbsenceProvider>(context, listen: false)
.convertBySettings();
}
Navigator.of(context).pop(true);
setState(() {});
@@ -246,6 +275,7 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
@override
Widget build(BuildContext context) {
settings = Provider.of<SettingsProvider>(context);
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
@@ -262,6 +292,18 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Panel(
child: PanelButton(
title: Text("italics_toggle".i18n),
trailing: Switch(
value: settings.renamedSubjectsItalics,
onChanged: (value) =>
settings.update(renamedSubjectsItalics: value),
)),
),
const SizedBox(
height: 20,
),
InkWell(
onTap: showRenameDialog,
borderRadius: BorderRadius.circular(12.0),
@@ -271,7 +313,8 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
border: Border.all(color: Colors.grey, width: 2),
borderRadius: BorderRadius.circular(12.0),
),
padding: const EdgeInsets.symmetric(vertical: 18.0, horizontal: 12.0),
padding: const EdgeInsets.symmetric(
vertical: 18.0, horizontal: 12.0),
child: Center(
child: Text(
"rename_new_subject".i18n,
@@ -290,14 +333,17 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
FutureBuilder<Map<String, String>>(
future: fetchRenamedSubjects(),
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data!.isEmpty) return Container();
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Container();
}
return Panel(
title: Text("renamed_subjects".i18n),
child: Column(
children: snapshot.data!.keys.map(
(key) {
Subject? subject = subjects.firstWhere((element) => key == element.id);
Subject? subject = subjects
.firstWhere((element) => key == element.id);
String renameTo = snapshot.data![key]!;
return RenamedSubjectItem(
subject: subject,
@@ -311,9 +357,12 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
},
removeCallback: () {
setState(() {
Map<String, String> subs = Map.from(snapshot.data!);
Map<String, String> subs =
Map.from(snapshot.data!);
subs.remove(key);
dbProvider.userStore.storeRenamedSubjects(subs, userId: user.id!);
dbProvider.userStore.storeRenamedSubjects(
subs,
userId: user.id!);
});
},
);
@@ -349,11 +398,14 @@ class RenamedSubjectItem extends StatelessWidget {
return ListTile(
minLeadingWidth: 32.0,
dense: true,
contentPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
visualDensity: VisualDensity.compact,
onTap: () {},
leading: Icon(SubjectIcon.resolveVariant(subject: subject, context: context), color: AppColors.of(context).text.withOpacity(.75)),
leading: Icon(
SubjectIcon.resolveVariant(subject: subject, context: context),
color: AppColors.of(context).text.withOpacity(.75)),
title: InkWell(
onTap: modifyCallback,
child: Column(
@@ -361,7 +413,10 @@ class RenamedSubjectItem extends StatelessWidget {
children: [
Text(
subject.name.capital(),
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 14, color: AppColors.of(context).text.withOpacity(.75)),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
color: AppColors.of(context).text.withOpacity(.75)),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
@@ -376,7 +431,8 @@ class RenamedSubjectItem extends StatelessWidget {
),
trailing: InkWell(
onTap: removeCallback,
child: Icon(FeatherIcons.trash, color: AppColors.of(context).red.withOpacity(.75)),
child: Icon(FeatherIcons.trash,
color: AppColors.of(context).red.withOpacity(.75)),
),
);
}

View File

@@ -13,6 +13,7 @@ extension SettingsLocalization on String {
"cancel": "Cancel",
"done": "Done",
"rename_new_subject": "Rename New Subject",
"italics_toggle": "Toggle Italics",
},
"hu_hu": {
"renamed_subjects": "Átnevezett Tantárgyaid",
@@ -24,6 +25,7 @@ extension SettingsLocalization on String {
"cancel": "Mégse",
"done": "Kész",
"rename_new_subject": "Új Tantárgy átnevezése",
"italics_toggle": "Dőlt betűs megjelenítés",
},
"de_de": {
"renamed_subjects": "Umbenannte Fächer",
@@ -35,6 +37,7 @@ extension SettingsLocalization on String {
"cancel": "Abbrechen",
"done": "Erledigt",
"rename_new_subject": "Neues Fach umbenennen",
"italics_toggle": "Kursivschrift umschalten",
},
};

View File

@@ -9,11 +9,15 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.i18n.dart';
class PremiumFSTimetableButton extends StatelessWidget {
const PremiumFSTimetableButton({Key? key, required this.controller}) : super(key: key);
const PremiumFSTimetableButton(
{Key? key, required this.controller, required this.tabcontroller})
: super(key: key);
final TimetableController controller;
final TabController tabcontroller;
@override
Widget build(BuildContext context) {
@@ -22,19 +26,32 @@ class PremiumFSTimetableButton extends StatelessWidget {
child: IconButton(
splashRadius: 24.0,
onPressed: () {
if (!Provider.of<PremiumProvider>(context, listen: false).hasScope(PremiumScopes.fsTimetable)) {
PremiumLockedFeatureUpsell.show(context: context, feature: PremiumFeature.weeklytimetable);
if (!Provider.of<PremiumProvider>(context, listen: false)
.hasScope(PremiumScopes.fsTimetable)) {
PremiumLockedFeatureUpsell.show(
context: context, feature: PremiumFeature.weeklytimetable);
return;
}
// If timetable empty, show empty
if (tabcontroller.length == 0) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("empty_timetable".i18n),
duration: const Duration(seconds: 2),
));
return;
}
Navigator.of(context, rootNavigator: true)
.push(PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => PremiumFSTimetable(
pageBuilder: (context, animation, secondaryAnimation) =>
PremiumFSTimetable(
controller: controller,
),
))
.then((_) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp]);
setSystemChrome(context);
});
},