Compare commits

...

34 Commits
3.3.4 ... 3.4.2

Author SHA1 Message Date
55nknown
2afad1cc89 update gitmodules 2022-11-21 21:16:52 +01:00
55nknown
94b2fe3cb7 fix app not starting when no users 2022-11-21 21:06:46 +01:00
55nknown
8a75642ec9 update gitmodules 2022-11-21 21:01:34 +01:00
55nknown
f4b03b0304 add premium submodule 2022-11-21 20:08:28 +01:00
annon
5281b9f202 Update README.md 2022-11-21 13:33:17 +01:00
55nknown
db58b54754 bump version 2022-11-21 13:27:39 +01:00
55nknown
7de3d211bb set user to last selected one 2022-11-21 12:41:22 +01:00
55nknown
d3e5fc99ea html fallback 2022-11-21 10:11:24 +01:00
55nknown
17cffc0576 revert goalplanner avghelper 2022-11-21 09:34:51 +01:00
55nknown
f38ff3b862 fix build 2022-11-20 23:03:50 +01:00
55nknown
89e67c369e fix build script 2022-11-20 22:26:27 +01:00
55nknown
6dd68c8f5d changelog 2022-11-20 22:18:21 +01:00
55nknown
97b01e9f14 fix build script 2022-11-20 22:03:46 +01:00
55nknown
556ba9d289 disable widget 2022-11-20 21:55:21 +01:00
55nknown
64868e85f3 widget setup 2022-11-20 21:02:32 +01:00
55nknown
30ac155b4b grade value nocolor 2022-11-20 20:18:58 +01:00
55nknown
36bd679644 cleanup 2022-11-20 19:27:06 +01:00
55nknown
75b03b95bc add premium backend 2022-11-20 19:25:04 +01:00
55nknown
ac18cf62c3 custom theme 2022-11-20 11:55:34 +01:00
55nknown
3619a7a4a7 nickname 2022-11-20 11:54:54 +01:00
55nknown
a71b365e4a deps 2022-11-20 11:38:31 +01:00
55nknown
20fa9a8aef migrate goalplanner 2022-11-17 13:44:12 +01:00
annon
da12ac8646 Goal planner backend (#132)
* refactor avghelper

* #59 backend

Co-authored-by: DarK-rtfm <44683230+DarK-rtfm@users.noreply.github.com>
2022-11-17 13:41:16 +01:00
55nknown
4b40692fe1 logic 2022-11-16 07:42:02 +01:00
55nknown
26f65a4144 fix livecard filter label lessons #128 2022-11-16 07:31:40 +01:00
55nknown
f8cfa04d04 nickname changing 2022-11-15 09:00:38 +01:00
55nknown
82671f5ec9 fix #125 2022-11-14 14:05:51 +01:00
55nknown
5d37de897f icon packs 2022-11-14 10:08:05 +01:00
55nknown
fcb1d8d6d9 fix #122 2022-11-14 09:14:08 +01:00
55nknown
446fad4c5f fix #124 2022-11-14 08:09:34 +01:00
55nknown
0f799375a1 fix #118 2022-11-14 08:02:50 +01:00
55nknown
29aa356cd0 Merge branch 'master' of https://github.com/filc/naplo 2022-11-14 07:39:07 +01:00
55nknown
3abaf2f96a ios livecard id 2022-11-14 07:38:49 +01:00
annon
5b48847cf2 Update FUNDING.yml 2022-11-07 18:19:11 +01:00
45 changed files with 437 additions and 190 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1 +1 @@
patreon: filcnaplo github: filc

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
# See https://www.dartlang.org/guides/libraries/private-files # See https://www.dartlang.org/guides/libraries/private-files
termek.txt termek.txt
.DS_Store
# Files and directories created by pub # Files and directories created by pub
.dart_tool/ .dart_tool/

4
.gitmodules vendored
View File

@@ -9,3 +9,7 @@
[submodule "Kreta API"] [submodule "Kreta API"]
path = filcnaplo_kreta_api path = filcnaplo_kreta_api
url = https://github.com/filc/kreten url = https://github.com/filc/kreten
[submodule "Premium"]
path = filcnaplo_premium
url = git@github.com:filc/premium

View File

@@ -5,7 +5,7 @@
#### Nem hivatalos e-napló alkalmazás az eKRÉTA rendszerhez #### Nem hivatalos e-napló alkalmazás az eKRÉTA rendszerhez
[![Downloads](https://img.shields.io/github/downloads-pre/filc/naplo/latest/total?color=%23&label=Downloads&logo=github&sort=semver)](https://github.com/filc/naplo/releases) &nbsp; [![discord](https://img.shields.io/discord/712698455193157643?label=Discord)](http://filcnaplo.hu/discord) &nbsp; [![Codemagic build status](https://api.codemagic.io/apps/612cc79b35b443d1b2c638ec/612cc79b35b443d1b2c638eb/status_badge.svg)](https://codemagic.io/apps/612cc79b35b443d1b2c638ec/612cc79b35b443d1b2c638eb/latest_build) [![Downloads](https://img.shields.io/github/downloads-pre/filc/naplo/latest/total?color=%23&label=Downloads&logo=github&sort=semver)](https://github.com/filc/naplo/releases) &nbsp; [![discord](https://img.shields.io/discord/712698455193157643?label=Discord)](http://filcnaplo.hu/discord)
## Setup ## Setup

View File

@@ -1,3 +1,4 @@
What's new: What's new:
- Hibajavítások 🐛🐛🐛🐛 - Hibajavítások 🐛
- **Megérkezett a Filc Premium!** ✨

View File

@@ -26,13 +26,8 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties() def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file("$System.env.ANDROID_SIGNING") def keystorePropertiesFile = rootProject.file("$System.env.HOME/keys/filc3.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
} else {
keystoreProperties.load(new FileInputStream(rootProject.file("signing/signing.properties")))
}
android { android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion

View File

@@ -6,8 +6,47 @@
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with https://api.filcnaplo.hu -->
<data
android:scheme="https"
android:host="api.filcnaplo.hu"
android:pathPrefix="/callback" />
</intent-filter>
</activity> </activity>
<meta-data android:name="flutterEmbedding" android:value="2" /> <meta-data android:name="flutterEmbedding" android:value="2" />
<!-- <receiver android:name=".WidgetTimetable.widget_timetable"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="list_widget.ACTION_WIDGET_CLICK_NAV_LEFT" />
<action android:name="list_widget.ACTION_WIDGET_CLICK_NAV_RIGHT" />
<action android:name="list_widget.ACTION_WIDGET_CLICK_NAV_TODAY" />
<action android:name="list_widget.ACTION_WIDGET_CLICK_NAV_REFRESH" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/home_widget_test_info" />
</receiver>
<service android:name=".WidgetTimetable.widget_timetable_service"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<receiver android:name="es.antonborri.home_widget.HomeWidgetBackgroundReceiver"
android:exported="true">
<intent-filter>
<action android:name="es.antonborri.home_widget.action.BACKGROUND" />
</intent-filter>
</receiver>
<service android:name="es.antonborri.home_widget.HomeWidgetBackgroundService"
android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true" />
-->
</application> </application>
<!-- Permissions --> <!-- Permissions -->

View File

@@ -1,4 +0,0 @@
keyAlias=test
keyPassword=test123
storeFile=../signing/signing.keystore
storePassword=test123

Binary file not shown.

10
filcnaplo/build.sh Normal file → Executable file
View File

@@ -1,4 +1,4 @@
#!/bin/fish #!/usr/bin/env fish
# With build number # With build number
function get_version_bn function get_version_bn
@@ -9,11 +9,5 @@ function get_version
cat pubspec.yaml | grep version: | cut -d' ' -f2 | cut -d+ -f1 cat pubspec.yaml | grep version: | cut -d' ' -f2 | cut -d+ -f1
end end
if test -e /mnt/enc/keys/filc3.properties flutter build apk --release --dart-define=APPVER=(get_version) --no-tree-shake-icons && \
set -x ANDROID_SIGNING /mnt/enc/keys/filc3.properties
end
flutter build apk --release --dart-define=APPVER=(get_version) --no-tree-shake-icons
cp -v "build/app/outputs/flutter-apk/app-release.apk" ~/"Desktop/hu.filc.naplo_"(get_version_bn).apk cp -v "build/app/outputs/flutter-apk/app-release.apk" ~/"Desktop/hu.filc.naplo_"(get_version_bn).apk
notify-send "Flutter" "Apk build done."

View File

@@ -42,6 +42,8 @@ PODS:
- FMDB (2.7.5): - FMDB (2.7.5):
- FMDB/standard (= 2.7.5) - FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5) - FMDB/standard (2.7.5)
- home_widget (0.0.1):
- Flutter
- live_activities (0.0.1): - live_activities (0.0.1):
- Flutter - Flutter
- open_file (0.0.1): - open_file (0.0.1):
@@ -62,8 +64,12 @@ PODS:
- Flutter - Flutter
- FMDB (>= 2.7.5) - FMDB (>= 2.7.5)
- SwiftyGif (5.4.3) - SwiftyGif (5.4.3)
- uni_links (0.0.1):
- Flutter
- url_launcher_ios (0.0.1): - url_launcher_ios (0.0.1):
- Flutter - Flutter
- workmanager (0.0.1):
- Flutter
DEPENDENCIES: DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
@@ -71,6 +77,7 @@ DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_custom_tabs (from `.symlinks/plugins/flutter_custom_tabs/ios`) - flutter_custom_tabs (from `.symlinks/plugins/flutter_custom_tabs/ios`)
- home_widget (from `.symlinks/plugins/home_widget/ios`)
- live_activities (from `.symlinks/plugins/live_activities/ios`) - live_activities (from `.symlinks/plugins/live_activities/ios`)
- open_file (from `.symlinks/plugins/open_file/ios`) - open_file (from `.symlinks/plugins/open_file/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
@@ -78,7 +85,9 @@ DEPENDENCIES:
- quick_actions_ios (from `.symlinks/plugins/quick_actions_ios/ios`) - quick_actions_ios (from `.symlinks/plugins/quick_actions_ios/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`)
- uni_links (from `.symlinks/plugins/uni_links/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- workmanager (from `.symlinks/plugins/workmanager/ios`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
@@ -99,6 +108,8 @@ EXTERNAL SOURCES:
:path: Flutter :path: Flutter
flutter_custom_tabs: flutter_custom_tabs:
:path: ".symlinks/plugins/flutter_custom_tabs/ios" :path: ".symlinks/plugins/flutter_custom_tabs/ios"
home_widget:
:path: ".symlinks/plugins/home_widget/ios"
live_activities: live_activities:
:path: ".symlinks/plugins/live_activities/ios" :path: ".symlinks/plugins/live_activities/ios"
open_file: open_file:
@@ -113,8 +124,12 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/share_plus/ios" :path: ".symlinks/plugins/share_plus/ios"
sqflite: sqflite:
:path: ".symlinks/plugins/sqflite/ios" :path: ".symlinks/plugins/sqflite/ios"
uni_links:
:path: ".symlinks/plugins/uni_links/ios"
url_launcher_ios: url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios" :path: ".symlinks/plugins/url_launcher_ios/ios"
workmanager:
:path: ".symlinks/plugins/workmanager/ios"
CHECKOUT OPTIONS: CHECKOUT OPTIONS:
DKImagePickerController: DKImagePickerController:
@@ -129,6 +144,7 @@ SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89 flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
home_widget: 2829415127ee92e876f816cbbe44c0b6601b8a37
live_activities: 9ff56a06a2d43ecd68f56deeed13b18a8304789c live_activities: 9ff56a06a2d43ecd68f56deeed13b18a8304789c
open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
@@ -139,7 +155,9 @@ SPEC CHECKSUMS:
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
PODFILE CHECKSUM: 862f939bb7e5390bdb8b2534eb81a9457ea9fbdc PODFILE CHECKSUM: 862f939bb7e5390bdb8b2534eb81a9457ea9fbdc

View File

@@ -517,7 +517,7 @@
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo.livecard; PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@@ -557,7 +557,7 @@
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo.livecard; PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@@ -596,7 +596,7 @@
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo.livecard; PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;

View File

@@ -4,5 +4,9 @@
<dict> <dict>
<key>aps-environment</key> <key>aps-environment</key>
<string>development</string> <string>development</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:api.filcnaplo.hu</string>
</array>
</dict> </dict>
</plist> </plist>

View File

@@ -21,6 +21,8 @@ class FilcAPI {
// Private API // Private API
static const config = "https://api.filcnaplo.hu/config"; static const config = "https://api.filcnaplo.hu/config";
static const reportApi = "https://api.filcnaplo.hu/report"; static const reportApi = "https://api.filcnaplo.hu/report";
static const premiumApi = "https://api.filcnaplo.hu/premium/activate";
static const premiumScopesApi = "https://api.filcnaplo.hu/premium/scopes";
// Updates // Updates
static const repo = "filc/naplo"; static const repo = "filc/naplo";
@@ -121,19 +123,16 @@ class FilcAPI {
return null; return null;
} }
static Future<http.StreamedResponse?> downloadRelease(Release release) { static Future<http.StreamedResponse?> downloadRelease(ReleaseDownload release) {
if (release.downloads.isNotEmpty) {
try { try {
var client = http.Client(); var client = http.Client();
var request = http.Request('GET', Uri.parse(release.downloads.first)); var request = http.Request('GET', Uri.parse(release.url));
return client.send(request); return client.send(request);
} catch (error) { } catch (error) {
print("ERROR: FilcAPI.downloadRelease: $error"); print("ERROR: FilcAPI.downloadRelease: $error");
}
}
return Future.value(null); return Future.value(null);
} }
}
static Future<void> sendReport(ErrorReport report) async { static Future<void> sendReport(ErrorReport report) async {
try { try {

View File

@@ -22,8 +22,8 @@ class LiveCardProvider extends ChangeNotifier {
LiveCardState currentState = LiveCardState.empty; LiveCardState currentState = LiveCardState.empty;
late Timer _timer; late Timer _timer;
late final TimetableProvider _lessonProvider; late final TimetableProvider _timetable;
late final SettingsProvider _settingsProvider; late final SettingsProvider _settings;
late Duration _delay; late Duration _delay;
@@ -32,13 +32,13 @@ class LiveCardProvider extends ChangeNotifier {
Map<String, String> _lastActivity = {}; Map<String, String> _lastActivity = {};
LiveCardProvider({ LiveCardProvider({
required TimetableProvider lessonProvider, required TimetableProvider timetable,
required SettingsProvider settingsProvider, required SettingsProvider settings,
}) : _lessonProvider = lessonProvider, }) : _timetable = timetable,
_settingsProvider = settingsProvider { _settings = settings {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) => update()); _timer = Timer.periodic(const Duration(seconds: 1), (timer) => update());
lessonProvider.restore().then((_) => update()); timetable.restore().then((_) => update());
_delay = settingsProvider.bellDelayEnabled ? Duration(seconds: settingsProvider.bellDelay) : Duration.zero; _delay = settings.bellDelayEnabled ? Duration(seconds: settings.bellDelay) : Duration.zero;
} }
@override @override
@@ -73,7 +73,7 @@ class LiveCardProvider extends ChangeNotifier {
switch (currentState) { switch (currentState) {
case LiveCardState.duringLesson: case LiveCardState.duringLesson:
return { return {
"icon": currentLesson != null ? SubjectIcon.resolve(subject: currentLesson?.subject).name : "book", "icon": currentLesson != null ? SubjectIcon.resolveName(subject: currentLesson?.subject) : "book",
"index": currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "", "index": currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "",
"title": currentLesson != null ? ShortSubject.resolve(subject: currentLesson?.subject).capital() : "", "title": currentLesson != null ? ShortSubject.resolve(subject: currentLesson?.subject).capital() : "",
"subtitle": currentLesson?.room.replaceAll("_", " ") ?? "", "subtitle": currentLesson?.room.replaceAll("_", " ") ?? "",
@@ -127,19 +127,20 @@ class LiveCardProvider extends ChangeNotifier {
} }
} }
List<Lesson> today = _today(_lessonProvider); List<Lesson> today = _today(_timetable);
if (today.isEmpty) { if (today.isEmpty) {
await _lessonProvider.fetch(week: Week.current()); await _timetable.fetch(week: Week.current());
today = _today(_lessonProvider); today = _today(_timetable);
} }
_delay = _settingsProvider.bellDelayEnabled ? Duration(seconds: _settingsProvider.bellDelay) : Duration.zero; _delay = _settings.bellDelayEnabled ? Duration(seconds: _settings.bellDelay) : Duration.zero;
final now = _now().add(_delay); final now = _now().add(_delay);
// Filter cancelled lessons #20 // Filter cancelled lessons #20
today = today.where((lesson) => lesson.status?.name != "Elmaradt").toList(); // Filter label lessons #128
today = today.where((lesson) => lesson.status?.name != "Elmaradt" && lesson.subject.id != '' && !lesson.isEmpty).toList();
if (today.isNotEmpty) { if (today.isNotEmpty) {
// sort // sort

View File

@@ -41,7 +41,7 @@ class NewsProvider extends ChangeNotifier {
} }
_state = state_; _state = state_;
Provider.of<SettingsProvider>(_context, listen: false).update(_context, newsState: _state); Provider.of<SettingsProvider>(_context, listen: false).update(newsState: _state);
} }
Future<void> fetch() async { Future<void> fetch() async {
@@ -53,7 +53,7 @@ class NewsProvider extends ChangeNotifier {
if (_fresh < 0) { if (_fresh < 0) {
_state = news_.length; _state = news_.length;
Provider.of<SettingsProvider>(_context, listen: false).update(_context, newsState: _state); Provider.of<SettingsProvider>(_context, listen: false).update(newsState: _state);
} }
_fresh = max(_fresh, 0); _fresh = max(_fresh, 0);
@@ -72,7 +72,7 @@ class NewsProvider extends ChangeNotifier {
_fresh--; _fresh--;
_state++; _state++;
Provider.of<SettingsProvider>(_context, listen: false).update(_context, newsState: _state); Provider.of<SettingsProvider>(_context, listen: false).update(newsState: _state);
if (_fresh > 0) { if (_fresh > 0) {
show = true; show = true;

View File

@@ -7,9 +7,12 @@ enum Status { network, maintenance, syncing }
class StatusProvider extends ChangeNotifier { class StatusProvider extends ChangeNotifier {
final List<Status> _stack = []; final List<Status> _stack = [];
double _progress = 0.0; double _progress = 0.0;
ConnectivityResult _networkType = ConnectivityResult.none;
ConnectivityResult get networkType => _networkType;
StatusProvider() { StatusProvider() {
_handleNetworkChanges(); _handleNetworkChanges();
Connectivity().checkConnectivity().then((value) => _networkType = value);
} }
Status? getStatus() => _stack.isNotEmpty ? _stack[0] : null; Status? getStatus() => _stack.isNotEmpty ? _stack[0] : null;
@@ -18,6 +21,7 @@ class StatusProvider extends ChangeNotifier {
void _handleNetworkChanges() { void _handleNetworkChanges() {
Connectivity().onConnectivityChanged.listen((event) { Connectivity().onConnectivityChanged.listen((event) {
_networkType = event;
if (event == ConnectivityResult.none) { if (event == ConnectivityResult.none) {
if (!_stack.contains(Status.network)) { if (!_stack.contains(Status.network)) {
_stack.insert(0, Status.network); _stack.insert(0, Status.network);

View File

@@ -1,3 +1,4 @@
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/models/user.dart'; import 'package:filcnaplo/models/user.dart';
import 'package:filcnaplo_kreta_api/models/student.dart'; import 'package:filcnaplo_kreta_api/models/student.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@@ -15,9 +16,16 @@ class UserProvider with ChangeNotifier {
String? get password => user?.password; String? get password => user?.password;
Role? get role => user?.role; Role? get role => user?.role;
Student? get student => user?.student; Student? get student => user?.student;
String? get nickname => user?.nickname;
String? get displayName => user?.displayName;
final SettingsProvider _settings;
UserProvider({required SettingsProvider settings}) : _settings = settings;
void setUser(String userId) { void setUser(String userId) {
_selectedUserId = userId; _selectedUserId = userId;
_settings.update(lastAccountId: userId);
notifyListeners(); notifyListeners();
} }

View File

@@ -45,15 +45,14 @@ import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/api/providers/update_provider.dart'; import 'package:filcnaplo/api/providers/update_provider.dart';
import 'package:filcnaplo_mobile_ui/pages/grades/calculator/grade_calculator_provider.dart'; import 'package:filcnaplo_mobile_ui/pages/grades/calculator/grade_calculator_provider.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
class App extends StatelessWidget { class App extends StatelessWidget {
final SettingsProvider settings; final SettingsProvider settings;
final UserProvider user; final UserProvider user;
final DatabaseProvider database; final DatabaseProvider database;
App({Key? key, required this.database, required this.settings, required this.user}) : super(key: key) { const App({Key? key, required this.database, required this.settings, required this.user}) : super(key: key);
if (user.getUsers().isNotEmpty) user.setUser(user.getUsers().first.id);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -62,20 +61,23 @@ class App extends StatelessWidget {
// Set high refresh mode #28 // Set high refresh mode #28
if (Platform.isAndroid) FlutterDisplayMode.setHighRefreshRate(); if (Platform.isAndroid) FlutterDisplayMode.setHighRefreshRate();
WidgetsBinding.instance.addPostFrameCallback((_) {
FilcAPI.getConfig(settings).then((Config? config) {
if (config != null) settings.update(context, database: database, config: config);
});
});
CorePalette? corePalette; CorePalette? corePalette;
final status = StatusProvider(); final status = StatusProvider();
final kreta = KretaClient(user: user, settings: settings, status: status); final kreta = KretaClient(user: user, settings: settings, status: status);
final timetable = TimetableProvider(user: user, database: database, kreta: kreta); final timetable = TimetableProvider(user: user, database: database, kreta: kreta);
final premium = PremiumProvider(settings: settings);
WidgetsBinding.instance.addPostFrameCallback((_) {
FilcAPI.getConfig(settings).then((Config? config) {
if (config != null) settings.update(config: config);
});
premium.activate();
});
return MultiProvider( return MultiProvider(
providers: [ providers: [
ChangeNotifierProvider<PremiumProvider>(create: (_) => premium),
ChangeNotifierProvider<SettingsProvider>(create: (_) => settings), ChangeNotifierProvider<SettingsProvider>(create: (_) => settings),
ChangeNotifierProvider<UserProvider>(create: (_) => user), ChangeNotifierProvider<UserProvider>(create: (_) => user),
ChangeNotifierProvider<StatusProvider>(create: (_) => status), ChangeNotifierProvider<StatusProvider>(create: (_) => status),
@@ -86,7 +88,7 @@ class App extends StatelessWidget {
ChangeNotifierProvider<UpdateProvider>(create: (context) => UpdateProvider(context: context)), ChangeNotifierProvider<UpdateProvider>(create: (context) => UpdateProvider(context: context)),
// User data providers // User data providers
ChangeNotifierProvider<GradeProvider>(create: (context) => GradeProvider(context: context)), ChangeNotifierProvider<GradeProvider>(create: (_) => GradeProvider(settings: settings, user: user, database: database, kreta: kreta)),
ChangeNotifierProvider<TimetableProvider>(create: (_) => timetable), ChangeNotifierProvider<TimetableProvider>(create: (_) => timetable),
ChangeNotifierProvider<ExamProvider>(create: (context) => ExamProvider(context: context)), ChangeNotifierProvider<ExamProvider>(create: (context) => ExamProvider(context: context)),
ChangeNotifierProvider<HomeworkProvider>(create: (context) => HomeworkProvider(context: context)), ChangeNotifierProvider<HomeworkProvider>(create: (context) => HomeworkProvider(context: context)),
@@ -95,8 +97,9 @@ class App extends StatelessWidget {
ChangeNotifierProvider<EventProvider>(create: (context) => EventProvider(context: context)), ChangeNotifierProvider<EventProvider>(create: (context) => EventProvider(context: context)),
ChangeNotifierProvider<AbsenceProvider>(create: (context) => AbsenceProvider(context: context)), ChangeNotifierProvider<AbsenceProvider>(create: (context) => AbsenceProvider(context: context)),
ChangeNotifierProvider<GradeCalculatorProvider>(create: (context) => GradeCalculatorProvider(context)), ChangeNotifierProvider<GradeCalculatorProvider>(
ChangeNotifierProvider<LiveCardProvider>(create: (context) => LiveCardProvider(lessonProvider: timetable, settingsProvider: settings)) create: (_) => GradeCalculatorProvider(settings: settings, user: user, database: database, kreta: kreta)),
ChangeNotifierProvider<LiveCardProvider>(create: (context) => LiveCardProvider(timetable: timetable, settings: settings))
], ],
child: Consumer<ThemeModeObserver>( child: Consumer<ThemeModeObserver>(
builder: (context, themeMode, child) { builder: (context, themeMode, child) {

View File

@@ -2,6 +2,7 @@
import 'dart:io'; import 'dart:io';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/database/struct.dart'; import 'package:filcnaplo/database/struct.dart';
import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo/models/settings.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
@@ -10,15 +11,17 @@ import 'package:sqflite_common_ffi/sqflite_ffi.dart';
const settingsDB = DatabaseStruct("settings", { const settingsDB = DatabaseStruct("settings", {
"language": String, "start_page": int, "rounding": int, "theme": int, "accent_color": int, "news": int, "news_state": int, "developer_mode": int, "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, // general "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 "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, "vibration_strength": int, "ab_weeks": int, "swap_ab_weeks": int,
"notifications": int, "notifications_bitfield": int, "notification_poll_interval": int, // notifications "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, "x_filc_id": String, "graph_class_avg": int, "presentation_mode": int, "bell_delay": int, "bell_delay_enabled": int,
"grade_opening_fun": int, "grade_opening_fun": int, "icon_pack": String, "premium_scopes": String, "premium_token": String, "last_account_id": String,
});
const usersDB = DatabaseStruct("users", {
"id": String, "name": String, "username": String, "password": String, "institute_code": String, "student": String, "role": int,
"nickname": String // premium only
}); });
const usersDB = DatabaseStruct(
"users", {"id": String, "name": String, "username": String, "password": String, "institute_code": String, "student": String, "role": int});
const userDataDB = DatabaseStruct("user_data", { 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, "events": String, "absences": String, "group_averages": String,
@@ -28,7 +31,7 @@ const userDataDB = DatabaseStruct("user_data", {
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() async { Future<Database> initDB(DatabaseProvider database) async {
Database db; Database db;
if (Platform.isLinux || Platform.isWindows) { if (Platform.isLinux || Platform.isWindows) {
@@ -44,7 +47,7 @@ Future<Database> initDB() async {
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 // Set default values for table Settings
await db.insert("settings", SettingsProvider.defaultSettings().toMap()); await db.insert("settings", SettingsProvider.defaultSettings(database: database).toMap());
} }
// Migrate Databases // Migrate Databases
@@ -52,7 +55,7 @@ Future<Database> initDB() async {
await migrateDB( await migrateDB(
db, db,
struct: settingsDB, struct: settingsDB,
defaultValues: SettingsProvider.defaultSettings().toMap(), defaultValues: SettingsProvider.defaultSettings(database: database).toMap(),
); );
await migrateDB( await migrateDB(
db, db,

View File

@@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/models/subject_lesson_count.dart'; import 'package:filcnaplo/models/subject_lesson_count.dart';
import 'package:filcnaplo/models/user.dart'; import 'package:filcnaplo/models/user.dart';
// ignore: depend_on_referenced_packages // ignore: depend_on_referenced_packages
@@ -22,18 +23,26 @@ class DatabaseQuery {
final Database db; final Database db;
Future<SettingsProvider> getSettings() async { Future<SettingsProvider> getSettings(DatabaseProvider database) async {
Map settingsMap = (await db.query("settings")).elementAt(0); Map settingsMap = (await db.query("settings")).elementAt(0);
SettingsProvider settings = SettingsProvider.fromMap(settingsMap); SettingsProvider settings = SettingsProvider.fromMap(settingsMap, database: database);
return settings; return settings;
} }
Future<UserProvider> getUsers() async { Future<UserProvider> getUsers(SettingsProvider settings) async {
var userProvider = UserProvider(); var userProvider = UserProvider(settings: settings);
List<Map> usersMap = await db.query("users"); List<Map> usersMap = await db.query("users");
for (var user in usersMap) { for (var user in usersMap) {
userProvider.addUser(User.fromMap(user)); userProvider.addUser(User.fromMap(user));
} }
if (userProvider.getUsers().map((e) => e.id).contains(settings.lastAccountId)) {
userProvider.setUser(settings.lastAccountId);
} else {
if (usersMap.isNotEmpty) {
userProvider.setUser(userProvider.getUsers().first.id);
settings.update(lastAccountId: userProvider.id);
}
}
return userProvider; return userProvider;
} }
} }

View File

@@ -7,19 +7,14 @@ class AverageHelper {
List<String> ignoreInFinal = ["5,SzorgalomErtek", "4,MagatartasErtek"]; List<String> ignoreInFinal = ["5,SzorgalomErtek", "4,MagatartasErtek"];
if (finalAvg) { if (finalAvg) {
grades.removeWhere((e) => grades.removeWhere((e) => (e.value.value == 0) || (ignoreInFinal.contains(e.gradeType?.id)));
(e.value.value == 0) ||
(ignoreInFinal.contains(e.gradeType?.id)));
} }
for (var e in grades) { for (var e in grades) {
average += e.value.value * ((finalAvg ? 100 : e.value.weight) / 100); average += e.value.value * ((finalAvg ? 100 : e.value.weight) / 100);
} }
average = average / average = average / grades.map((e) => (finalAvg ? 100 : e.value.weight) / 100).fold(0.0, (a, b) => a + b);
grades
.map((e) => (finalAvg ? 100 : e.value.weight) / 100)
.fold(0.0, (a, b) => a + b);
return average.isNaN ? 0.0 : average; return average.isNaN ? 0.0 : average;
} }

View File

@@ -5,6 +5,7 @@ import 'package:share_plus/share_plus.dart';
class ShareHelper { class ShareHelper {
static Future<void> shareText(String text, {String? subject}) => Share.share(text, subject: subject); static Future<void> shareText(String text, {String? subject}) => Share.share(text, subject: subject);
// ignore: deprecated_member_use
static Future<void> shareFile(String path, {String? text, String? subject}) => Share.shareFiles([path], text: text, subject: subject); static Future<void> shareFile(String path, {String? text, String? subject}) => Share.shareFiles([path], text: text, subject: subject);
static Future<void> shareAttachment(Attachment attachment, {required BuildContext context}) async { static Future<void> shareAttachment(Attachment attachment, {required BuildContext context}) async {

View File

@@ -1,20 +1,40 @@
import 'package:filcnaplo/icons/filc_icons.dart'; import 'package:filcnaplo/icons/filc_icons.dart';
import 'package:filcnaplo/models/icon_pack.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/utils/format.dart'; import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_kreta_api/models/subject.dart'; import 'package:filcnaplo_kreta_api/models/subject.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
typedef SubjectIconVariants = Map<IconPack, IconData>;
class SubjectIconData { class SubjectIconData {
final IconData data; final SubjectIconVariants data;
final String name; // for iOS live activities compatibilty final String name; // for iOS live activities compatibilty
SubjectIconData({ SubjectIconData({
this.data = CupertinoIcons.rectangle_grid_2x2, this.data = const {
IconPack.material: Icons.widgets_outlined,
IconPack.cupertino: CupertinoIcons.rectangle_grid_2x2,
},
this.name = "square.grid.2x2", this.name = "square.grid.2x2",
}); });
} }
SubjectIconVariants createIcon({required IconData material, required IconData cupertino}) {
return {
IconPack.material: material,
IconPack.cupertino: cupertino,
};
}
class SubjectIcon { class SubjectIcon {
static SubjectIconData resolve({Subject? subject, String? subjectName}) { static String resolveName({Subject? subject, String? subjectName}) => _resolve(subject: subject, subjectName: subjectName).name;
static IconData resolveVariant({Subject? subject, String? subjectName, required BuildContext context}) =>
_resolve(subject: subject, subjectName: subjectName).data[Provider.of<SettingsProvider>(context, listen: false).iconPack]!;
static SubjectIconData _resolve({Subject? subject, String? subjectName}) {
assert(!(subject == null && subjectName == null)); assert(!(subject == null && subjectName == null));
String name = (subject?.name ?? subjectName ?? "").toLowerCase().specialChars().trim(); String name = (subject?.name ?? subjectName ?? "").toLowerCase().specialChars().trim();
@@ -22,65 +42,70 @@ class SubjectIcon {
// todo: check for categories // todo: check for categories
if (RegExp("mate(k|matika)").hasMatch(name) || category == "matematika") { if (RegExp("mate(k|matika)").hasMatch(name) || category == "matematika") {
return SubjectIconData(data: CupertinoIcons.function, name: "function"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.function, material: Icons.calculate_outlined), name: "function");
} else if (RegExp("magyar nyelv|nyelvtan").hasMatch(name)) { } else if (RegExp("magyar nyelv|nyelvtan").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.textformat_alt, name: "textformat.alt"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.textformat_alt, material: Icons.spellcheck_outlined), name: "textformat.alt");
} else if (RegExp("irodalom").hasMatch(name)) { } else if (RegExp("irodalom").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.book, name: "book"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.book, material: Icons.menu_book_outlined), name: "book");
} else if (RegExp("tor(i|tenelem)").hasMatch(name)) { } else if (RegExp("tor(i|tenelem)").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.compass, name: "safari"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.compass, material: Icons.hourglass_empty_outlined), name: "safari");
} else if (RegExp("foldrajz").hasMatch(name)) { } else if (RegExp("foldrajz").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.map, name: "map"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.map, material: Icons.public_outlined), name: "map");
} else if (RegExp("rajz|muvtori|muveszet|vizualis").hasMatch(name)) { } else if (RegExp("rajz|muvtori|muveszet|vizualis").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.paintbrush, name: "paintbrush"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.paintbrush, material: Icons.palette_outlined), name: "paintbrush");
} else if (RegExp("fizika").hasMatch(name)) { } else if (RegExp("fizika").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.lightbulb, name: "lightbulb"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.lightbulb, material: Icons.emoji_objects_outlined), name: "lightbulb");
} else if (RegExp("^enek|zene|szolfezs|zongora|korus").hasMatch(name)) { } else if (RegExp("^enek|zene|szolfezs|zongora|korus").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.music_note, name: "music.note"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.music_note, material: Icons.music_note_outlined), name: "music.note");
} else if (RegExp("^tes(i|tneveles)|sport").hasMatch(name)) { } else if (RegExp("^tes(i|tneveles)|sport").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.sportscourt, name: "sportscourt"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.sportscourt, material: Icons.sports_soccer_outlined), name: "sportscourt");
} else if (RegExp("kemia").hasMatch(name)) { } else if (RegExp("kemia").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.lab_flask, name: "testtube.2"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.lab_flask, material: Icons.science_outlined), name: "testtube.2");
} else if (RegExp("biologia").hasMatch(name)) { } else if (RegExp("biologia").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.paw, name: "pawprint"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.paw, material: Icons.pets_outlined), name: "pawprint");
} else if (RegExp("kornyezet|termeszet ?(tudomany|ismeret)|hon( es nep)?ismeret").hasMatch(name)) { } else if (RegExp("kornyezet|termeszet ?(tudomany|ismeret)|hon( es nep)?ismeret").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.arrow_3_trianglepath, name: "arrow.3.trianglepath"); return SubjectIconData(
data: createIcon(cupertino: CupertinoIcons.arrow_3_trianglepath, material: Icons.eco_outlined), name: "arrow.3.trianglepath");
} else if (RegExp("(hit|erkolcs)tan|vallas|etika").hasMatch(name)) { } else if (RegExp("(hit|erkolcs)tan|vallas|etika").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.heart, name: "heart"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.heart, material: Icons.favorite_border_outlined), name: "heart");
} else if (RegExp("penzugy").hasMatch(name)) { } else if (RegExp("penzugy").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.money_dollar, name: "dollarsign"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.money_dollar, material: Icons.savings_outlined), name: "dollarsign");
} else if (RegExp("informatika|szoftver|iroda|digitalis").hasMatch(name)) { } else if (RegExp("informatika|szoftver|iroda|digitalis").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.device_laptop, name: "laptopcomputer"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.device_laptop, material: Icons.computer_outlined), name: "laptopcomputer");
} else if (RegExp("prog").hasMatch(name)) { } else if (RegExp("prog").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.chevron_left_slash_chevron_right, name: "chevron.left.forwardslash.chevron.right"); return SubjectIconData(
data: createIcon(cupertino: CupertinoIcons.chevron_left_slash_chevron_right, material: Icons.code_outlined),
name: "chevron.left.forwardslash.chevron.right");
} else if (RegExp("halozat").hasMatch(name)) { } else if (RegExp("halozat").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.antenna_radiowaves_left_right, name: "antenna.radiowaves.left.and.right"); return SubjectIconData(
data: createIcon(cupertino: CupertinoIcons.antenna_radiowaves_left_right, material: Icons.wifi_tethering_outlined),
name: "antenna.radiowaves.left.and.right");
} else if (RegExp("szinhaz").hasMatch(name)) { } else if (RegExp("szinhaz").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.hifispeaker, name: "hifispeaker"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.hifispeaker, material: Icons.theater_comedy_outlined), name: "hifispeaker");
} else if (RegExp("film|media").hasMatch(name)) { } else if (RegExp("film|media").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.film, name: "film"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.film, material: Icons.theaters_outlined), name: "film");
} else if (RegExp("elektro(tech)?nika").hasMatch(name)) { } else if (RegExp("elektro(tech)?nika").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.bolt, name: "bolt"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.bolt, material: Icons.electrical_services_outlined), name: "bolt");
} else if (RegExp("gepesz|mernok|ipar").hasMatch(name)) { } else if (RegExp("gepesz|mernok|ipar").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.wrench, name: "wrench"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.wrench, material: Icons.precision_manufacturing_outlined), name: "wrench");
} else if (RegExp("technika").hasMatch(name)) { } else if (RegExp("technika").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.hammer, name: "hammer"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.hammer, material: Icons.build_outlined), name: "hammer");
} else if (RegExp("tanc").hasMatch(name)) { } else if (RegExp("tanc").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.music_mic, name: "music.mic"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.music_mic, material: Icons.speaker_outlined), name: "music.mic");
} else if (RegExp("filozofia").hasMatch(name)) { } else if (RegExp("filozofia").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.bubble_left, name: "bubble.left"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.bubble_left, material: Icons.psychology_outlined), name: "bubble.left");
} else if (RegExp("osztaly(fonoki|kozosseg)").hasMatch(name) || name == "ofo") { } else if (RegExp("osztaly(fonoki|kozosseg)").hasMatch(name) || name == "ofo") {
return SubjectIconData(data: CupertinoIcons.group, name: "person.3"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.group, material: Icons.groups_outlined), name: "person.3");
} else if (RegExp("gazdasag").hasMatch(name)) { } else if (RegExp("gazdasag").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.chart_pie, name: "chart.pie"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.chart_pie, material: Icons.account_balance_outlined), name: "chart.pie");
} else if (RegExp("szorgalom").hasMatch(name)) { } else if (RegExp("szorgalom").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.checkmark_seal, name: "checkmark.seal"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.checkmark_seal, material: Icons.verified_outlined), name: "checkmark.seal");
} else if (RegExp("magatartas").hasMatch(name)) { } else if (RegExp("magatartas").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.smiley, name: "face.smiling"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.smiley, material: Icons.emoji_people_outlined), name: "face.smiling");
} else if (RegExp("angol|nemet|francia|olasz|orosz|spanyol|latin|kinai|nyelv").hasMatch(name)) { } else if (RegExp("angol|nemet|francia|olasz|orosz|spanyol|latin|kinai|nyelv").hasMatch(name)) {
return SubjectIconData(data: CupertinoIcons.globe, name: "globe"); return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.globe, material: Icons.translate_outlined), name: "globe");
} else if (RegExp("linux").hasMatch(name)) { } else if (RegExp("linux").hasMatch(name)) {
return SubjectIconData(data: FilcIcons.linux); return SubjectIconData(data: createIcon(material: FilcIcons.linux, cupertino: FilcIcons.linux));
} }
return SubjectIconData(); return SubjectIconData();

View File

@@ -40,7 +40,7 @@ extension UpdateHelper on Release {
} }
Future<Uint8List> download({UpdateCallback? updateCallback}) async { Future<Uint8List> download({UpdateCallback? updateCallback}) async {
var response = await FilcAPI.downloadRelease(this); var response = await FilcAPI.downloadRelease(downloads.first);
List<List<int>> chunks = []; List<List<int>> chunks = [];
int downloaded = 0; int downloaded = 0;

View File

@@ -16,4 +16,7 @@ class FilcIcons {
/// downstairs /// downstairs
static const IconData downstairs = IconData(0x03, fontFamily: iconFontFamily); static const IconData downstairs = IconData(0x03, fontFamily: iconFontFamily);
/// premium
static const IconData premium = IconData(0x04, fontFamily: iconFontFamily);
} }

View File

@@ -32,12 +32,12 @@ class Startup {
late DatabaseProvider database; late DatabaseProvider database;
Future<void> start() async { Future<void> start() async {
var db = await initDB();
await db.close();
database = DatabaseProvider(); database = DatabaseProvider();
var db = await initDB(database);
await db.close();
await database.init(); await database.init();
settings = await database.query.getSettings(); settings = await database.query.getSettings(database);
user = await database.query.getUsers(); user = await database.query.getUsers(settings);
} }
} }

View File

@@ -0,0 +1 @@
enum IconPack { material, cupertino }

View File

@@ -1,9 +1,26 @@
class ReleaseDownload {
String url;
int size;
ReleaseDownload({
required this.url,
required this.size,
});
factory ReleaseDownload.fromJson(Map json) {
return ReleaseDownload(
url: json["browser_download_url"] ?? "",
size: json["size"] ?? 0,
);
}
}
class Release { class Release {
String tag; String tag;
Version version; Version version;
String author; String author;
String body; String body;
List<String> downloads; List<ReleaseDownload> downloads;
bool prerelease; bool prerelease;
Release({ Release({
@@ -20,7 +37,7 @@ class Release {
tag: json["tag_name"] ?? Version.zero.toString(), tag: json["tag_name"] ?? Version.zero.toString(),
author: json["author"] != null ? json["author"]["login"] ?? "" : "", author: json["author"] != null ? json["author"]["login"] ?? "" : "",
body: json["body"] ?? "", body: json["body"] ?? "",
downloads: json["assets"] != null ? json["assets"].map((a) => a["browser_download_url"] ?? "").toList().cast<String>() : [], downloads: json["assets"] != null ? json["assets"].map((a) => ReleaseDownload.fromJson(a)).toList().cast<ReleaseDownload>() : [],
prerelease: json["prerelease"] ?? false, prerelease: json["prerelease"] ?? false,
version: Version.fromString(json["tag_name"] ?? ""), version: Version.fromString(json["tag_name"] ?? ""),
); );

View File

@@ -3,10 +3,10 @@ import 'dart:developer';
import 'package:filcnaplo/api/providers/database_provider.dart'; import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/models/config.dart'; import 'package:filcnaplo/models/config.dart';
import 'package:filcnaplo/models/icon_pack.dart';
import 'package:filcnaplo/theme/colors/accent.dart'; import 'package:filcnaplo/theme/colors/accent.dart';
import 'package:filcnaplo/theme/colors/dark_mobile.dart'; import 'package:filcnaplo/theme/colors/dark_mobile.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
enum Pages { home, grades, timetable, messages, absences } enum Pages { home, grades, timetable, messages, absences }
@@ -16,6 +16,8 @@ enum UpdateChannel { stable, beta, dev }
enum VibrationStrength { off, light, medium, strong } enum VibrationStrength { off, light, medium, strong }
class SettingsProvider extends ChangeNotifier { class SettingsProvider extends ChangeNotifier {
final DatabaseProvider? _database;
// en_en, hu_hu, de_de // en_en, hu_hu, de_de
String _language; String _language;
Pages _startPage; Pages _startPage;
@@ -56,8 +58,16 @@ class SettingsProvider extends ChangeNotifier {
bool _bellDelayEnabled; bool _bellDelayEnabled;
int _bellDelay; int _bellDelay;
bool _gradeOpeningFun; bool _gradeOpeningFun;
IconPack _iconPack;
Color _customAccentColor;
Color _customBackgroundColor;
Color _customHighlightColor;
List<String> _premiumScopes;
String _premiumAccessToken;
String _lastAccountId;
SettingsProvider({ SettingsProvider({
DatabaseProvider? database,
required String language, required String language,
required Pages startPage, required Pages startPage,
required int rounding, required int rounding,
@@ -82,7 +92,15 @@ class SettingsProvider extends ChangeNotifier {
required bool bellDelayEnabled, required bool bellDelayEnabled,
required int bellDelay, required int bellDelay,
required bool gradeOpeningFun, required bool gradeOpeningFun,
}) : _language = language, required IconPack iconPack,
required Color customAccentColor,
required Color customBackgroundColor,
required Color customHighlightColor,
required List<String> premiumScopes,
required String premiumAccessToken,
required String lastAccountId,
}) : _database = database,
_language = language,
_startPage = startPage, _startPage = startPage,
_rounding = rounding, _rounding = rounding,
_theme = theme, _theme = theme,
@@ -105,9 +123,16 @@ class SettingsProvider extends ChangeNotifier {
_presentationMode = presentationMode, _presentationMode = presentationMode,
_bellDelayEnabled = bellDelayEnabled, _bellDelayEnabled = bellDelayEnabled,
_bellDelay = bellDelay, _bellDelay = bellDelay,
_gradeOpeningFun = gradeOpeningFun; _gradeOpeningFun = gradeOpeningFun,
_iconPack = iconPack,
_customAccentColor = customAccentColor,
_customBackgroundColor = customBackgroundColor,
_customHighlightColor = customHighlightColor,
_premiumScopes = premiumScopes,
_premiumAccessToken = premiumAccessToken,
_lastAccountId = lastAccountId;
factory SettingsProvider.fromMap(Map map) { factory SettingsProvider.fromMap(Map map, {required DatabaseProvider database}) {
Map<String, Object?>? configMap; Map<String, Object?>? configMap;
try { try {
@@ -117,6 +142,7 @@ class SettingsProvider extends ChangeNotifier {
} }
return SettingsProvider( return SettingsProvider(
database: database,
language: map["language"], language: map["language"],
startPage: Pages.values[map["start_page"]], startPage: Pages.values[map["start_page"]],
rounding: map["rounding"], rounding: map["rounding"],
@@ -147,6 +173,13 @@ class SettingsProvider extends ChangeNotifier {
bellDelayEnabled: map["bell_delay_enabled"] == 1, bellDelayEnabled: map["bell_delay_enabled"] == 1,
bellDelay: map["bell_delay"], bellDelay: map["bell_delay"],
gradeOpeningFun: map["grade_opening_fun"] == 1, gradeOpeningFun: map["grade_opening_fun"] == 1,
iconPack: Map.fromEntries(IconPack.values.map((e) => MapEntry(e.name, e)))[map["icon_pack"]]!,
customAccentColor: Color(map["custom_accent_color"]),
customBackgroundColor: Color(map["custom_background_color"]),
customHighlightColor: Color(map["custom_highlight_color"]),
premiumScopes: jsonDecode(map["premium_scopes"]).cast<String>(),
premiumAccessToken: map["premium_token"],
lastAccountId: map["last_account_id"],
); );
} }
@@ -179,11 +212,19 @@ class SettingsProvider extends ChangeNotifier {
"bell_delay_enabled": _bellDelayEnabled ? 1 : 0, "bell_delay_enabled": _bellDelayEnabled ? 1 : 0,
"bell_delay": _bellDelay, "bell_delay": _bellDelay,
"grade_opening_fun": _gradeOpeningFun ? 1 : 0, "grade_opening_fun": _gradeOpeningFun ? 1 : 0,
"icon_pack": _iconPack.name,
"custom_accent_color": _customAccentColor.value,
"custom_background_color": _customBackgroundColor.value,
"custom_highlight_color": _customHighlightColor.value,
"premium_scopes": jsonEncode(_premiumScopes),
"premium_token": _premiumAccessToken,
"last_account_id": _lastAccountId,
}; };
} }
factory SettingsProvider.defaultSettings() { factory SettingsProvider.defaultSettings({DatabaseProvider? database}) {
return SettingsProvider( return SettingsProvider(
database: database,
language: "hu", language: "hu",
startPage: Pages.home, startPage: Pages.home,
rounding: 5, rounding: 5,
@@ -214,6 +255,13 @@ class SettingsProvider extends ChangeNotifier {
bellDelayEnabled: false, bellDelayEnabled: false,
bellDelay: 0, bellDelay: 0,
gradeOpeningFun: true, gradeOpeningFun: true,
iconPack: IconPack.cupertino,
customAccentColor: const Color(0xff20AC9B),
customBackgroundColor: const Color(0xff000000),
customHighlightColor: const Color(0xff222222),
premiumScopes: [],
premiumAccessToken: "",
lastAccountId: "",
); );
} }
@@ -242,10 +290,15 @@ class SettingsProvider extends ChangeNotifier {
bool get bellDelayEnabled => _bellDelayEnabled; bool get bellDelayEnabled => _bellDelayEnabled;
int get bellDelay => _bellDelay; int get bellDelay => _bellDelay;
bool get gradeOpeningFun => _gradeOpeningFun; bool get gradeOpeningFun => _gradeOpeningFun;
IconPack get iconPack => _iconPack;
Color? get customAccentColor => _customAccentColor == accentColorMap[AccentColor.custom] ? null : _customAccentColor;
Color? get customBackgroundColor => _customBackgroundColor;
Color? get customHighlightColor => _customHighlightColor;
List<String> get premiumScopes => _premiumScopes;
String get premiumAccessToken => _premiumAccessToken;
String get lastAccountId => _lastAccountId;
Future<void> update( Future<void> update({
BuildContext context, {
DatabaseProvider? database,
bool store = true, bool store = true,
String? language, String? language,
Pages? startPage, Pages? startPage,
@@ -271,6 +324,13 @@ class SettingsProvider extends ChangeNotifier {
bool? bellDelayEnabled, bool? bellDelayEnabled,
int? bellDelay, int? bellDelay,
bool? gradeOpeningFun, bool? gradeOpeningFun,
IconPack? iconPack,
Color? customAccentColor,
Color? customBackgroundColor,
Color? customHighlightColor,
List<String>? premiumScopes,
String? premiumAccessToken,
String? lastAccountId,
}) async { }) async {
if (language != null && language != _language) _language = language; if (language != null && language != _language) _language = language;
if (startPage != null && startPage != _startPage) _startPage = startPage; if (startPage != null && startPage != _startPage) _startPage = startPage;
@@ -298,9 +358,15 @@ class SettingsProvider extends ChangeNotifier {
if (bellDelay != null && bellDelay != _bellDelay) _bellDelay = bellDelay; if (bellDelay != null && bellDelay != _bellDelay) _bellDelay = bellDelay;
if (bellDelayEnabled != null && bellDelayEnabled != _bellDelayEnabled) _bellDelayEnabled = bellDelayEnabled; if (bellDelayEnabled != null && bellDelayEnabled != _bellDelayEnabled) _bellDelayEnabled = bellDelayEnabled;
if (gradeOpeningFun != null && gradeOpeningFun != _gradeOpeningFun) _gradeOpeningFun = gradeOpeningFun; if (gradeOpeningFun != null && gradeOpeningFun != _gradeOpeningFun) _gradeOpeningFun = gradeOpeningFun;
if (iconPack != null && iconPack != _iconPack) _iconPack = iconPack;
if (customAccentColor != null && customAccentColor != _customAccentColor) _customAccentColor = customAccentColor;
if (customBackgroundColor != null && customBackgroundColor != _customBackgroundColor) _customBackgroundColor = customBackgroundColor;
if (customHighlightColor != null && customHighlightColor != _customHighlightColor) _customHighlightColor = customHighlightColor;
if (premiumScopes != null && premiumScopes != _premiumScopes) _premiumScopes = premiumScopes;
if (premiumAccessToken != null && premiumAccessToken != _premiumAccessToken) _premiumAccessToken = premiumAccessToken;
if (lastAccountId != null && lastAccountId != _lastAccountId) _lastAccountId = lastAccountId;
database ??= Provider.of<DatabaseProvider>(context, listen: false); if (store) await _database?.store.storeSettings(this);
if (store) await database.store.storeSettings(this);
notifyListeners(); notifyListeners();
} }
} }

View File

@@ -13,6 +13,9 @@ class User {
String name; String name;
Student student; Student student;
Role role; Role role;
String nickname;
String get displayName => nickname != '' ? nickname : name;
User({ User({
String? id, String? id,
@@ -22,6 +25,7 @@ class User {
required this.instituteCode, required this.instituteCode,
required this.student, required this.student,
required this.role, required this.role,
this.nickname = "",
}) { }) {
if (id != null) { if (id != null) {
this.id = id; this.id = id;
@@ -39,6 +43,7 @@ class User {
name: map["name"].trim(), name: map["name"].trim(),
student: Student.fromJson(jsonDecode(map["student"])), student: Student.fromJson(jsonDecode(map["student"])),
role: Role.values[map["role"] ?? 0], role: Role.values[map["role"] ?? 0],
nickname: map["nickname"] ?? "",
); );
} }
@@ -51,6 +56,7 @@ class User {
"name": name, "name": name,
"student": jsonEncode(student.json), "student": jsonEncode(student.json),
"role": role.index, "role": role.index,
"nickname": nickname,
}; };
} }

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
enum AccentColor { filc, blue, green, lime, yellow, orange, red, pink, purple, adaptive } enum AccentColor { filc, blue, green, lime, yellow, orange, red, pink, purple, adaptive, custom }
Map<AccentColor, Color> accentColorMap = { Map<AccentColor, Color> accentColorMap = {
AccentColor.filc: const Color(0xff20AC9B), AccentColor.filc: const Color(0xff20AC9B),
@@ -13,4 +13,5 @@ Map<AccentColor, Color> accentColorMap = {
AccentColor.pink: Colors.pink.shade300, AccentColor.pink: Colors.pink.shade300,
AccentColor.purple: Colors.purple.shade300, AccentColor.purple: Colors.purple.shade300,
AccentColor.adaptive: const Color(0xff20AC9B), AccentColor.adaptive: const Color(0xff20AC9B),
AccentColor.custom: const Color(0xff20AC9B),
}; };

View File

@@ -22,8 +22,10 @@ class AppTheme {
// Light Theme // Light Theme
static ThemeData lightTheme(BuildContext context, {CorePalette? palette}) { static ThemeData lightTheme(BuildContext context, {CorePalette? palette}) {
var lightColors = AppColors.fromBrightness(Brightness.light); var lightColors = AppColors.fromBrightness(Brightness.light);
AccentColor accentColor = Provider.of<SettingsProvider>(context, listen: false).accentColor; final settings = Provider.of<SettingsProvider>(context, listen: false);
Color accent = accentColorMap[accentColor] ?? const Color(0x00000000); AccentColor accentColor = settings.accentColor;
final customAccentColor = accentColor == AccentColor.custom ? settings.customAccentColor : null;
Color accent = customAccentColor ?? accentColorMap[accentColor] ?? const Color(0x00000000);
if (accentColor == AccentColor.adaptive) { if (accentColor == AccentColor.adaptive) {
if (palette != null) accent = _paletteAccentLight(palette)!; if (palette != null) accent = _paletteAccentLight(palette)!;
@@ -31,31 +33,35 @@ class AppTheme {
palette = null; palette = null;
} }
Color backgroundColor =
accentColor == AccentColor.custom ? settings.customBackgroundColor : _paletteBackgroundLight(palette) ?? lightColors.background;
Color highlighColor =
accentColor == AccentColor.custom ? settings.customHighlightColor : _paletteHighlightLight(palette) ?? lightColors.highlight;
return ThemeData( return ThemeData(
brightness: Brightness.light, brightness: Brightness.light,
useMaterial3: false, useMaterial3: false,
fontFamily: _fontFamily, fontFamily: _fontFamily,
scaffoldBackgroundColor: _paletteBackgroundLight(palette) ?? lightColors.background, scaffoldBackgroundColor: backgroundColor,
backgroundColor: _paletteHighlightLight(palette) ?? lightColors.highlight, backgroundColor: highlighColor,
primaryColor: lightColors.filc, primaryColor: lightColors.filc,
dividerColor: const Color(0x00000000), dividerColor: const Color(0x00000000),
colorScheme: ColorScheme.fromSwatch( colorScheme: ColorScheme.fromSwatch(
accentColor: accent, accentColor: accent,
backgroundColor: _paletteBackgroundLight(palette) ?? lightColors.background, backgroundColor: backgroundColor,
brightness: Brightness.light, brightness: Brightness.light,
cardColor: _paletteHighlightLight(palette) ?? lightColors.highlight, cardColor: highlighColor,
errorColor: lightColors.red, errorColor: lightColors.red,
primaryColorDark: lightColors.filc, primaryColorDark: lightColors.filc,
primarySwatch: Colors.teal,
), ),
shadowColor: lightColors.shadow, shadowColor: highlighColor.withOpacity(.5), //lightColors.shadow,
appBarTheme: AppBarTheme(backgroundColor: _paletteBackgroundLight(palette) ?? lightColors.background), appBarTheme: AppBarTheme(backgroundColor: backgroundColor),
indicatorColor: accent, indicatorColor: accent,
iconTheme: IconThemeData(color: lightColors.text.withOpacity(.75)), iconTheme: IconThemeData(color: lightColors.text.withOpacity(.75)),
navigationBarTheme: NavigationBarThemeData( navigationBarTheme: NavigationBarThemeData(
indicatorColor: accent.withOpacity(accentColor == AccentColor.adaptive ? 0.4 : 0.8), indicatorColor: accent.withOpacity(accentColor == AccentColor.adaptive ? 0.4 : 0.8),
iconTheme: MaterialStateProperty.all(IconThemeData(color: lightColors.text)), iconTheme: MaterialStateProperty.all(IconThemeData(color: lightColors.text)),
backgroundColor: _paletteHighlightLight(palette) ?? lightColors.highlight, backgroundColor: highlighColor,
labelTextStyle: MaterialStateProperty.all(TextStyle( labelTextStyle: MaterialStateProperty.all(TextStyle(
fontSize: 13.0, fontSize: 13.0,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@@ -75,8 +81,10 @@ class AppTheme {
// Dark Theme // Dark Theme
static ThemeData darkTheme(BuildContext context, {CorePalette? palette}) { static ThemeData darkTheme(BuildContext context, {CorePalette? palette}) {
var darkColors = AppColors.fromBrightness(Brightness.dark); var darkColors = AppColors.fromBrightness(Brightness.dark);
AccentColor accentColor = Provider.of<SettingsProvider>(context, listen: false).accentColor; final settings = Provider.of<SettingsProvider>(context, listen: false);
Color accent = accentColorMap[accentColor] ?? const Color(0x00000000); AccentColor accentColor = settings.accentColor;
final customAccentColor = accentColor == AccentColor.custom ? settings.customAccentColor : null;
Color accent = customAccentColor ?? accentColorMap[accentColor] ?? const Color(0x00000000);
if (accentColor == AccentColor.adaptive) { if (accentColor == AccentColor.adaptive) {
if (palette != null) accent = _paletteAccentDark(palette)!; if (palette != null) accent = _paletteAccentDark(palette)!;
@@ -84,31 +92,34 @@ class AppTheme {
palette = null; palette = null;
} }
Color backgroundColor =
accentColor == AccentColor.custom ? settings.customBackgroundColor : _paletteBackgroundDark(palette) ?? darkColors.background;
Color highlightColor = accentColor == AccentColor.custom ? settings.customHighlightColor : _paletteHighlightDark(palette) ?? darkColors.highlight;
return ThemeData( return ThemeData(
brightness: Brightness.dark, brightness: Brightness.dark,
useMaterial3: false, useMaterial3: false,
fontFamily: _fontFamily, fontFamily: _fontFamily,
scaffoldBackgroundColor: _paletteBackgroundDark(palette) ?? darkColors.background, scaffoldBackgroundColor: backgroundColor,
backgroundColor: _paletteHighlightDark(palette) ?? darkColors.highlight, backgroundColor: highlightColor,
primaryColor: darkColors.filc, primaryColor: darkColors.filc,
dividerColor: const Color(0x00000000), dividerColor: const Color(0x00000000),
colorScheme: ColorScheme.fromSwatch( colorScheme: ColorScheme.fromSwatch(
accentColor: accent, accentColor: accent,
backgroundColor: _paletteBackgroundDark(palette) ?? darkColors.background, backgroundColor: backgroundColor,
brightness: Brightness.dark, brightness: Brightness.dark,
cardColor: _paletteHighlightDark(palette) ?? darkColors.highlight, cardColor: highlightColor,
errorColor: darkColors.red, errorColor: darkColors.red,
primaryColorDark: darkColors.filc, primaryColorDark: darkColors.filc,
primarySwatch: Colors.teal,
), ),
shadowColor: darkColors.shadow, shadowColor: highlightColor.withOpacity(.5), //darkColors.shadow,
appBarTheme: AppBarTheme(backgroundColor: _paletteBackgroundDark(palette) ?? darkColors.background), appBarTheme: AppBarTheme(backgroundColor: backgroundColor),
indicatorColor: accent, indicatorColor: accent,
iconTheme: IconThemeData(color: darkColors.text.withOpacity(.75)), iconTheme: IconThemeData(color: darkColors.text.withOpacity(.75)),
navigationBarTheme: NavigationBarThemeData( navigationBarTheme: NavigationBarThemeData(
indicatorColor: accent.withOpacity(accentColor == AccentColor.adaptive ? 0.4 : 0.8), indicatorColor: accent.withOpacity(accentColor == AccentColor.adaptive ? 0.4 : 0.8),
iconTheme: MaterialStateProperty.all(IconThemeData(color: darkColors.text)), iconTheme: MaterialStateProperty.all(IconThemeData(color: darkColors.text)),
backgroundColor: _paletteHighlightDark(palette) ?? darkColors.highlight, backgroundColor: highlightColor,
labelTextStyle: MaterialStateProperty.all(TextStyle( labelTextStyle: MaterialStateProperty.all(TextStyle(
fontSize: 13.0, fontSize: 13.0,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,

View File

@@ -1,5 +1,5 @@
import 'package:filcnaplo/api/providers/update_provider.dart'; import 'package:filcnaplo/api/providers/update_provider.dart';
import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/ui/date_widget.dart'; import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo/ui/filter/widgets/grades.dart' as grade_filter; import 'package:filcnaplo/ui/filter/widgets/grades.dart' as grade_filter;
import 'package:filcnaplo/ui/filter/widgets/certifications.dart' as certification_filter; import 'package:filcnaplo/ui/filter/widgets/certifications.dart' as certification_filter;
@@ -12,6 +12,7 @@ import 'package:filcnaplo/ui/filter/widgets/events.dart' as event_filter;
import 'package:filcnaplo/ui/filter/widgets/lessons.dart' as lesson_filter; import 'package:filcnaplo/ui/filter/widgets/lessons.dart' as lesson_filter;
import 'package:filcnaplo/ui/filter/widgets/update.dart' as update_filter; import 'package:filcnaplo/ui/filter/widgets/update.dart' as update_filter;
import 'package:filcnaplo/ui/filter/widgets/missed_exams.dart' as missed_exam_filter; import 'package:filcnaplo/ui/filter/widgets/missed_exams.dart' as missed_exam_filter;
import 'package:filcnaplo/ui/filter/widgets/premium.dart' as premium_filter;
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart'; import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
import 'package:filcnaplo_kreta_api/providers/event_provider.dart'; import 'package:filcnaplo_kreta_api/providers/event_provider.dart';
import 'package:filcnaplo_kreta_api/providers/exam_provider.dart'; import 'package:filcnaplo_kreta_api/providers/exam_provider.dart';
@@ -20,6 +21,7 @@ import 'package:filcnaplo_kreta_api/providers/homework_provider.dart';
import 'package:filcnaplo_kreta_api/providers/message_provider.dart'; import 'package:filcnaplo_kreta_api/providers/message_provider.dart';
import 'package:filcnaplo_kreta_api/providers/note_provider.dart'; import 'package:filcnaplo_kreta_api/providers/note_provider.dart';
import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart'; import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:implicitly_animated_reorderable_list/transitions.dart'; import 'package:implicitly_animated_reorderable_list/transitions.dart';
@@ -27,7 +29,7 @@ import 'package:provider/provider.dart';
const List<FilterType> homeFilters = [FilterType.all, FilterType.grades, FilterType.messages, FilterType.absences]; const List<FilterType> homeFilters = [FilterType.all, FilterType.grades, FilterType.messages, FilterType.absences];
enum FilterType { all, grades, newGrades, messages, absences, homework, exams, notes, events, lessons, updates, certifications, missedExams } enum FilterType { all, grades, newGrades, messages, absences, homework, exams, notes, events, lessons, updates, certifications, missedExams, premium }
Future<List<DateWidget>> getFilterWidgets(FilterType activeData, {bool absencesNoExcused = false, required BuildContext context}) async { Future<List<DateWidget>> getFilterWidgets(FilterType activeData, {bool absencesNoExcused = false, required BuildContext context}) async {
final gradeProvider = Provider.of<GradeProvider>(context); final gradeProvider = Provider.of<GradeProvider>(context);
@@ -39,6 +41,8 @@ Future<List<DateWidget>> getFilterWidgets(FilterType activeData, {bool absencesN
final noteProvider = Provider.of<NoteProvider>(context); final noteProvider = Provider.of<NoteProvider>(context);
final eventProvider = Provider.of<EventProvider>(context); final eventProvider = Provider.of<EventProvider>(context);
final updateProvider = Provider.of<UpdateProvider>(context); final updateProvider = Provider.of<UpdateProvider>(context);
final settingsProvider = Provider.of<SettingsProvider>(context);
final premiumProvider = Provider.of<PremiumProvider>(context);
List<DateWidget> items = []; List<DateWidget> items = [];
@@ -55,6 +59,7 @@ Future<List<DateWidget>> getFilterWidgets(FilterType activeData, {bool absencesN
getFilterWidgets(FilterType.updates, context: context), getFilterWidgets(FilterType.updates, context: context),
getFilterWidgets(FilterType.certifications, context: context), getFilterWidgets(FilterType.certifications, context: context),
getFilterWidgets(FilterType.missedExams, context: context), getFilterWidgets(FilterType.missedExams, context: context),
getFilterWidgets(FilterType.premium, context: context),
]); ]);
items = all.expand((x) => x).toList(); items = all.expand((x) => x).toList();
@@ -63,7 +68,9 @@ Future<List<DateWidget>> getFilterWidgets(FilterType activeData, {bool absencesN
// Grades // Grades
case FilterType.grades: case FilterType.grades:
items = grade_filter.getWidgets(gradeProvider.grades, gradeProvider.lastSeenDate); items = grade_filter.getWidgets(gradeProvider.grades, gradeProvider.lastSeenDate);
if (settingsProvider.gradeOpeningFun) {
items.addAll(await getFilterWidgets(FilterType.newGrades, context: context)); items.addAll(await getFilterWidgets(FilterType.newGrades, context: context));
}
break; break;
// Grades // Grades
@@ -124,6 +131,12 @@ Future<List<DateWidget>> getFilterWidgets(FilterType activeData, {bool absencesN
case FilterType.missedExams: case FilterType.missedExams:
items = missed_exam_filter.getWidgets(timetableProvider.lessons); items = missed_exam_filter.getWidgets(timetableProvider.lessons);
break; break;
case FilterType.premium:
final now = DateTime.now();
final isWeekend = now.weekday == DateTime.saturday || now.weekday == DateTime.sunday;
items = [if (!premiumProvider.hasPremium && isWeekend) premium_filter.getWidget()];
break;
} }
return items; return items;
} }
@@ -145,11 +158,11 @@ Widget filterItemBuilder(BuildContext context, Animation<double> animation, Widg
child: DecoratedBox( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
boxShadow: [ boxShadow: [
if (Theme.of(context).brightness == Brightness.light)
BoxShadow( BoxShadow(
offset: const Offset(0, 21), offset: const Offset(0, 21),
blurRadius: 23.0, blurRadius: 23.0,
color: AppColors.of(context).shadow.withOpacity( color: Theme.of(context).shadowColor.withOpacity(
Theme.of(context).shadowColor.opacity *
CurvedAnimation( CurvedAnimation(
parent: CurvedAnimation(parent: animation, curve: Curves.easeInOutCubic), parent: CurvedAnimation(parent: animation, curve: Curves.easeInOutCubic),
curve: const Interval(2 / 3, 1.0), curve: const Interval(2 / 3, 1.0),

View File

@@ -8,7 +8,8 @@ import 'package:filcnaplo_desktop_ui/common/widgets/grade/grade_viewable.dart' a
List<DateWidget> getWidgets(List<Grade> providerGrades, DateTime? lastSeenDate) { List<DateWidget> getWidgets(List<Grade> providerGrades, DateTime? lastSeenDate) {
List<DateWidget> items = []; List<DateWidget> items = [];
for (var grade in providerGrades) { for (var grade in providerGrades) {
if (grade.type == GradeType.midYear && (!(lastSeenDate != null && grade.date.isAfter(lastSeenDate)) || grade.value.value == 0)) { final surprise = (!(lastSeenDate != null && grade.date.isAfter(lastSeenDate)) || grade.value.value == 0);
if (grade.type == GradeType.midYear && surprise) {
items.add(DateWidget( items.add(DateWidget(
key: grade.id, key: grade.id,
date: grade.date, date: grade.date,
@@ -23,7 +24,8 @@ List<DateWidget> getNewWidgets(List<Grade> providerGrades, DateTime? lastSeenDat
List<DateWidget> items = []; List<DateWidget> items = [];
List<Grade> newGrades = []; List<Grade> newGrades = [];
for (var grade in providerGrades) { for (var grade in providerGrades) {
if (grade.type == GradeType.midYear && !(lastSeenDate != null && !grade.date.isAfter(lastSeenDate)) && grade.value.value != 0) { final surprise = !(lastSeenDate != null && !grade.date.isAfter(lastSeenDate)) && grade.value.value != 0;
if (grade.type == GradeType.midYear && surprise) {
newGrades.add(grade); newGrades.add(grade);
} }
} }

View File

@@ -4,15 +4,12 @@ import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_viewable.da
List<DateWidget> getWidgets(List<Homework> providerHomework) { List<DateWidget> getWidgets(List<Homework> providerHomework) {
List<DateWidget> items = []; List<DateWidget> items = [];
final now = DateTime.now(); for (var homework in providerHomework) {
providerHomework.where((h) => h.deadline.hour == 0 ? _sameDate(h.deadline, now) : h.deadline.isAfter(now)).forEach((homework) {
items.add(DateWidget( items.add(DateWidget(
key: homework.id, key: homework.id,
date: homework.deadline.year != 0 ? homework.deadline : homework.date, date: homework.deadline.year != 0 ? homework.deadline : homework.date,
widget: mobile.HomeworkViewable(homework), widget: mobile.HomeworkViewable(homework),
)); ));
}); }
return items; return items;
} }
bool _sameDate(DateTime a, DateTime b) => (a.year == b.year && a.month == b.month && a.day == b.day);

View File

@@ -7,7 +7,7 @@ List<DateWidget> getWidgets(List<Lesson> providerLessons) {
providerLessons.where((l) => l.isChanged && l.start.isAfter(DateTime.now())).forEach((lesson) { providerLessons.where((l) => l.isChanged && l.start.isAfter(DateTime.now())).forEach((lesson) {
items.add(DateWidget( items.add(DateWidget(
key: lesson.id, key: lesson.id,
date: lesson.date, date: DateTime(lesson.date.year, lesson.date.month, lesson.date.day, lesson.start.hour, lesson.start.minute),
widget: mobile.ChangedLessonViewable(lesson), widget: mobile.ChangedLessonViewable(lesson),
)); ));
}); });

View File

@@ -0,0 +1,16 @@
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo_premium/ui/mobile/premium/premium_banner_button.dart';
import 'package:flutter/widgets.dart';
DateWidget getWidget() {
return DateWidget(
date: DateTime.now().add(const Duration(minutes: 1)),
widget: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(14.0),
child: const PremiumBannerButton(),
),
),
);
}

View File

@@ -80,7 +80,7 @@ class GradeTile extends StatelessWidget {
child: Padding( child: Padding(
padding: leadingPadding, padding: leadingPadding,
child: Icon( child: Icon(
SubjectIcon.resolve(subject: grade.subject).data, SubjectIcon.resolveVariant(subject: grade.subject, context: context),
size: 28.0, size: 28.0,
color: AppColors.of(context).text.withOpacity(.75), color: AppColors.of(context).text.withOpacity(.75),
), ),
@@ -129,6 +129,7 @@ class GradeValueWidget extends StatelessWidget {
this.shadow = false, this.shadow = false,
this.outline = false, this.outline = false,
this.complemented = false, this.complemented = false,
this.nocolor = false,
}) : super(key: key); }) : super(key: key);
final GradeValue value; final GradeValue value;
@@ -138,13 +139,14 @@ class GradeValueWidget extends StatelessWidget {
final bool shadow; final bool shadow;
final bool outline; final bool outline;
final bool complemented; final bool complemented;
final bool nocolor;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
GradeValue value = Provider.of<SettingsProvider>(context).goodStudent ? GradeValue(5, "Példás", "Példás", this.value.weight) : this.value; GradeValue value = Provider.of<SettingsProvider>(context).goodStudent ? GradeValue(5, "Példás", "Példás", this.value.weight) : this.value;
bool isSubjectView = SubjectGradesContainer.of(context) != null; bool isSubjectView = SubjectGradesContainer.of(context) != null;
Color color = gradeColor(context: context, value: value.value); Color color = gradeColor(context: context, value: value.value, nocolor: nocolor);
Widget valueText; Widget valueText;
final percentage = value.percentage; final percentage = value.percentage;
@@ -166,7 +168,9 @@ class GradeValueWidget extends StatelessWidget {
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
); );
} else if (value.value != 0) { } else if (value.valueName.toLowerCase().specialChars() == 'nem irt') {
valueText = const Icon(FeatherIcons.slash);
} else {
valueText = Stack(alignment: Alignment.topRight, children: [ valueText = Stack(alignment: Alignment.topRight, children: [
Transform.translate( Transform.translate(
offset: (value.weight >= 200) ? const Offset(2, 1.5) : Offset.zero, offset: (value.weight >= 200) ? const Offset(2, 1.5) : Offset.zero,
@@ -196,10 +200,6 @@ class GradeValueWidget extends StatelessWidget {
), ),
), ),
]); ]);
} else if (value.valueName.toLowerCase().specialChars() == 'nem irt') {
valueText = const Icon(FeatherIcons.slash);
} else {
valueText = const Icon(FeatherIcons.type);
} }
return fill return fill
@@ -223,7 +223,7 @@ class GradeValueWidget extends StatelessWidget {
} }
} }
Color gradeColor({required BuildContext context, required num value}) { Color gradeColor({required BuildContext context, required num value, bool nocolor = false}) {
int valueInt = 0; int valueInt = 0;
var settings = Provider.of<SettingsProvider>(context, listen: false); var settings = Provider.of<SettingsProvider>(context, listen: false);
@@ -236,6 +236,8 @@ Color gradeColor({required BuildContext context, required num value}) {
} }
} catch (_) {} } catch (_) {}
if (nocolor) return AppColors.of(context).text;
switch (valueInt) { switch (valueInt) {
case 5: case 5:
return settings.gradeColors[4]; return settings.gradeColors[4];

View File

@@ -29,7 +29,7 @@ extension StringFormatUtils on String {
htmlString = htmlString.replaceAll(RegExp(r'<p ?>'), ""); htmlString = htmlString.replaceAll(RegExp(r'<p ?>'), "");
htmlString = htmlString.replaceAll(RegExp(r'</p ?>'), "\n"); htmlString = htmlString.replaceAll(RegExp(r'</p ?>'), "\n");
var document = parse(htmlString); var document = parse(htmlString);
return document.body?.text.trim() ?? ""; return document.body?.text.trim() ?? htmlString;
} }
String limit(int max) { String limit(int max) {

View File

@@ -3,7 +3,7 @@ description: "Nem hivatalos e-napló alkalmazás az e-Kréta rendszerhez"
homepage: https://filcnaplo.hu homepage: https://filcnaplo.hu
publish_to: "none" publish_to: "none"
version: 3.3.4+168 version: 3.4.2+173
environment: environment:
sdk: ">=2.16.0-80.1.beta <3.0.0" sdk: ">=2.16.0-80.1.beta <3.0.0"
@@ -20,6 +20,8 @@ dependencies:
path: "../filcnaplo_desktop_ui/" path: "../filcnaplo_desktop_ui/"
filcnaplo_kreta_api: filcnaplo_kreta_api:
path: "../filcnaplo_kreta_api/" path: "../filcnaplo_kreta_api/"
filcnaplo_premium:
path: "../filcnaplo_premium/"
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
@@ -51,6 +53,15 @@ dependencies:
lottie: ^1.4.3 lottie: ^1.4.3
rive: ^0.9.1 rive: ^0.9.1
animated_background: ^2.0.0 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
uni_links: ^0.5.1
url_launcher: ^6.1.6
workmanager: ^0.5.1
dev_dependencies: dev_dependencies:
flutter_lints: ^2.0.1 flutter_lints: ^2.0.1

1
filcnaplo_premium Submodule

Submodule filcnaplo_premium added at 3f23e52748