Merge pull request #73 from refilc/dev

Dev
This commit is contained in:
Márton Kiss 2023-10-15 20:29:14 +02:00 committed by GitHub
commit 382961971d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 1133 additions and 1101 deletions

52
.gitignore vendored
View File

@ -1,52 +0,0 @@
# See https://www.dartlang.org/guides/libraries/private-files
.gitignore
termek.txt
.DS_Store
filc3.properties
local.properties
# Files and directories created by pub
.dart_tool/
.packages
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/
# Avoid committing generated Javascript files:
*.dart.js
*.info.json # Produced by the --dump-info flag.
*.js # When generated by dart2js. Don't specify *.js if your
# project includes source files written in JavaScript.
*.js_
*.js.deps
*.js.map
*.txt
filcnaplo/macos/Flutter/GeneratedPluginRegistrant.swift
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/connectivity_plus
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/dynamic_color
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/flutter_acrylic
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/share_plus_windows
filcnaplo/windows/flutter/ephemeral/.plugin_symlinks/url_launcher_windows
filcnaplo/windows/flutter/ephemeral/generated_config.cmake
filcnaplo/windows/flutter/generated_plugin_registrant.cc
filcnaplo/windows/flutter/generated_plugin_registrant.h
filcnaplo/windows/flutter/generated_plugins.cmake
filcnaplo/linux/flutter/generated_plugin_registrant.cc
filcnaplo/linux/flutter/generated_plugin_registrant.h
filcnaplo/linux/flutter/generated_plugins.cmake
filcnaplo/macos/Flutter/*
filcnaplo/ios/Podfile.lock
.vscode/
key.properties
.flutter-plugins*
filcnaplo/ios/Flutter/flutter_export_environment 4.sh
filcnaplo/ios/Flutter/Generated 4.xcconfig

3
.idea/.gitignore generated vendored
View File

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

40
.vscode/launch.json vendored
View File

@ -1,40 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "filcnaplo",
"cwd": "filcnaplo",
"request": "launch",
"type": "dart",
"toolArgs": [
"--dart-define=APPVER=$(cat pubspec.yaml | grep version: | cut -d' ' -f2 | cut -d+ -f1)"
]
},
// {
// "name": "filcnaplo release",
// "cwd": "filcnaplo release",
// "request": "launch",
// "type": "dart",
// "program": "lib/main.dart",
// "toolArgs": [
// "--dart-define=APPVER=$(cat pubspec.yaml | grep version: | cut -d' ' -f2 | cut -d+ -f1)"
// ]
// },
{
"name": "Flutter",
"program": "lib/main.dart",
"cwd": "filcnaplo",
"request": "launch",
"type": "dart",
"flutterMode": "debug"
},
{
"name": "Flutter (release)",
"program": "lib/main.dart",
"cwd": "filcnaplo",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}

47
filcnaplo/.gitignore vendored
View File

@ -1,47 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
key.properties

View File

@ -1,12 +0,0 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
.project

View File

@ -1,93 +0,0 @@
Copyright 2011 The Montserrat Project Authors (https://github.com/JulietaUla/Montserrat)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -1,33 +0,0 @@
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View File

@ -190,12 +190,22 @@ class FilcAPI {
static Future<void> sendReport(ErrorReport report) async { static Future<void> sendReport(ErrorReport report) async {
try { try {
http.Response res = await http.post(Uri.parse(reportApi), body: { Map body = {
"os": report.os, "os": report.os,
"version": report.version, "version": report.version,
"error": report.error, "error": report.error,
"stack_trace": report.stack, "stack_trace": report.stack,
}); };
var client = http.Client();
http.Response res = await client.post(
Uri.parse(reportApi),
body: body,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
);
if (res.statusCode != 200) { if (res.statusCode != 200) {
throw "HTTP ${res.statusCode}: ${res.body}"; throw "HTTP ${res.statusCode}: ${res.body}";
@ -213,6 +223,8 @@ class FilcAPI {
theme.json['background_color'] = theme.backgroundColor.value.toString(); theme.json['background_color'] = theme.backgroundColor.value.toString();
theme.json['panels_color'] = theme.panelsColor.value.toString(); theme.json['panels_color'] = theme.panelsColor.value.toString();
theme.json['accent_color'] = theme.accentColor.value.toString(); theme.json['accent_color'] = theme.accentColor.value.toString();
theme.json['icon_color'] = theme.iconColor.value.toString();
theme.json['shadow_effect'] = theme.shadowEffect.toString();
// set linked grade colors // set linked grade colors
theme.json['grade_colors_id'] = theme.gradeColors.id; theme.json['grade_colors_id'] = theme.gradeColors.id;

View File

@ -15,7 +15,8 @@ const settingsDB = DatabaseStruct("settings", {
"accent_color": int, "news": int, "seen_news": String, "accent_color": int, "news": int, "seen_news": String,
"developer_mode": int, "developer_mode": int,
"update_channel": int, "config": String, "custom_accent_color": int, "update_channel": int, "config": String, "custom_accent_color": int,
"custom_background_color": int, "custom_highlight_color": int, // general "custom_background_color": int, "custom_highlight_color": int,
"custom_icon_color": int, "shadow_effect": int, // general
"grade_color1": int, "grade_color2": int, "grade_color3": int, "grade_color1": int, "grade_color2": int, "grade_color3": int,
"grade_color4": int, "grade_color5": int, // grade colors "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,

View File

@ -22,7 +22,8 @@ class SubjectIconData {
}); });
} }
SubjectIconVariants createIcon({required IconData material, required IconData cupertino}) { SubjectIconVariants createIcon(
{required IconData material, required IconData cupertino}) {
return { return {
IconPack.material: material, IconPack.material: material,
IconPack.cupertino: cupertino, IconPack.cupertino: cupertino,
@ -30,88 +31,224 @@ SubjectIconVariants createIcon({required IconData material, required IconData cu
} }
class SubjectIcon { class SubjectIcon {
static String resolveName({Subject? subject, String? subjectName}) => _resolve(subject: subject, subjectName: subjectName).name; static String resolveName({GradeSubject? subject, String? subjectName}) =>
static IconData resolveVariant({Subject? subject, String? subjectName, required BuildContext context}) => _resolve(subject: subject, subjectName: subjectName).name;
_resolve(subject: subject, subjectName: subjectName).data[Provider.of<SettingsProvider>(context, listen: false).iconPack]!; static IconData resolveVariant(
{GradeSubject? 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}) { static SubjectIconData _resolve(
{GradeSubject? 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 ?? "")
String category = subject?.category.description.toLowerCase().specialChars() ?? ""; .toLowerCase()
.specialChars()
.trim();
String category =
subject?.category.description.toLowerCase().specialChars() ?? "";
// 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: createIcon(cupertino: CupertinoIcons.function, material: Icons.calculate_outlined), name: "function");
} else if (RegExp("magyar nyelv|nyelvtan").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.textformat_alt, material: Icons.spellcheck_outlined), name: "textformat.alt");
} else if (RegExp("irodalom").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.book, material: Icons.menu_book_outlined), name: "book");
} else if (RegExp("tor(i|tenelem)").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.compass, material: Icons.hourglass_empty_outlined), name: "safari");
} else if (RegExp("foldrajz").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.map, material: Icons.public_outlined), name: "map");
} else if (RegExp("rajz|muvtori|muveszet|vizualis").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.paintbrush, material: Icons.palette_outlined), name: "paintbrush");
} else if (RegExp("fizika").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.lightbulb, material: Icons.emoji_objects_outlined), name: "lightbulb");
} else if (RegExp("^enek|zene|szolfezs|zongora|korus").hasMatch(name)) {
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)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.sportscourt, material: Icons.sports_soccer_outlined), name: "sportscourt");
} else if (RegExp("kemia").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.lab_flask, material: Icons.science_outlined), name: "testtube.2");
} else if (RegExp("biologia").hasMatch(name)) {
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)) {
return SubjectIconData( return SubjectIconData(
data: createIcon(cupertino: CupertinoIcons.arrow_3_trianglepath, material: Icons.eco_outlined), name: "arrow.3.trianglepath"); data: createIcon(
cupertino: CupertinoIcons.function,
material: Icons.calculate_outlined),
name: "function");
} else if (RegExp("magyar nyelv|nyelvtan").hasMatch(name)) {
return SubjectIconData(
data: createIcon(
cupertino: CupertinoIcons.textformat_alt,
material: Icons.spellcheck_outlined),
name: "textformat.alt");
} else if (RegExp("irodalom").hasMatch(name)) {
return SubjectIconData(
data: createIcon(
cupertino: CupertinoIcons.book,
material: Icons.menu_book_outlined),
name: "book");
} else if (RegExp("tor(i|tenelem)").hasMatch(name)) {
return SubjectIconData(
data: createIcon(
cupertino: CupertinoIcons.compass,
material: Icons.hourglass_empty_outlined),
name: "safari");
} else if (RegExp("foldrajz").hasMatch(name)) {
return SubjectIconData(
data: createIcon(
cupertino: CupertinoIcons.map, material: Icons.public_outlined),
name: "map");
} else if (RegExp("rajz|muvtori|muveszet|vizualis").hasMatch(name)) {
return SubjectIconData(
data: createIcon(
cupertino: CupertinoIcons.paintbrush,
material: Icons.palette_outlined),
name: "paintbrush");
} else if (RegExp("fizika").hasMatch(name)) {
return SubjectIconData(
data: createIcon(
cupertino: CupertinoIcons.lightbulb,
material: Icons.emoji_objects_outlined),
name: "lightbulb");
} else if (RegExp("^enek|zene|szolfezs|zongora|korus").hasMatch(name)) {
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)) {
return SubjectIconData(
data: createIcon(
cupertino: CupertinoIcons.sportscourt,
material: Icons.sports_soccer_outlined),
name: "sportscourt");
} else if (RegExp("kemia").hasMatch(name)) {
return SubjectIconData(
data: createIcon(
cupertino: CupertinoIcons.lab_flask,
material: Icons.science_outlined),
name: "testtube.2");
} else if (RegExp("biologia").hasMatch(name)) {
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)) {
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: createIcon(cupertino: CupertinoIcons.heart, material: Icons.favorite_border_outlined), 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: createIcon(cupertino: CupertinoIcons.money_dollar, material: Icons.savings_outlined), 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: createIcon(cupertino: CupertinoIcons.device_laptop, material: Icons.computer_outlined), 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( return SubjectIconData(
data: createIcon(cupertino: CupertinoIcons.chevron_left_slash_chevron_right, material: Icons.code_outlined), data: createIcon(
cupertino: CupertinoIcons.chevron_left_slash_chevron_right,
material: Icons.code_outlined),
name: "chevron.left.forwardslash.chevron.right"); name: "chevron.left.forwardslash.chevron.right");
} else if (RegExp("halozat").hasMatch(name)) { } else if (RegExp("halozat").hasMatch(name)) {
return SubjectIconData( return SubjectIconData(
data: createIcon(cupertino: CupertinoIcons.antenna_radiowaves_left_right, material: Icons.wifi_tethering_outlined), data: createIcon(
cupertino: CupertinoIcons.antenna_radiowaves_left_right,
material: Icons.wifi_tethering_outlined),
name: "antenna.radiowaves.left.and.right"); name: "antenna.radiowaves.left.and.right");
} else if (RegExp("szinhaz").hasMatch(name)) { } else if (RegExp("szinhaz").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.hifispeaker, material: Icons.theater_comedy_outlined), 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: createIcon(cupertino: CupertinoIcons.film, material: Icons.theaters_outlined), 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: createIcon(cupertino: CupertinoIcons.bolt, material: Icons.electrical_services_outlined), 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: createIcon(cupertino: CupertinoIcons.wrench, material: Icons.precision_manufacturing_outlined), 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: createIcon(cupertino: CupertinoIcons.hammer, material: Icons.build_outlined), 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: createIcon(cupertino: CupertinoIcons.music_mic, material: Icons.speaker_outlined), 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: createIcon(cupertino: CupertinoIcons.bubble_left, material: Icons.psychology_outlined), name: "bubble.left"); return SubjectIconData(
} else if (RegExp("osztaly(fonoki|kozosseg)").hasMatch(name) || name == "ofo") { data: createIcon(
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.group, material: Icons.groups_outlined), name: "person.3"); cupertino: CupertinoIcons.bubble_left,
material: Icons.psychology_outlined),
name: "bubble.left");
} else if (RegExp("osztaly(fonoki|kozosseg)").hasMatch(name) ||
name == "ofo") {
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: createIcon(cupertino: CupertinoIcons.chart_pie, material: Icons.account_balance_outlined), 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: createIcon(cupertino: CupertinoIcons.checkmark_seal, material: Icons.verified_outlined), 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: createIcon(cupertino: CupertinoIcons.smiley, material: Icons.emoji_people_outlined), name: "face.smiling"); return SubjectIconData(
} else if (RegExp("angol|nemet|francia|olasz|orosz|spanyol|latin|kinai|nyelv").hasMatch(name)) { data: createIcon(
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.globe, material: Icons.translate_outlined), name: "globe"); 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)) {
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: createIcon(material: FilcIcons.linux, cupertino: FilcIcons.linux)); return SubjectIconData(
data: createIcon(
material: FilcIcons.linux, cupertino: FilcIcons.linux));
} else if (RegExp("adatbazis").hasMatch(name)) { } else if (RegExp("adatbazis").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.table_badge_more, material: Icons.table_chart), name: "table.badge.more"); return SubjectIconData(
data: createIcon(
cupertino: CupertinoIcons.table_badge_more,
material: Icons.table_chart),
name: "table.badge.more");
} else if (RegExp("asztali alkalmazasok").hasMatch(name)) { } else if (RegExp("asztali alkalmazasok").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.macwindow, material: Icons.desktop_windows_outlined), name: "macwindow"); return SubjectIconData(
data: createIcon(
cupertino: CupertinoIcons.macwindow,
material: Icons.desktop_windows_outlined),
name: "macwindow");
} else if (RegExp("projekt").hasMatch(name)) { } else if (RegExp("projekt").hasMatch(name)) {
return SubjectIconData(data: createIcon(cupertino: CupertinoIcons.person_3_fill, material: Icons.groups_3), name: "person.3.fill"); return SubjectIconData(
data: createIcon(
cupertino: CupertinoIcons.person_3_fill,
material: Icons.groups_3),
name: "person.3.fill");
} }
return SubjectIconData(); return SubjectIconData();
@ -119,10 +256,13 @@ class SubjectIcon {
} }
class ShortSubject { class ShortSubject {
static String resolve({Subject? subject, String? subjectName}) { static String resolve({GradeSubject? 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();
// String category = subject?.category.description.toLowerCase().specialChars() ?? ""; // String category = subject?.category.description.toLowerCase().specialChars() ?? "";
if (RegExp("magyar irodalom").hasMatch(name)) { if (RegExp("magyar irodalom").hasMatch(name)) {
@ -137,7 +277,9 @@ class ShortSubject {
return "Tesi"; return "Tesi";
} else if (RegExp("tortenelem").hasMatch(name)) { } else if (RegExp("tortenelem").hasMatch(name)) {
return "Töri"; return "Töri";
} 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 (subject?.name ?? subjectName ?? "?").replaceAll(" nyelv", ""); return (subject?.name ?? subjectName ?? "?").replaceAll(" nyelv", "");
} else if (RegExp("informatika").hasMatch(name)) { } else if (RegExp("informatika").hasMatch(name)) {
return "Infó"; return "Infó";

View File

@ -67,6 +67,8 @@ class SettingsProvider extends ChangeNotifier {
Color _customAccentColor; Color _customAccentColor;
Color _customBackgroundColor; Color _customBackgroundColor;
Color _customHighlightColor; Color _customHighlightColor;
Color _customIconColor;
bool _shadowEffect;
List<String> _premiumScopes; List<String> _premiumScopes;
String _premiumAccessToken; String _premiumAccessToken;
String _premiumLogin; String _premiumLogin;
@ -112,6 +114,8 @@ class SettingsProvider extends ChangeNotifier {
required Color customAccentColor, required Color customAccentColor,
required Color customBackgroundColor, required Color customBackgroundColor,
required Color customHighlightColor, required Color customHighlightColor,
required Color customIconColor,
required bool shadowEffect,
required List<String> premiumScopes, required List<String> premiumScopes,
required String premiumAccessToken, required String premiumAccessToken,
required String premiumLogin, required String premiumLogin,
@ -155,6 +159,8 @@ class SettingsProvider extends ChangeNotifier {
_customAccentColor = customAccentColor, _customAccentColor = customAccentColor,
_customBackgroundColor = customBackgroundColor, _customBackgroundColor = customBackgroundColor,
_customHighlightColor = customHighlightColor, _customHighlightColor = customHighlightColor,
_customIconColor = customIconColor,
_shadowEffect = shadowEffect,
_premiumScopes = premiumScopes, _premiumScopes = premiumScopes,
_premiumAccessToken = premiumAccessToken, _premiumAccessToken = premiumAccessToken,
_premiumLogin = premiumLogin, _premiumLogin = premiumLogin,
@ -217,6 +223,8 @@ class SettingsProvider extends ChangeNotifier {
customAccentColor: Color(map["custom_accent_color"]), customAccentColor: Color(map["custom_accent_color"]),
customBackgroundColor: Color(map["custom_background_color"]), customBackgroundColor: Color(map["custom_background_color"]),
customHighlightColor: Color(map["custom_highlight_color"]), customHighlightColor: Color(map["custom_highlight_color"]),
customIconColor: Color(map["custom_icon_color"]),
shadowEffect: map["shadow_effect"] == 1,
premiumScopes: jsonDecode(map["premium_scopes"]).cast<String>(), premiumScopes: jsonDecode(map["premium_scopes"]).cast<String>(),
premiumAccessToken: map["premium_token"], premiumAccessToken: map["premium_token"],
premiumLogin: map["premium_login"], premiumLogin: map["premium_login"],
@ -267,6 +275,8 @@ class SettingsProvider extends ChangeNotifier {
"custom_accent_color": _customAccentColor.value, "custom_accent_color": _customAccentColor.value,
"custom_background_color": _customBackgroundColor.value, "custom_background_color": _customBackgroundColor.value,
"custom_highlight_color": _customHighlightColor.value, "custom_highlight_color": _customHighlightColor.value,
"custom_icon_color": _customIconColor.value,
"shadow_effect": _shadowEffect ? 1 : 0,
"premium_scopes": jsonEncode(_premiumScopes), "premium_scopes": jsonEncode(_premiumScopes),
"premium_token": _premiumAccessToken, "premium_token": _premiumAccessToken,
"premium_login": _premiumLogin, "premium_login": _premiumLogin,
@ -321,6 +331,8 @@ class SettingsProvider extends ChangeNotifier {
customAccentColor: const Color(0xff3D7BF4), customAccentColor: const Color(0xff3D7BF4),
customBackgroundColor: const Color(0xff000000), customBackgroundColor: const Color(0xff000000),
customHighlightColor: const Color(0xff222222), customHighlightColor: const Color(0xff222222),
customIconColor: const Color(0x00000000),
shadowEffect: true,
premiumScopes: [PremiumScopes.all], premiumScopes: [PremiumScopes.all],
premiumAccessToken: "igen", premiumAccessToken: "igen",
premiumLogin: "igen", premiumLogin: "igen",
@ -370,6 +382,8 @@ class SettingsProvider extends ChangeNotifier {
: _customAccentColor; : _customAccentColor;
Color? get customBackgroundColor => _customBackgroundColor; Color? get customBackgroundColor => _customBackgroundColor;
Color? get customHighlightColor => _customHighlightColor; Color? get customHighlightColor => _customHighlightColor;
Color? get customIconColor => _customIconColor;
bool get shadowEffect => _shadowEffect;
List<String> get premiumScopes => _premiumScopes; List<String> get premiumScopes => _premiumScopes;
String get premiumAccessToken => _premiumAccessToken; String get premiumAccessToken => _premiumAccessToken;
String get premiumLogin => _premiumLogin; String get premiumLogin => _premiumLogin;
@ -415,6 +429,8 @@ class SettingsProvider extends ChangeNotifier {
Color? customAccentColor, Color? customAccentColor,
Color? customBackgroundColor, Color? customBackgroundColor,
Color? customHighlightColor, Color? customHighlightColor,
Color? customIconColor,
bool? shadowEffect,
List<String>? premiumScopes, List<String>? premiumScopes,
String? premiumAccessToken, String? premiumAccessToken,
String? premiumLogin, String? premiumLogin,
@ -511,6 +527,12 @@ class SettingsProvider extends ChangeNotifier {
customHighlightColor != _customHighlightColor) { customHighlightColor != _customHighlightColor) {
_customHighlightColor = customHighlightColor; _customHighlightColor = customHighlightColor;
} }
if (customIconColor != null && customIconColor != _customIconColor) {
_customIconColor = customIconColor;
}
if (shadowEffect != null && shadowEffect != _shadowEffect) {
_shadowEffect = shadowEffect;
}
if (premiumScopes != null && premiumScopes != _premiumScopes) { if (premiumScopes != null && premiumScopes != _premiumScopes) {
_premiumScopes = premiumScopes; _premiumScopes = premiumScopes;
} }

View File

@ -8,6 +8,8 @@ class SharedTheme {
Color backgroundColor; Color backgroundColor;
Color panelsColor; Color panelsColor;
Color accentColor; Color accentColor;
Color iconColor;
bool shadowEffect;
SharedGradeColors gradeColors; SharedGradeColors gradeColors;
SharedTheme({ SharedTheme({
@ -18,6 +20,8 @@ class SharedTheme {
required this.backgroundColor, required this.backgroundColor,
required this.panelsColor, required this.panelsColor,
required this.accentColor, required this.accentColor,
required this.iconColor,
required this.shadowEffect,
required this.gradeColors, required this.gradeColors,
}); });
@ -30,6 +34,8 @@ class SharedTheme {
backgroundColor: Color(json['background_color']), backgroundColor: Color(json['background_color']),
panelsColor: Color(json['panels_color']), panelsColor: Color(json['panels_color']),
accentColor: Color(json['accent_color']), accentColor: Color(json['accent_color']),
iconColor: Color(json['icon_color']),
shadowEffect: json['shadow_effect'] ?? true,
gradeColors: gradeColors, gradeColors: gradeColors,
); );
} }

View File

@ -5,17 +5,21 @@ enum SubjectLessonCountUpdateState { ready, updating }
class SubjectLessonCount { class SubjectLessonCount {
DateTime lastUpdated; DateTime lastUpdated;
Map<Subject, int> subjects; Map<GradeSubject, int> subjects;
SubjectLessonCountUpdateState state; SubjectLessonCountUpdateState state;
SubjectLessonCount({required this.lastUpdated, required this.subjects, this.state = SubjectLessonCountUpdateState.ready}); SubjectLessonCount(
{required this.lastUpdated,
required this.subjects,
this.state = SubjectLessonCountUpdateState.ready});
factory SubjectLessonCount.fromMap(Map json) { factory SubjectLessonCount.fromMap(Map json) {
return SubjectLessonCount( return SubjectLessonCount(
lastUpdated: DateTime.fromMillisecondsSinceEpoch(json["last_updated"] ?? 0), lastUpdated:
DateTime.fromMillisecondsSinceEpoch(json["last_updated"] ?? 0),
subjects: ((json["subjects"] as Map?) ?? {}).map( subjects: ((json["subjects"] as Map?) ?? {}).map(
(key, value) => MapEntry( (key, value) => MapEntry(
Subject(id: key, name: "", category: Category.fromJson({})), GradeSubject(id: key, name: "", category: Category.fromJson({})),
value, value,
), ),
), ),

View File

@ -214,6 +214,8 @@ Widget filterItemBuilder(
child: DecoratedBox( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
boxShadow: [ boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow( BoxShadow(
offset: const Offset(0, 21), offset: const Offset(0, 21),
blurRadius: 23.0, blurRadius: 23.0,

View File

@ -34,8 +34,7 @@ class GradeTile extends StatelessWidget {
GradeCalculatorProvider calculatorProvider = GradeCalculatorProvider calculatorProvider =
Provider.of<GradeCalculatorProvider>(context, listen: false); Provider.of<GradeCalculatorProvider>(context, listen: false);
SettingsProvider settingsProvider = SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
Provider.of<SettingsProvider>(context);
// Test order: // Test order:
// description // description
// mode // mode
@ -50,7 +49,8 @@ class GradeTile extends StatelessWidget {
} }
} else { } else {
title = subjectName; title = subjectName;
isTitleItalic = grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics; isTitleItalic =
grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics;
} }
// Test order: // Test order:
@ -62,7 +62,9 @@ class GradeTile extends StatelessWidget {
? modeDescription ? modeDescription
: "" : ""
: subjectName; : subjectName;
isSubtitleItalic = isSubjectView ? false : grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics; isSubtitleItalic = isSubjectView
? false
: grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics;
} else { } else {
subtitle = grade.value.valueName.split("(")[0]; subtitle = grade.value.valueName.split("(")[0];
} }
@ -127,9 +129,7 @@ class GradeTile extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontStyle: isTitleItalic fontStyle: isTitleItalic ? FontStyle.italic : null),
? FontStyle.italic
: null),
), ),
subtitle: subtitle != "" subtitle: subtitle != ""
? censored ? censored
@ -149,7 +149,10 @@ class GradeTile extends StatelessWidget {
subtitle, subtitle,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: isSubtitleItalic ? FontStyle.italic : null), style: TextStyle(
fontWeight: FontWeight.w500,
fontStyle:
isSubtitleItalic ? FontStyle.italic : null),
) )
: null, : null,
trailing: isSubjectView trailing: isSubjectView
@ -210,8 +213,8 @@ class GradeValueWidget extends StatelessWidget {
GradeValue value = this.value; GradeValue value = this.value;
bool isSubjectView = SubjectGradesContainer.of(context) != null; bool isSubjectView = SubjectGradesContainer.of(context) != null;
Color color = Color color = this.color ??
this.color ?? gradeColor(context: context, value: value.value, nocolor: nocolor); gradeColor(context: context, value: value.value, nocolor: nocolor);
Widget valueText; Widget valueText;
final percentage = value.percentage; final percentage = value.percentage;
@ -283,7 +286,9 @@ class GradeValueWidget extends StatelessWidget {
color: color.withOpacity(contrast ? 1.0 : .25), color: color.withOpacity(contrast ? 1.0 : .25),
shape: BoxShape.circle, shape: BoxShape.circle,
boxShadow: [ boxShadow: [
if (shadow) if (shadow &&
Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow( BoxShadow(
color: color, color: color,
blurRadius: 62.0, blurRadius: 62.0,

View File

@ -1 +0,0 @@
flutter/ephemeral

View File

@ -1,116 +0,0 @@
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
set(BINARY_NAME "filcnaplo")
set(APPLICATION_ID "hu.filc.filcnaplo")
cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
# Configure build options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Application build
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
apply_standard_settings(${BINARY_NAME})
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()

View File

@ -1,87 +0,0 @@
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
)
add_dependencies(flutter flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)

View File

@ -1,7 +0,0 @@
# Flutter-related
**/Flutter/ephemeral/
**/Pods/
# Xcode-related
**/dgph
**/xcuserdata/

View File

@ -1,2 +0,0 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"

View File

@ -1,2 +0,0 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"

View File

@ -3,7 +3,7 @@ description: "Nem hivatalos e-napló alkalmazás az e-Kréta rendszerhez"
homepage: https://refilc.hu homepage: https://refilc.hu
publish_to: "none" publish_to: "none"
version: 4.3.1+230 version: 4.4.0+232
environment: environment:
sdk: ">=2.17.0 <3.0.0" sdk: ">=2.17.0 <3.0.0"

View File

@ -1,17 +0,0 @@
flutter/ephemeral/
# Visual Studio user-specific files.
*.suo
*.user
*.userosscache
*.sln.docstates
# Visual Studio build-related files.
x64/
x86/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

View File

@ -1,47 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
pubspec.lock

View File

@ -31,7 +31,7 @@ import 'absences_page.i18n.dart';
enum AbsenceFilter { absences, delays, misses } enum AbsenceFilter { absences, delays, misses }
class SubjectAbsence { class SubjectAbsence {
Subject subject; GradeSubject subject;
List<Absence> absences; List<Absence> absences;
double percentage; double percentage;
@ -56,7 +56,7 @@ class _AbsencesPageState extends State<AbsencesPage>
late String firstName; late String firstName;
late TabController _tabController; late TabController _tabController;
late List<SubjectAbsence> absences = []; late List<SubjectAbsence> absences = [];
final Map<Subject, Lesson> _lessonCount = {}; final Map<GradeSubject, Lesson> _lessonCount = {};
@override @override
void initState() { void initState() {
@ -87,7 +87,7 @@ class _AbsencesPageState extends State<AbsencesPage>
} }
void buildSubjectAbsences() { void buildSubjectAbsences() {
Map<Subject, SubjectAbsence> _absences = {}; Map<GradeSubject, SubjectAbsence> _absences = {};
for (final absence in absenceProvider.absences) { for (final absence in absenceProvider.absences) {
if (absence.delay != 0) continue; if (absence.delay != 0) continue;

View File

@ -32,7 +32,7 @@ class GradeSubjectView extends StatefulWidget {
const GradeSubjectView(this.subject, {Key? key, this.groupAverage = 0.0}) const GradeSubjectView(this.subject, {Key? key, this.groupAverage = 0.0})
: super(key: key); : super(key: key);
final Subject subject; final GradeSubject subject;
final double groupAverage; final double groupAverage;
void push(BuildContext context, {bool root = false}) { void push(BuildContext context, {bool root = false}) {
@ -62,7 +62,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
bool gradeCalcMode = false; bool gradeCalcMode = false;
List<Grade> getSubjectGrades(Subject subject) => !gradeCalcMode List<Grade> getSubjectGrades(GradeSubject subject) => !gradeCalcMode
? gradeProvider.grades.where((e) => e.subject == subject).toList() ? gradeProvider.grades.where((e) => e.subject == subject).toList()
: calculatorProvider.grades.where((e) => e.subject == subject).toList(); : calculatorProvider.grades.where((e) => e.subject == subject).toList();

View File

@ -41,7 +41,7 @@ class _GradesPageState extends State<GradesPage> {
int avgDropValue = 0; int avgDropValue = 0;
List<Grade> getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider List<Grade> getSubjectGrades(GradeSubject subject, {int days = 0}) => gradeProvider
.grades .grades
.where((e) => .where((e) =>
e.subject == subject && e.subject == subject &&
@ -51,14 +51,14 @@ class _GradesPageState extends State<GradesPage> {
.toList(); .toList();
void generateTiles() { void generateTiles() {
List<Subject> subjects = gradeProvider.grades List<GradeSubject> subjects = gradeProvider.grades
.map((e) => e.subject) .map((e) => e.subject)
.toSet() .toSet()
.toList() .toList()
..sort((a, b) => a.name.compareTo(b.name)); ..sort((a, b) => a.name.compareTo(b.name));
List<Widget> tiles = []; List<Widget> tiles = [];
Map<Subject, double> subjectAvgs = {}; Map<GradeSubject, double> subjectAvgs = {};
tiles.addAll(subjects.map((subject) { tiles.addAll(subjects.map((subject) {
List<Grade> subjectGrades = getSubjectGrades(subject); List<Grade> subjectGrades = getSubjectGrades(subject);

View File

@ -16,7 +16,7 @@ extension Localization on String {
"7_days_average": "Weekly Average", "7_days_average": "Weekly Average",
"subjectavg": "Subject Average", "subjectavg": "Subject Average",
"classavg": "Class Average", "classavg": "Class Average",
"fail_warning": "Faliure warning", "fail_warning": "Failure warning",
"fail_warning_description": "You are failing %d subject(s)", "fail_warning_description": "You are failing %d subject(s)",
}, },
"hu_hu": { "hu_hu": {

View File

@ -1,47 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
pubspec.lock

View File

@ -54,6 +54,30 @@ class KretaAPI {
String iss, String uid, String type) => String iss, String uid, String type) =>
BaseKreta.kreta(iss) + BaseKreta.kreta(iss) +
KretaApiEndpoints.downloadHomeworkAttachments(uid, type); KretaApiEndpoints.downloadHomeworkAttachments(uid, type);
static String subjects(String iss, String uid) =>
BaseKreta.kreta(iss) +
KretaApiEndpoints.subjects +
"?oktatasiNevelesiFeladatUid=" + uid;
// Structure:
// {
// "Uid": 000,
// "Tantargy": {
// "Uid": 000,
// "Nev": "Irodalom",
// "Kategoria": {
// "Uid": "000,magyar_nyelv_es_irodalom",
// "Nev": "magyar_nyelv_es_irodalom",
// "Leiras": "Magyar nyelv és irodalom"
// },
// "SortIndex": 0,
// },
// "Atlag": null, // float
// "AtlagAlakulasaIdoFuggvenyeben": Array[], // no idea what this is
// "SulyozottOsztalyzatOsszege": null, // int | float
// "SulyozottOsztalyzatSzama": null, // int | float
// "SortIndex": 0
// }
// refer to https://discord.com/channels/1111649116020285532/1111798771513303102/1148368925969612920
// ADMIN API // ADMIN API
static const sendMessage = static const sendMessage =
@ -105,6 +129,7 @@ class KretaApiEndpoints {
static const capabilities = "/ellenorzo/V3/Sajat/Intezmenyek"; static const capabilities = "/ellenorzo/V3/Sajat/Intezmenyek";
static String downloadHomeworkAttachments(String uid, String type) => static String downloadHomeworkAttachments(String uid, String type) =>
"/ellenorzo/V3/Sajat/Csatolmany/$uid"; "/ellenorzo/V3/Sajat/Csatolmany/$uid";
static const subjects = "/ellenorzo/V3/Sajat/Ertekelesek/Atlagok/TantargyiAtlagok";
} }
class KretaAdminEndpoints { class KretaAdminEndpoints {

View File

@ -13,7 +13,7 @@ class Absence {
Category? justification; Category? justification;
Category? type; Category? type;
Category? mode; Category? mode;
Subject subject; GradeSubject subject;
DateTime lessonStart; DateTime lessonStart;
DateTime lessonEnd; DateTime lessonEnd;
int? lessonIndex; int? lessonIndex;
@ -83,7 +83,7 @@ class Absence {
: null, : null,
type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null, type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null,
mode: json["Mod"] != null ? Category.fromJson(json["Mod"]) : null, mode: json["Mod"] != null ? Category.fromJson(json["Mod"]) : null,
subject: Subject.fromJson(json["Tantargy"] ?? {}), subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
lessonStart: lessonStart, lessonStart: lessonStart,
lessonEnd: lessonEnd, lessonEnd: lessonEnd,
lessonIndex: lessonIndex, lessonIndex: lessonIndex,

View File

@ -1,3 +1,5 @@
import 'package:filcnaplo_kreta_api/models/subject.dart';
import 'category.dart'; import 'category.dart';
import 'teacher.dart'; import 'teacher.dart';
@ -6,8 +8,9 @@ class Exam {
DateTime date; DateTime date;
DateTime writeDate; DateTime writeDate;
Category? mode; Category? mode;
int? subjectIndex; // int? subjectIndex;
String subjectName; // String subjectName;
GradeSubject subject;
Teacher teacher; Teacher teacher;
String description; String description;
String group; String group;
@ -18,8 +21,9 @@ class Exam {
required this.date, required this.date,
required this.writeDate, required this.writeDate,
this.mode, this.mode,
this.subjectIndex, // this.subjectIndex,
required this.subjectName, // required this.subjectName,
required this.subject,
required this.teacher, required this.teacher,
required this.description, required this.description,
required this.group, required this.group,
@ -36,8 +40,9 @@ class Exam {
? DateTime.parse(json["Datum"]).toLocal() ? DateTime.parse(json["Datum"]).toLocal()
: DateTime(0), : DateTime(0),
mode: json["Modja"] != null ? Category.fromJson(json["Modja"]) : null, mode: json["Modja"] != null ? Category.fromJson(json["Modja"]) : null,
subjectIndex: json["OrarendiOraOraszama"], // subjectIndex: json["OrarendiOraOraszama"],
subjectName: json["TantargyNeve"] ?? "", // subjectName: json["TantargyNeve"] ?? "",
subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()), teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()),
description: (json["Temaja"] ?? "").trim(), description: (json["Temaja"] ?? "").trim(),
group: json["OsztalyCsoport"] != null group: json["OsztalyCsoport"] != null

View File

@ -12,7 +12,7 @@ class Grade {
String description; String description;
GradeType type; GradeType type;
String groupId; String groupId;
Subject subject; GradeSubject subject;
Category? gradeType; Category? gradeType;
Category mode; Category mode;
DateTime writeDate; DateTime writeDate;
@ -57,7 +57,7 @@ class Grade {
? Category.getGradeType(json["Tipus"]["Nev"]) ? Category.getGradeType(json["Tipus"]["Nev"])
: GradeType.unknown, : GradeType.unknown,
groupId: (json["OsztalyCsoport"] ?? {})["Uid"] ?? "", groupId: (json["OsztalyCsoport"] ?? {})["Uid"] ?? "",
subject: Subject.fromJson(json["Tantargy"] ?? {}), subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
gradeType: json["ErtekFajta"] != null gradeType: json["ErtekFajta"] != null
? Category.fromJson(json["ErtekFajta"]) ? Category.fromJson(json["ErtekFajta"])
: null, : null,

View File

@ -3,7 +3,7 @@ import 'package:filcnaplo_kreta_api/models/subject.dart';
class GroupAverage { class GroupAverage {
String uid; String uid;
double average; double average;
Subject subject; GradeSubject subject;
Map json; Map json;
GroupAverage({required this.uid, required this.average, required this.subject, this.json = const {}}); GroupAverage({required this.uid, required this.average, required this.subject, this.json = const {}});
@ -12,7 +12,7 @@ class GroupAverage {
return GroupAverage( return GroupAverage(
uid: json["Uid"] ?? "", uid: json["Uid"] ?? "",
average: json["OsztalyCsoportAtlag"] ?? 0, average: json["OsztalyCsoportAtlag"] ?? 0,
subject: Subject.fromJson(json["Tantargy"] ?? {}), subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
json: json, json: json,
); );
} }

View File

@ -12,7 +12,7 @@ class Homework {
bool homeworkEnabled; bool homeworkEnabled;
Teacher teacher; Teacher teacher;
String content; String content;
Subject subject; GradeSubject subject;
String group; String group;
List<HomeworkAttachment> attachments; List<HomeworkAttachment> attachments;
String id; String id;
@ -48,7 +48,7 @@ class Homework {
homeworkEnabled: json["IsTanuloHaziFeladatEnabled"] ?? false, homeworkEnabled: json["IsTanuloHaziFeladatEnabled"] ?? false,
teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()), teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()),
content: (json["Szoveg"] ?? "").trim(), content: (json["Szoveg"] ?? "").trim(),
subject: Subject.fromJson(json["Tantargy"] ?? {}), subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
group: json["OsztalyCsoport"] != null group: json["OsztalyCsoport"] != null
? json["OsztalyCsoport"]["Uid"] ?? "" ? json["OsztalyCsoport"]["Uid"] ?? ""
: "", : "",

View File

@ -6,7 +6,7 @@ class Lesson {
Map? json; Map? json;
Category? status; Category? status;
DateTime date; DateTime date;
Subject subject; GradeSubject subject;
String lessonIndex; String lessonIndex;
int? lessonYearIndex; int? lessonYearIndex;
Teacher? substituteTeacher; Teacher? substituteTeacher;
@ -68,7 +68,7 @@ class Lesson {
date: json["Datum"] != null date: json["Datum"] != null
? DateTime.parse(json["Datum"]).toLocal() ? DateTime.parse(json["Datum"]).toLocal()
: DateTime(0), : DateTime(0),
subject: Subject.fromJson(json["Tantargy"] ?? {}), subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
lessonIndex: json["Oraszam"] != null ? json["Oraszam"].toString() : "+", lessonIndex: json["Oraszam"] != null ? json["Oraszam"].toString() : "+",
lessonYearIndex: json["OraEvesSorszama"], lessonYearIndex: json["OraEvesSorszama"],
substituteTeacher: json["HelyettesTanarNeve"] != null substituteTeacher: json["HelyettesTanarNeve"] != null

View File

@ -1,6 +1,6 @@
import 'category.dart'; import 'category.dart';
class Subject { class GradeSubject {
String id; String id;
Category category; Category category;
String name; String name;
@ -8,11 +8,16 @@ class Subject {
bool get isRenamed => renamedTo != null; bool get isRenamed => renamedTo != null;
Subject({required this.id, required this.category, required this.name, this.renamedTo}); GradeSubject({
required this.id,
required this.category,
required this.name,
this.renamedTo,
});
factory Subject.fromJson(Map json) { factory GradeSubject.fromJson(Map json) {
final id = json["Uid"] ?? ""; final id = json["Uid"] ?? "";
return Subject( return GradeSubject(
id: id, id: id,
category: Category.fromJson(json["Kategoria"] ?? {}), category: Category.fromJson(json["Kategoria"] ?? {}),
name: (json["Nev"] ?? "").trim(), name: (json["Nev"] ?? "").trim(),
@ -21,7 +26,7 @@ class Subject {
@override @override
bool operator ==(other) { bool operator ==(other) {
if (other is! Subject) return false; if (other is! GradeSubject) return false;
return id == other.id; return id == other.id;
} }

View File

@ -27,19 +27,49 @@ class ExamProvider with ChangeNotifier {
// Load exams from the database // Load exams from the database
if (userId != null) { if (userId != null) {
var dbExams = await Provider.of<DatabaseProvider>(_context, listen: false).userQuery.getExams(userId: userId); var dbExams = await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getExams(userId: userId);
_exams = dbExams; _exams = dbExams;
notifyListeners(); notifyListeners();
await convertBySettings();
} }
} }
// for renamed subjects
Future<void> convertBySettings() async {
final _database = Provider.of<DatabaseProvider>(_context, listen: false);
Map<String, String> renamedSubjects =
(await _database.query.getSettings(_database)).renamedSubjectsEnabled
? await _database.userQuery.renamedSubjects(
userId:
Provider.of<UserProvider>(_context, listen: false).user!.id)
: {};
Map<String, String> renamedTeachers =
(await _database.query.getSettings(_database)).renamedTeachersEnabled
? await _database.userQuery.renamedTeachers(
userId:
Provider.of<UserProvider>(_context, listen: false).user!.id)
: {};
for (Exam exam in _exams) {
exam.subject.renamedTo =
renamedSubjects.isNotEmpty ? renamedSubjects[exam.subject.id] : null;
exam.teacher.renamedTo =
renamedTeachers.isNotEmpty ? renamedTeachers[exam.teacher.id] : null;
}
notifyListeners();
}
// Fetches Exams from the Kreta API then stores them in the database // Fetches Exams from the Kreta API then stores them in the database
Future<void> fetch() async { Future<void> fetch() async {
User? user = Provider.of<UserProvider>(_context, listen: false).user; User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot fetch Exams for User null"; if (user == null) throw "Cannot fetch Exams for User null";
String iss = user.instituteCode; String iss = user.instituteCode;
List? examsJson = await Provider.of<KretaClient>(_context, listen: false).getAPI(KretaAPI.exams(iss)); List? examsJson = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.exams(iss));
if (examsJson == null) throw "Cannot fetch Exams for User ${user.id}"; if (examsJson == null) throw "Cannot fetch Exams for User ${user.id}";
List<Exam> exams = examsJson.map((e) => Exam.fromJson(e)).toList(); List<Exam> exams = examsJson.map((e) => Exam.fromJson(e)).toList();
@ -52,8 +82,11 @@ class ExamProvider with ChangeNotifier {
if (user == null) throw "Cannot store Exams for User null"; if (user == null) throw "Cannot store Exams for User null";
String userId = user.id; String userId = user.id;
await Provider.of<DatabaseProvider>(_context, listen: false).userStore.storeExams(exams, userId: userId); await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeExams(exams, userId: userId);
_exams = exams; _exams = exams;
notifyListeners(); notifyListeners();
await convertBySettings();
} }
} }

View File

@ -1,6 +1,6 @@
import 'package:filcnaplo/api/providers/user_provider.dart'; import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/api/providers/database_provider.dart'; import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/models/settings.dart'; // import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/models/user.dart'; import 'package:filcnaplo/models/user.dart';
import 'package:filcnaplo_kreta_api/client/api.dart'; import 'package:filcnaplo_kreta_api/client/api.dart';
import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/client/client.dart';
@ -11,7 +11,7 @@ import 'package:provider/provider.dart';
class HomeworkProvider with ChangeNotifier { class HomeworkProvider with ChangeNotifier {
// Private // Private
late final SettingsProvider _settings; // late final SettingsProvider _settings;
late final UserProvider _user; late final UserProvider _user;
late final DatabaseProvider _database; late final DatabaseProvider _database;
@ -80,9 +80,11 @@ class HomeworkProvider with ChangeNotifier {
List? homeworkJson = []; List? homeworkJson = [];
try { try {
homeworkJson = await Provider.of<KretaClient>(_context, listen: false) Iterable hwjson = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.homework(iss, start: from)); .getAPI(KretaAPI.homework(iss, start: from));
homeworkJson = List.from(hwjson.map((model) => model));
} catch (e) { } catch (e) {
if (kDebugMode) print(e);
// error fetcing homework (unknown error) // error fetcing homework (unknown error)
} }
@ -95,15 +97,15 @@ class HomeworkProvider with ChangeNotifier {
await Future.forEach(homeworkJson.cast<Map>(), (Map hw) async { await Future.forEach(homeworkJson.cast<Map>(), (Map hw) async {
Map? e = await Provider.of<KretaClient>(_context, listen: false) Map? e = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.homework(iss, id: hw["Uid"])); .getAPI(KretaAPI.homework(iss, id: hw["Uid"]));
Map<String, String> renamedSubjects = _settings.renamedSubjectsEnabled // Map<String, String> renamedSubjects = _settings.renamedSubjectsEnabled
? await _database.userQuery.renamedSubjects(userId: _user.user!.id) // ? await _database.userQuery.renamedSubjects(userId: _user.user!.id)
: {}; // : {};
if (e != null) { if (e != null) {
Homework hw = Homework.fromJson(e); Homework hmwrk = Homework.fromJson(e);
hw.subject.renamedTo = // hw.subject.renamedTo =
renamedSubjects.isNotEmpty ? renamedSubjects[hw.subject.id] : null; // renamedSubjects.isNotEmpty ? renamedSubjects[hw.subject.id] : null;
homework.add(hw); homework.add(hmwrk);
} }
}); });
@ -112,6 +114,7 @@ class HomeworkProvider with ChangeNotifier {
if (db) await store(homework); if (db) await store(homework);
_homework = homework; _homework = homework;
notifyListeners(); notifyListeners();
await convertBySettings();
} }
// Stores Homework in the database // Stores Homework in the database

View File

@ -0,0 +1,165 @@
// import 'package:filcnaplo/api/providers/user_provider.dart';
// import 'package:filcnaplo/api/providers/database_provider.dart';
// import 'package:filcnaplo/models/settings.dart';
// import 'package:filcnaplo/models/user.dart';
// import 'package:filcnaplo_kreta_api/client/api.dart';
// import 'package:filcnaplo_kreta_api/client/client.dart';
// import 'package:filcnaplo_kreta_api/models/grade.dart';
// import 'package:filcnaplo_kreta_api/models/group_average.dart';
// import 'package:filcnaplo_kreta_api/providers/grade_provider.i18n.dart';
// import 'package:flutter/material.dart';
// class SubjectProvider with ChangeNotifier {
// // Private
// late List<Grade> _grades;
// late DateTime _lastSeen;
// late String _groups;
// List<GroupAverage> _groupAvg = [];
// late final SettingsProvider _settings;
// late final UserProvider _user;
// late final DatabaseProvider _database;
// late final KretaClient _kreta;
// // Public
// List<Grade> get grades => _grades;
// DateTime get lastSeenDate =>
// _settings.gradeOpeningFun ? _lastSeen : DateTime(3000);
// String get groups => _groups;
// List<GroupAverage> get groupAverages => _groupAvg;
// SubjectProvider({
// List<Grade> initialGrades = const [],
// required SettingsProvider settings,
// required UserProvider user,
// required DatabaseProvider database,
// required KretaClient kreta,
// }) {
// _settings = settings;
// _user = user;
// _database = database;
// _kreta = kreta;
// _grades = List.castFrom(initialGrades);
// _lastSeen = DateTime.now();
// if (_grades.isEmpty) restore();
// }
// Future<void> seenAll() async {
// String? userId = _user.id;
// if (userId != null) {
// final userStore = _database.userStore;
// userStore.storeLastSeenGrade(DateTime.now(), userId: userId);
// _lastSeen = DateTime.now();
// }
// }
// Future<void> restore() async {
// String? userId = _user.id;
// // Load grades from the database
// if (userId != null) {
// final userQuery = _database.userQuery;
// _grades = await userQuery.getGrades(userId: userId);
// await convertBySettings();
// _groupAvg = await userQuery.getGroupAverages(userId: userId);
// notifyListeners();
// DateTime lastSeenDB = await userQuery.lastSeenGrade(userId: userId);
// if (lastSeenDB.millisecondsSinceEpoch == 0 ||
// lastSeenDB.year == 0 ||
// !_settings.gradeOpeningFun) {
// _lastSeen = DateTime.now();
// await seenAll();
// } else {
// _lastSeen = lastSeenDB;
// }
// notifyListeners();
// }
// }
// // good student mode, renamed subjects
// Future<void> convertBySettings() async {
// Map<String, String> renamedSubjects = _settings.renamedSubjectsEnabled
// ? await _database.userQuery.renamedSubjects(userId: _user.user!.id)
// : {};
// Map<String, String> renamedTeachers = _settings.renamedTeachersEnabled
// ? await _database.userQuery.renamedTeachers(userId: _user.user!.id)
// : {};
// for (Grade grade in _grades) {
// grade.subject.renamedTo =
// renamedSubjects.isNotEmpty ? renamedSubjects[grade.subject.id] : null;
// grade.teacher.renamedTo =
// renamedTeachers.isNotEmpty ? renamedTeachers[grade.teacher.id] : null;
// grade.value.value =
// _settings.goodStudent ? 5 : grade.json!["SzamErtek"] ?? 0;
// grade.value.valueName = _settings.goodStudent
// ? "Jeles".i18n
// : '${grade.json!["SzovegesErtek"]}'
// .replaceAll(RegExp(r'[(]+[12345]?[)]'), '')
// .i18n;
// grade.value.shortName = _settings.goodStudent
// ? "Jeles".i18n
// : '${grade.json!["SzovegesErtekelesRovidNev"]}' != "null" &&
// '${grade.json!["SzovegesErtekelesRovidNev"]}' != "-" &&
// '${grade.json!["SzovegesErtekelesRovidNev"]}'
// .replaceAll(RegExp(r'[0123456789]+[%]?'), '') !=
// ""
// ? '${grade.json!["SzovegesErtekelesRovidNev"]}'.i18n
// : grade.value.valueName;
// }
// notifyListeners();
// }
// // fetch subjects from kreten then store them
// Future<void> fetch() async {
// User? user = _user.user;
// if (user == null) throw "Cannot fetch Subjects for User null";
// String iss = user.instituteCode;
// List? gradesJson = await _kreta.getAPI(KretaAPI.subjects(iss, ""));
// if (gradesJson == null) throw "Cannot fetch Subjects for User ${user.id}";
// List<Grade> grades = gradesJson.map((e) => Grade.fromJson(e)).toList();
// if (grades.isNotEmpty || _grades.isNotEmpty) await store(grades);
// List? groupsJson = await _kreta.getAPI(KretaAPI.groups(iss));
// if (groupsJson == null || groupsJson.isEmpty) {
// throw "Cannot fetch Groups for User ${user.id}";
// }
// _groups = (groupsJson[0]["OktatasNevelesiFeladat"] ?? {})["Uid"] ?? "";
// List? groupAvgJson =
// await _kreta.getAPI(KretaAPI.groupAverages(iss, _groups));
// if (groupAvgJson == null) {
// throw "Cannot fetch Class Averages for User ${user.id}";
// }
// final groupAvgs =
// groupAvgJson.map((e) => GroupAverage.fromJson(e)).toList();
// await storeGroupAvg(groupAvgs);
// }
// // store subjects in db
// Future<void> store(List<Grade> grades) async {
// User? user = _user.user;
// if (user == null) throw "Cannot store Grades for User null";
// String userId = user.id;
// await _database.userStore.storeGrades(grades, userId: userId);
// _grades = grades;
// await convertBySettings();
// }
// Future<void> storeGroupAvg(List<GroupAverage> groupAvgs) async {
// _groupAvg = groupAvgs;
// User? user = _user.user;
// if (user == null) throw "Cannot store Grades for User null";
// String userId = user.id;
// await _database.userStore.storeGroupAverages(groupAvgs, userId: userId);
// notifyListeners();
// }
// }

View File

@ -1,47 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
pubspec.lock

View File

@ -1,8 +1,12 @@
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Panel extends StatelessWidget { class Panel extends StatelessWidget {
const Panel({Key? key, this.child, this.title, this.padding, this.hasShadow = true}) : super(key: key); const Panel(
{Key? key, this.child, this.title, this.padding, this.hasShadow = true})
: super(key: key);
final Widget? child; final Widget? child;
final Widget? title; final Widget? title;
@ -25,7 +29,9 @@ class Panel extends StatelessWidget {
borderRadius: BorderRadius.circular(16.0), borderRadius: BorderRadius.circular(16.0),
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
boxShadow: [ boxShadow: [
if (hasShadow) if (hasShadow &&
Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow( BoxShadow(
offset: const Offset(0, 21), offset: const Offset(0, 21),
blurRadius: 23.0, blurRadius: 23.0,
@ -51,7 +57,9 @@ class PanelTitle extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.only(left: 14.0, bottom: 8.0), padding: const EdgeInsets.only(left: 14.0, bottom: 8.0),
child: DefaultTextStyle( child: DefaultTextStyle(
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600, color: AppColors.of(context).text.withOpacity(0.65)), style: Theme.of(context).textTheme.titleMedium!.copyWith(
fontWeight: FontWeight.w600,
color: AppColors.of(context).text.withOpacity(0.65)),
child: title, child: title,
), ),
); );
@ -69,9 +77,12 @@ class PanelHeader extends StatelessWidget {
width: double.infinity, width: double.infinity,
padding: padding, padding: padding,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0)), borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0)),
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
boxShadow: [ boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow( BoxShadow(
offset: const Offset(0, 21), offset: const Offset(0, 21),
blurRadius: 23.0, blurRadius: 23.0,
@ -96,6 +107,8 @@ class PanelBody extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
boxShadow: [ boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow( BoxShadow(
offset: const Offset(0, 21), offset: const Offset(0, 21),
blurRadius: 23.0, blurRadius: 23.0,
@ -120,9 +133,13 @@ class PanelFooter extends StatelessWidget {
width: double.infinity, width: double.infinity,
padding: padding, padding: padding,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)), borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(16.0),
bottomRight: Radius.circular(16.0)),
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
boxShadow: [ boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow( BoxShadow(
offset: const Offset(0, 21), offset: const Offset(0, 21),
blurRadius: 23.0, blurRadius: 23.0,

View File

@ -1,5 +1,7 @@
import 'package:dotted_border/dotted_border.dart'; import 'package:dotted_border/dotted_border.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class EmptyCard extends StatefulWidget { class EmptyCard extends StatefulWidget {
const EmptyCard({ const EmptyCard({
@ -39,6 +41,8 @@ class _EmptyCardState extends State<EmptyCard> {
color: const Color(0x280008FF), color: const Color(0x280008FF),
borderRadius: const BorderRadius.all(Radius.circular(5)), borderRadius: const BorderRadius.all(Radius.circular(5)),
boxShadow: [ boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.08), color: Colors.black.withOpacity(0.08),
offset: const Offset(0, 5), offset: const Offset(0, 5),

View File

@ -35,12 +35,12 @@ class _PersonalityCardState extends State<PersonalityCard> {
late SettingsProvider settings; late SettingsProvider settings;
late List<int> subjectAvgsList = []; late List<int> subjectAvgsList = [];
late Map<Subject, double> subjectAvgs = {}; late Map<GradeSubject, double> subjectAvgs = {};
late double subjectAvg; late double subjectAvg;
late List<Grade> classWorkGrades; late List<Grade> classWorkGrades;
late Map<int, int> mostCommonGrade; late Map<int, int> mostCommonGrade;
late List<Absence> absences = []; late List<Absence> absences = [];
final Map<Subject, Lesson> _lessonCount = {}; final Map<GradeSubject, Lesson> _lessonCount = {};
late int totalDelays; late int totalDelays;
late int unexcusedAbsences; late int unexcusedAbsences;
@ -48,13 +48,14 @@ class _PersonalityCardState extends State<PersonalityCard> {
bool hold = false; bool hold = false;
List<Grade> getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider List<Grade> getSubjectGrades(GradeSubject subject, {int days = 0}) =>
.grades gradeProvider.grades
.where((e) => .where((e) =>
e.subject == subject && e.subject == subject &&
e.type == GradeType.midYear && e.type == GradeType.midYear &&
(days == 0 || (days == 0 ||
e.date.isBefore(DateTime.now().subtract(Duration(days: days))))) e.date
.isBefore(DateTime.now().subtract(Duration(days: days)))))
.toList(); .toList();
@override @override
@ -89,13 +90,13 @@ class _PersonalityCardState extends State<PersonalityCard> {
} }
void getGrades() { void getGrades() {
List<Subject> subjects = gradeProvider.grades List<GradeSubject> subjects = gradeProvider.grades
.map((e) => e.subject) .map((e) => e.subject)
.toSet() .toSet()
.toList() .toList()
..sort((a, b) => a.name.compareTo(b.name)); ..sort((a, b) => a.name.compareTo(b.name));
for (Subject subject in subjects) { for (GradeSubject subject in subjects) {
List<Grade> subjectGrades = getSubjectGrades(subject); List<Grade> subjectGrades = getSubjectGrades(subject);
double avg = AverageHelper.averageEvals(subjectGrades); double avg = AverageHelper.averageEvals(subjectGrades);

View File

@ -0,0 +1,22 @@
import 'package:filcnaplo_mobile_ui/common/action_button.dart';
import 'package:filcnaplo_mobile_ui/common/soon_alert/soon_alert.i18n.dart';
import 'package:flutter/material.dart';
class SoonAlert extends StatelessWidget {
const SoonAlert({Key? key}) : super(key: key);
static show({required BuildContext context}) =>
showDialog(context: context, builder: (context) => const SoonAlert());
@override
Widget build(BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
title: Text('soon'.i18n),
content: Text('soon_body'.i18n),
actions: [
ActionButton(label: "Ok", onTap: () => Navigator.of(context).pop())
],
);
}
}

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": {
"soon": "Soon...",
"soon_body": "This feature is currently not available yet!",
},
"hu_hu": {
"soon": "Hamarosan...",
"soon_body": "Ez a funkció jelenleg még nem elérhető!",
},
"de_de": {
"soon": "Bald...",
"soon_body": "Diese Funktion ist derzeit noch nicht verfügbar!",
},
};
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

@ -8,10 +8,16 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class AbsenceSubjectTile extends StatelessWidget { 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}) const AbsenceSubjectTile(this.subject,
{Key? key,
this.percentage = 0.0,
this.excused = 0,
this.unexcused = 0,
this.pending = 0,
this.onTap})
: super(key: key); : super(key: key);
final Subject subject; final GradeSubject subject;
final void Function()? onTap; final void Function()? onTap;
final double percentage; final double percentage;
@ -31,12 +37,20 @@ class AbsenceSubjectTile extends StatelessWidget {
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
onTap: onTap, onTap: onTap,
leading: Icon(SubjectIcon.resolveVariant(subject: subject, context: context), size: 32.0), leading: Icon(
SubjectIcon.resolveVariant(subject: subject, context: context),
size: 32.0),
title: Text( title: Text(
subject.renamedTo ?? subject.name.capital(), subject.renamedTo ?? subject.name.capital(),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15.0, fontStyle: subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null), style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 15.0,
fontStyle:
subject.isRenamed && settingsProvider.renamedSubjectsItalics
? FontStyle.italic
: null),
), ),
subtitle: AbsenceDisplay(excused, unexcused, pending), subtitle: AbsenceDisplay(excused, unexcused, pending),
trailing: Row( trailing: Row(
@ -47,7 +61,10 @@ class AbsenceSubjectTile extends StatelessWidget {
Stack( Stack(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
children: [ children: [
const Opacity(child: Text("100%", style: TextStyle(fontFamily: "monospace")), opacity: 0), const Opacity(
child: Text("100%",
style: TextStyle(fontFamily: "monospace")),
opacity: 0),
Text( Text(
percentage.round().toString() + "%", percentage.round().toString() + "%",
style: TextStyle( style: TextStyle(

View File

@ -6,7 +6,8 @@ import 'package:filcnaplo/utils/format.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart';
class ExamTile extends StatelessWidget { class ExamTile extends StatelessWidget {
const ExamTile(this.exam, {Key? key, this.onTap, this.padding}) : super(key: key); const ExamTile(this.exam, {Key? key, this.onTap, this.padding})
: super(key: key);
final Exam exam; final Exam exam;
final void Function()? onTap; final void Function()? onTap;
@ -22,26 +23,32 @@ class ExamTile extends StatelessWidget {
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0), contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
onTap: onTap, onTap: onTap,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)), shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
leading: SizedBox( leading: SizedBox(
width: 44, width: 44,
height: 44, height: 44,
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 2.0), padding: const EdgeInsets.only(top: 2.0),
child: Icon( child: Icon(
SubjectIcon.resolveVariant(subjectName: exam.subjectName, context: context), SubjectIcon.resolveVariant(
subject: exam.subject, context: context),
size: 28.0, size: 28.0,
color: AppColors.of(context).text.withOpacity(.75), color: AppColors.of(context).text.withOpacity(.75),
), ),
)), )),
title: Text( title: Text(
exam.description != "" ? exam.description : (exam.mode?.description ?? "Számonkérés"), exam.description != ""
? exam.description
: (exam.mode?.description ?? "Számonkérés"),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600), style: const TextStyle(fontWeight: FontWeight.w600),
), ),
subtitle: Text( subtitle: Text(
exam.subjectName.capital(), exam.subject.isRenamed
? exam.subject.renamedTo!
: exam.subject.name.capital(),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w500), style: const TextStyle(fontWeight: FontWeight.w500),

View File

@ -29,13 +29,15 @@ class ExamView extends StatelessWidget {
padding: const EdgeInsets.only(left: 6.0), padding: const EdgeInsets.only(left: 6.0),
child: Icon( child: Icon(
SubjectIcon.resolveVariant( SubjectIcon.resolveVariant(
subjectName: exam.subjectName, context: context), subject: exam.subject, context: context),
size: 36.0, size: 36.0,
color: AppColors.of(context).text.withOpacity(.75), color: AppColors.of(context).text.withOpacity(.75),
), ),
), ),
title: Text( title: Text(
exam.subjectName.capital(), exam.subject.isRenamed
? exam.subject.renamedTo!
: exam.subject.name.capital(),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600), style: const TextStyle(fontWeight: FontWeight.w600),

View File

@ -16,7 +16,7 @@ class GradeSubjectTile extends StatelessWidget {
this.averageBefore = 0.0}) this.averageBefore = 0.0})
: super(key: key); : super(key: key);
final Subject subject; final GradeSubject subject;
final void Function()? onTap; final void Function()? onTap;
final double average; final double average;
final double groupAverage; final double groupAverage;
@ -56,7 +56,10 @@ class GradeSubjectTile extends StatelessWidget {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontSize: 14.0, fontSize: 14.0,
color: textColor, color: textColor,
fontStyle: settingsProvider.renamedSubjectsItalics && subject.isRenamed ? FontStyle.italic : null), fontStyle:
settingsProvider.renamedSubjectsItalics && subject.isRenamed
? FontStyle.italic
: null),
), ),
trailing: Row( trailing: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,

View File

@ -46,7 +46,7 @@ class HomeworkTile extends StatelessWidget {
padding: const EdgeInsets.only(top: 2.0), padding: const EdgeInsets.only(top: 2.0),
child: Icon( child: Icon(
SubjectIcon.resolveVariant( SubjectIcon.resolveVariant(
subjectName: homework.subject.name, context: context), subject: homework.subject, context: context),
size: 28.0, size: 28.0,
color: AppColors.of(context).text.withOpacity(.75), color: AppColors.of(context).text.withOpacity(.75),
), ),
@ -69,7 +69,12 @@ class HomeworkTile extends StatelessWidget {
homework.subject.renamedTo ?? homework.subject.name.capital(), homework.subject.renamedTo ?? homework.subject.name.capital(),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: homework.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null), style: TextStyle(
fontWeight: FontWeight.w600,
fontStyle: homework.subject.isRenamed &&
settingsProvider.renamedSubjectsItalics
? FontStyle.italic
: null),
), ),
subtitle: censored subtitle: censored
? Wrap( ? Wrap(

View File

@ -44,7 +44,7 @@ class HomeworkView extends StatelessWidget {
ListTile( ListTile(
leading: Icon( leading: Icon(
SubjectIcon.resolveVariant( SubjectIcon.resolveVariant(
subjectName: homework.subject.name, context: context), subject: homework.subject, context: context),
size: 36.0, size: 36.0,
), ),
title: Text( title: Text(

View File

@ -57,9 +57,9 @@ class ChangedLessonTile extends StatelessWidget {
), ),
), ),
title: Text( title: Text(
lesson.substituteTeacher?.name != "" || lesson.substituteTeacher?.name != null lesson.status?.name == "Elmaradt" && lesson.substituteTeacher?.name != ""
? "substituted".i18n ? "cancelled".i18n
: "cancelled".i18n, : "substituted".i18n,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600), style: const TextStyle(fontWeight: FontWeight.w600),

View File

@ -1,7 +1,9 @@
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart'; import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:i18n_extension/i18n_widget.dart'; import 'package:i18n_extension/i18n_widget.dart';
import 'package:provider/provider.dart';
class StatisticsTile extends StatelessWidget { class StatisticsTile extends StatelessWidget {
const StatisticsTile({ const StatisticsTile({
@ -31,7 +33,9 @@ class StatisticsTile extends StatelessWidget {
} else { } else {
valueText = value.toStringAsFixed(0); valueText = value.toStringAsFixed(0);
} }
if (I18n.of(context).locale.languageCode != "en") valueText = valueText.replaceAll(".", ","); if (I18n.of(context).locale.languageCode != "en") {
valueText = valueText.replaceAll(".", ",");
}
if (value.isNaN) { if (value.isNaN) {
valueText = "?"; valueText = "?";
@ -44,6 +48,8 @@ class StatisticsTile extends StatelessWidget {
borderRadius: BorderRadius.circular(12.0), borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
boxShadow: [ boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow( BoxShadow(
offset: const Offset(0, 21), offset: const Offset(0, 21),
blurRadius: 23.0, blurRadius: 23.0,
@ -68,13 +74,19 @@ class StatisticsTile extends StatelessWidget {
if (title != null) const SizedBox(height: 4.0), if (title != null) const SizedBox(height: 4.0),
Container( Container(
margin: const EdgeInsets.only(top: 4.0), margin: const EdgeInsets.only(top: 4.0),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0), padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: fill ? (color ?? gradeColor(context: context, value: value)).withOpacity(.2) : null, color: fill
? (color ?? gradeColor(context: context, value: value))
.withOpacity(.2)
: null,
border: outline || fill border: outline || fill
? Border.all( ? Border.all(
color: (color ?? gradeColor(context: context, value: value)).withOpacity(outline ? 1.0 : 0.0), color:
width: fill ? 2.0 : 5.0, (color ?? gradeColor(context: context, value: value))
.withOpacity(outline ? 1.0 : 0.0),
width: fill ? 5.0 : 5.0,
) )
: null, : null,
borderRadius: BorderRadius.circular(45.0), borderRadius: BorderRadius.circular(45.0),
@ -96,7 +108,7 @@ class StatisticsTile extends StatelessWidget {
style: TextStyle( style: TextStyle(
color: color ?? gradeColor(context: context, value: value), color: color ?? gradeColor(context: context, value: value),
fontWeight: FontWeight.w800, fontWeight: FontWeight.w800,
fontSize: 32.0, fontSize: 28.0,
), ),
), ),
), ),

View File

@ -22,10 +22,10 @@ class AbsenceSubjectView extends StatelessWidget {
const AbsenceSubjectView(this.subject, {Key? key, this.absences = const []}) const AbsenceSubjectView(this.subject, {Key? key, this.absences = const []})
: super(key: key); : super(key: key);
final Subject subject; final GradeSubject subject;
final List<Absence> absences; final List<Absence> absences;
static void show(Subject subject, List<Absence> absences, static void show(GradeSubject subject, List<Absence> absences,
{required BuildContext context}) { {required BuildContext context}) {
Navigator.of(context, rootNavigator: true) Navigator.of(context, rootNavigator: true)
.push<Absence>(CupertinoPageRoute( .push<Absence>(CupertinoPageRoute(

View File

@ -33,7 +33,7 @@ import 'absences_page.i18n.dart';
enum AbsenceFilter { absences, delays, misses } enum AbsenceFilter { absences, delays, misses }
class SubjectAbsence { class SubjectAbsence {
Subject subject; GradeSubject subject;
List<Absence> absences; List<Absence> absences;
double percentage; double percentage;
@ -58,7 +58,7 @@ class _AbsencesPageState extends State<AbsencesPage>
late String firstName; late String firstName;
late TabController _tabController; late TabController _tabController;
late List<SubjectAbsence> absences = []; late List<SubjectAbsence> absences = [];
final Map<Subject, Lesson> _lessonCount = {}; final Map<GradeSubject, Lesson> _lessonCount = {};
@override @override
void initState() { void initState() {
@ -90,7 +90,7 @@ class _AbsencesPageState extends State<AbsencesPage>
} }
void buildSubjectAbsences() { void buildSubjectAbsences() {
Map<Subject, SubjectAbsence> _absences = {}; Map<GradeSubject, SubjectAbsence> _absences = {};
for (final absence in absenceProvider.absences) { for (final absence in absenceProvider.absences) {
if (absence.delay != 0) continue; if (absence.delay != 0) continue;
@ -286,7 +286,8 @@ class _AbsencesPageState extends State<AbsencesPage>
actions: [ actions: [
ActionButton( ActionButton(
label: "Ok", label: "Ok",
onTap: () => Navigator.of(context).pop()) onTap: () => Navigator.of(context).pop(),
),
], ],
), ),
); );

View File

@ -16,7 +16,7 @@ import 'grade_calculator.i18n.dart';
class GradeCalculator extends StatefulWidget { class GradeCalculator extends StatefulWidget {
const GradeCalculator(this.subject, {Key? key}) : super(key: key); const GradeCalculator(this.subject, {Key? key}) : super(key: key);
final Subject subject; final GradeSubject subject;
@override @override
_GradeCalculatorState createState() => _GradeCalculatorState(); _GradeCalculatorState createState() => _GradeCalculatorState();

View File

@ -7,7 +7,7 @@ import 'grades_page.i18n.dart';
class FailWarning extends StatelessWidget { class FailWarning extends StatelessWidget {
const FailWarning({Key? key, required this.subjectAvgs}) : super(key: key); const FailWarning({Key? key, required this.subjectAvgs}) : super(key: key);
final Map<Subject, double> subjectAvgs; final Map<GradeSubject, double> subjectAvgs;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -42,7 +42,7 @@ class GradeSubjectView extends StatefulWidget {
const GradeSubjectView(this.subject, {Key? key, this.groupAverage = 0.0}) const GradeSubjectView(this.subject, {Key? key, this.groupAverage = 0.0})
: super(key: key); : super(key: key);
final Subject subject; final GradeSubject subject;
final double groupAverage; final double groupAverage;
void push(BuildContext context, {bool root = false}) { void push(BuildContext context, {bool root = false}) {
@ -77,7 +77,7 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
String plan = ''; String plan = '';
List<Grade> getSubjectGrades(Subject subject) => !gradeCalcMode List<Grade> getSubjectGrades(GradeSubject subject) => !gradeCalcMode
? gradeProvider.grades.where((e) => e.subject == subject).toList() ? gradeProvider.grades.where((e) => e.subject == subject).toList()
: calculatorProvider.grades.where((e) => e.subject == subject).toList(); : calculatorProvider.grades.where((e) => e.subject == subject).toList();

View File

@ -48,24 +48,25 @@ class _GradesPageState extends State<GradesPage> {
int avgDropValue = 0; int avgDropValue = 0;
List<Grade> getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider List<Grade> getSubjectGrades(GradeSubject subject, {int days = 0}) =>
.grades gradeProvider.grades
.where((e) => .where((e) =>
e.subject == subject && e.subject == subject &&
e.type == GradeType.midYear && e.type == GradeType.midYear &&
(days == 0 || (days == 0 ||
e.date.isBefore(DateTime.now().subtract(Duration(days: days))))) e.date
.isBefore(DateTime.now().subtract(Duration(days: days)))))
.toList(); .toList();
void generateTiles() { void generateTiles() {
List<Subject> subjects = gradeProvider.grades List<GradeSubject> subjects = gradeProvider.grades
.map((e) => e.subject) .map((e) => e.subject)
.toSet() .toSet()
.toList() .toList()
..sort((a, b) => a.name.compareTo(b.name)); ..sort((a, b) => a.name.compareTo(b.name));
List<Widget> tiles = []; List<Widget> tiles = [];
Map<Subject, double> subjectAvgs = {}; Map<GradeSubject, double> subjectAvgs = {};
tiles.addAll(subjects.map((subject) { tiles.addAll(subjects.map((subject) {
List<Grade> subjectGrades = getSubjectGrades(subject); List<Grade> subjectGrades = getSubjectGrades(subject);
@ -154,7 +155,7 @@ class _GradesPageState extends State<GradesPage> {
title: AutoSizeText( title: AutoSizeText(
"subjectavg".i18n, "subjectavg".i18n,
textAlign: TextAlign.center, textAlign: TextAlign.center,
maxLines: 2, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
value: subjectAvg, value: subjectAvg,
@ -167,7 +168,7 @@ class _GradesPageState extends State<GradesPage> {
title: AutoSizeText( title: AutoSizeText(
"classavg".i18n, "classavg".i18n,
textAlign: TextAlign.center, textAlign: TextAlign.center,
maxLines: 2, maxLines: 1,
wrapWords: false, wrapWords: false,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),

View File

@ -16,7 +16,7 @@ extension Localization on String {
"7_days_average": "Weekly Average", "7_days_average": "Weekly Average",
"subjectavg": "Subject Average", "subjectavg": "Subject Average",
"classavg": "Class Average", "classavg": "Class Average",
"fail_warning": "Faliure warning", "fail_warning": "Failure warning",
"fail_warning_description": "You are failing %d subject(s)", "fail_warning_description": "You are failing %d subject(s)",
}, },
"hu_hu": { "hu_hu": {

View File

@ -172,7 +172,8 @@ class _LiveCardState extends State<LiveCard> {
: ""), : ""),
title: liveCard.currentLesson!.subject.renamedTo ?? title: liveCard.currentLesson!.subject.renamedTo ??
liveCard.currentLesson!.subject.name.capital(), liveCard.currentLesson!.subject.name.capital(),
titleItalic: liveCard.currentLesson!.subject.isRenamed, titleItalic: liveCard.currentLesson!.subject.isRenamed &&
settingsProvider.renamedSubjectsEnabled,
subtitle: liveCard.currentLesson!.room, subtitle: liveCard.currentLesson!.room,
icon: SubjectIcon.resolveVariant( icon: SubjectIcon.resolveVariant(
subject: liveCard.currentLesson!.subject, context: context), subject: liveCard.currentLesson!.subject, context: context),

View File

@ -1,7 +1,9 @@
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_mobile_ui/common/progress_bar.dart'; import 'package:filcnaplo_mobile_ui/common/progress_bar.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'live_card.i18n.dart'; import 'live_card.i18n.dart';
enum ProgressAccuracy { minutes, seconds } enum ProgressAccuracy { minutes, seconds }
@ -67,6 +69,8 @@ class _LiveCardWidgetState extends State<LiveCardWidget> {
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(16.0), borderRadius: BorderRadius.circular(16.0),
boxShadow: [ boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow( BoxShadow(
offset: const Offset(0, 21), offset: const Offset(0, 21),
blurRadius: 23.0, blurRadius: 23.0,

View File

@ -11,8 +11,10 @@ import 'package:filcnaplo_mobile_ui/common/filter_bar.dart';
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_button.dart'; import 'package:filcnaplo_mobile_ui/common/profile_image/profile_button.dart';
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart'; import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
import 'package:filcnaplo/ui/filter/sort.dart'; import 'package:filcnaplo/ui/filter/sort.dart';
import 'package:filcnaplo_mobile_ui/common/soon_alert/soon_alert.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/message/message_viewable.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/message/message_viewable.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'messages_page.i18n.dart'; import 'messages_page.i18n.dart';
@ -61,6 +63,33 @@ class _MessagesPageState extends State<MessagesPage>
centerTitle: false, centerTitle: false,
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor, surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
actions: [ actions: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 5.0),
child: IconButton(
splashRadius: 24.0,
onPressed: () {
// Navigator.of(context, rootNavigator: true)
// .push(PageRouteBuilder(
// pageBuilder: (context, animation, secondaryAnimation) =>
// PremiumFSTimetable(
// controller: controller,
// ),
// ))
// .then((_) {
// SystemChrome.setPreferredOrientations(
// [DeviceOrientation.portraitUp]);
// setSystemChrome(context);
// });
SoonAlert.show(context: context);
},
icon: Icon(
FeatherIcons.send,
color: AppColors.of(context).text,
),
),
),
// Profile Icon // Profile Icon
Padding( Padding(
padding: const EdgeInsets.only(right: 24.0), padding: const EdgeInsets.only(right: 24.0),

View File

@ -20,6 +20,7 @@ import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_i
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart'; import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart'; import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
import 'package:filcnaplo_mobile_ui/common/soon_alert/soon_alert.dart';
import 'package:filcnaplo_mobile_ui/common/system_chrome.dart'; import 'package:filcnaplo_mobile_ui/common/system_chrome.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/update/updates_view.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/update/updates_view.dart';
import 'package:filcnaplo_mobile_ui/screens/news/news_screen.dart'; import 'package:filcnaplo_mobile_ui/screens/news/news_screen.dart';
@ -339,7 +340,7 @@ class _SettingsScreenState extends State<SettingsScreen>
), ),
), ),
// Updates // updates
if (updateProvider.available) if (updateProvider.available)
Padding( Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -377,122 +378,7 @@ class _SettingsScreenState extends State<SettingsScreen>
// child: ActiveSponsorCard(), // child: ActiveSponsorCard(),
// ), // ),
// General Settings // secret settings
Padding(
padding:
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
title: Text("general".i18n),
child: Column(
children: [
PanelButton(
onPressed: () {
SettingsHelper.language(context);
setState(() {});
},
title: Text("language".i18n),
leading: const Icon(FeatherIcons.globe),
trailing: Text(
languageText,
style: const TextStyle(fontSize: 14.0),
),
),
PanelButton(
onPressed: () {
SettingsHelper.startPage(context);
setState(() {});
},
title: Text("startpage".i18n),
leading: const Icon(FeatherIcons.play),
trailing: Text(
startPageTitle.capital(),
style: const TextStyle(fontSize: 14.0),
),
),
PanelButton(
onPressed: () {
SettingsHelper.rounding(context);
setState(() {});
},
title: Text("rounding".i18n),
leading: const Icon(FeatherIcons.gitCommit),
trailing: Text(
(settings.rounding / 10).toStringAsFixed(1),
style: const TextStyle(fontSize: 14.0),
),
),
PanelButton(
onPressed: () {
SettingsHelper.vibrate(context);
setState(() {});
},
title: Text("vibrate".i18n),
leading: const Icon(FeatherIcons.radio),
trailing: Text(
vibrateTitle,
style: const TextStyle(fontSize: 14.0),
),
),
PanelButton(
padding: const EdgeInsets.only(left: 14.0),
onPressed: () {
SettingsHelper.bellDelay(context);
setState(() {});
},
title: Text(
"bell_delay".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(
settings.bellDelayEnabled ? 1.0 : .5)),
),
leading: settings.bellDelayEnabled
? const Icon(FeatherIcons.bell)
: Icon(FeatherIcons.bellOff,
color:
AppColors.of(context).text.withOpacity(.25)),
trailingDivider: true,
trailing: Switch(
onChanged: (v) => settings.update(bellDelayEnabled: v),
value: settings.bellDelayEnabled,
activeColor: Theme.of(context).colorScheme.secondary,
),
),
Material(
type: MaterialType.transparency,
child: MenuNotifications(settings: settings)),
WelcomeMessagePanelButton(settings, user),
],
),
),
),
if (kDebugMode)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 12.0, horizontal: 24.0),
child: Panel(
title: const Text("Debug"),
child: Column(
children: [
PanelButton(
title: const Text("Subject Icon Gallery"),
leading:
const Icon(CupertinoIcons.rectangle_3_offgrid_fill),
trailing: const Icon(Icons.arrow_forward),
onPressed: () {
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute(
builder: (context) =>
const SubjectIconGallery()),
);
},
)
],
),
),
),
// Secret Settings
if (__ss) if (__ss)
Padding( Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -589,7 +475,123 @@ class _SettingsScreenState extends State<SettingsScreen>
), ),
), ),
// Theme Settings // general things
Padding(
padding:
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
title: Text("general".i18n),
child: Column(
children: [
PanelButton(
onPressed: () {
SettingsHelper.language(context);
setState(() {});
},
title: Text("language".i18n),
leading: const Icon(FeatherIcons.globe),
trailing: Text(
languageText,
style: const TextStyle(fontSize: 14.0),
),
),
PanelButton(
onPressed: () {
SettingsHelper.startPage(context);
setState(() {});
},
title: Text("startpage".i18n),
leading: const Icon(FeatherIcons.play),
trailing: Text(
startPageTitle.capital(),
style: const TextStyle(fontSize: 14.0),
),
),
PanelButton(
onPressed: () {
SettingsHelper.rounding(context);
setState(() {});
},
title: Text("rounding".i18n),
leading: const Icon(FeatherIcons.gitCommit),
trailing: Text(
(settings.rounding / 10).toStringAsFixed(1),
style: const TextStyle(fontSize: 14.0),
),
),
PanelButton(
onPressed: () {
SettingsHelper.vibrate(context);
setState(() {});
},
title: Text("vibrate".i18n),
leading: const Icon(FeatherIcons.radio),
trailing: Text(
vibrateTitle,
style: const TextStyle(fontSize: 14.0),
),
),
PanelButton(
padding: const EdgeInsets.only(left: 14.0),
onPressed: () {
SettingsHelper.bellDelay(context);
setState(() {});
},
title: Text(
"bell_delay".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(
settings.bellDelayEnabled ? 1.0 : .5)),
),
leading: settings.bellDelayEnabled
? const Icon(FeatherIcons.bell)
: Icon(FeatherIcons.bellOff,
color:
AppColors.of(context).text.withOpacity(.25)),
trailingDivider: true,
trailing: Switch(
onChanged: (v) => settings.update(bellDelayEnabled: v),
value: settings.bellDelayEnabled,
activeColor: Theme.of(context).colorScheme.secondary,
),
),
Material(
type: MaterialType.transparency,
child: MenuNotifications(settings: settings)),
WelcomeMessagePanelButton(settings, user),
],
),
),
),
// icon gallery (debug mode)
if (kDebugMode)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 12.0, horizontal: 24.0),
child: Panel(
title: const Text("Debug"),
child: Column(
children: [
PanelButton(
title: const Text("Subject Icon Gallery"),
leading:
const Icon(CupertinoIcons.rectangle_3_offgrid_fill),
trailing: const Icon(Icons.arrow_forward),
onPressed: () {
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute(
builder: (context) =>
const SubjectIconGallery()),
);
},
)
],
),
),
),
// appearance things
Padding( Padding(
padding: padding:
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0), const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
@ -684,8 +686,8 @@ class _SettingsScreenState extends State<SettingsScreen>
), ),
), ),
const PremiumIconPackSelector(), const PremiumIconPackSelector(),
// If iOS, show the iOS specific settings
// if ios show live activity color option
if (defaultTargetPlatform == TargetPlatform.iOS) if (defaultTargetPlatform == TargetPlatform.iOS)
PanelButton( PanelButton(
onPressed: () { onPressed: () {
@ -703,12 +705,46 @@ class _SettingsScreenState extends State<SettingsScreen>
), ),
), ),
), ),
Material(
type: MaterialType.transparency,
child: SwitchListTile(
contentPadding: const EdgeInsets.only(left: 14.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: Row(
children: [
Icon(
FeatherIcons.moon,
color: settings.shadowEffect
? Theme.of(context).colorScheme.secondary
: AppColors.of(context).text.withOpacity(.25),
),
const SizedBox(width: 14.0),
Expanded(
child: Text(
"shadow_effect".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
color: AppColors.of(context).text.withOpacity(
settings.shadowEffect ? 1.0 : .5),
),
),
),
],
),
onChanged: (v) => settings.update(shadowEffect: v),
value: settings.shadowEffect,
activeColor: Theme.of(context).colorScheme.secondary,
),
),
], ],
), ),
), ),
), ),
// Notifications // popup alerts
Padding( Padding(
padding: padding:
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0), const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
@ -751,7 +787,7 @@ class _SettingsScreenState extends State<SettingsScreen>
), ),
), ),
// Extras // extra settings
Padding( Padding(
padding: padding:
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0), const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
@ -798,12 +834,23 @@ class _SettingsScreenState extends State<SettingsScreen>
MenuRenamedTeachers( MenuRenamedTeachers(
settings: settings, settings: settings,
), ),
PanelButton(
onPressed: () {
SoonAlert.show(context: context);
},
title: Text('app_icon'.i18n),
leading: const Icon(FeatherIcons.edit),
// trailing: Text(
// 'default'.i18n,
// style: const TextStyle(fontSize: 14.0),
// ),
),
], ],
), ),
), ),
), ),
// About // about sweetie
Padding( Padding(
padding: padding:
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0), const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
@ -954,6 +1001,8 @@ class _SettingsScreenState extends State<SettingsScreen>
), ),
), ),
), ),
// version info
SafeArea( SafeArea(
top: false, top: false,
child: Center( child: Center(

View File

@ -74,8 +74,10 @@ extension SettingsLocalization on String {
"devmode": "Developer Mode", "devmode": "Developer Mode",
"copy_jwt": "Copy JWT", "copy_jwt": "Copy JWT",
"welcome_msg": "Welcome Message", "welcome_msg": "Welcome Message",
"default": "Default", "default": "Dynamic",
"edit_welcome_msg": "Edit welcome message", "edit_welcome_msg": "Edit welcome message",
"shadow_effect": "Shadow Effect",
"app_icon": "App Icon",
}, },
"hu_hu": { "hu_hu": {
"personal_details": "Személyes információk", "personal_details": "Személyes információk",
@ -148,8 +150,10 @@ extension SettingsLocalization on String {
"devmode": "Fejlesztői mód", "devmode": "Fejlesztői mód",
"copy_jwt": "JWT másolása", "copy_jwt": "JWT másolása",
"welcome_msg": "Üdvözlő üzenet", "welcome_msg": "Üdvözlő üzenet",
"default": "Alapértelmezett", "default": "Dinamikus",
"edit_welcome_msg": "Üdvözlő üzenet szerkesztése", "edit_welcome_msg": "Üdvözlő üzenet szerkesztése",
"shadow_effect": "Árnyékhatás",
"app_icon": "Alkalmazásikon",
}, },
"de_de": { "de_de": {
"personal_details": "Persönliche Angaben", "personal_details": "Persönliche Angaben",
@ -198,7 +202,8 @@ extension SettingsLocalization on String {
"graph_class_avg": "Klassendurchschnitt in der Grafik", "graph_class_avg": "Klassendurchschnitt in der Grafik",
"goodstudent": "Guter Student Modus", "goodstudent": "Guter Student Modus",
"attention": "Achtung!", "attention": "Achtung!",
"goodstudent_disclaimer": "reFilc kann nicht für die Nutzung dieser Funktion haftbar gemacht werden.\n\n(Wenn deine Mutter dich verprügelt, weil du ihr falsche Noten gezeigt hast, kannst du dir nur die Schuld dafür geben)", "goodstudent_disclaimer":
"reFilc kann nicht für die Nutzung dieser Funktion haftbar gemacht werden.\n\n(Wenn deine Mutter dich verprügelt, weil du ihr falsche Noten gezeigt hast, kannst du dir nur die Schuld dafür geben)",
"understand": "Ich verstehe", "understand": "Ich verstehe",
"secret": "Geheime Einstellungen", "secret": "Geheime Einstellungen",
"bell_delay": "Klingelverzögerung", "bell_delay": "Klingelverzögerung",
@ -221,8 +226,10 @@ extension SettingsLocalization on String {
"devmode": "Entwicklermodus", "devmode": "Entwicklermodus",
"copy_jwt": "JWT kopieren", "copy_jwt": "JWT kopieren",
"welcome_msg": "Willkommensnachricht", "welcome_msg": "Willkommensnachricht",
"default": "Standard", "default": "Dynamisch",
"edit_welcome_msg": "Begrüßungsnachricht bearbeiten", "edit_welcome_msg": "Begrüßungsnachricht bearbeiten",
"shadow_effect": "Schatteneffekt",
"app_icon": "App-Symbol",
}, },
}; };

View File

@ -30,7 +30,7 @@ class _AllSumBodyState extends State<AllSumBody> {
int avgDropValue = 0; int avgDropValue = 0;
bool animation = false; bool animation = false;
List<Grade> getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider List<Grade> getSubjectGrades(GradeSubject subject, {int days = 0}) => gradeProvider
.grades .grades
.where((e) => .where((e) =>
e.subject == subject && e.subject == subject &&

View File

@ -50,13 +50,14 @@ class _GradesBodyState extends State<GradesBody> {
int avgDropValue = 0; int avgDropValue = 0;
bool animation = false; bool animation = false;
List<Grade> getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider List<Grade> getSubjectGrades(GradeSubject subject, {int days = 0}) =>
.grades gradeProvider.grades
.where((e) => .where((e) =>
e.subject == subject && e.subject == subject &&
e.type == GradeType.midYear && e.type == GradeType.midYear &&
(days == 0 || (days == 0 ||
e.date.isBefore(DateTime.now().subtract(Duration(days: days))))) e.date
.isBefore(DateTime.now().subtract(Duration(days: days)))))
.toList(); .toList();
@override @override
@ -74,18 +75,18 @@ class _GradesBodyState extends State<GradesBody> {
} }
void generateTiles({required int filter}) { void generateTiles({required int filter}) {
List<Subject> subjects = gradeProvider.grades List<GradeSubject> subjects = gradeProvider.grades
.map((e) => e.subject) .map((e) => e.subject)
.toSet() .toSet()
.toList() .toList()
..sort((a, b) => a.name.compareTo(b.name)); ..sort((a, b) => a.name.compareTo(b.name));
List<Widget> tiles = []; List<Widget> tiles = [];
Map<Subject, double> subjectAvgs = {}; Map<GradeSubject, double> subjectAvgs = {};
var count = 1; var count = 1;
for (Subject subject in subjects) { for (GradeSubject subject in subjects) {
List<Grade> subjectGrades = getSubjectGrades(subject); List<Grade> subjectGrades = getSubjectGrades(subject);
double avg = AverageHelper.averageEvals(subjectGrades); double avg = AverageHelper.averageEvals(subjectGrades);

View File

@ -28,7 +28,7 @@ List<String> faces = [
]; ];
class SubjectAbsence { class SubjectAbsence {
Subject subject; GradeSubject subject;
List<Absence> absences; List<Absence> absences;
double percentage; double percentage;
@ -52,7 +52,7 @@ class _LessonsBodyState extends State<LessonsBody> {
late List<SubjectAbsence> absences = []; late List<SubjectAbsence> absences = [];
late List<Widget> lessons = []; late List<Widget> lessons = [];
late List<Absence> delays = []; late List<Absence> delays = [];
final Map<Subject, Lesson> _lessonCount = {}; final Map<GradeSubject, Lesson> _lessonCount = {};
@override @override
void initState() { void initState() {
@ -85,7 +85,7 @@ class _LessonsBodyState extends State<LessonsBody> {
} }
void buildSubjectAbsences() { void buildSubjectAbsences() {
Map<Subject, SubjectAbsence> _absences = {}; Map<GradeSubject, SubjectAbsence> _absences = {};
for (final absence in absenceProvider.absences) { for (final absence in absenceProvider.absences) {
if (absence.delay != 0) continue; if (absence.delay != 0) continue;

View File

@ -1,48 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
pubspec.lock
android/local.properties

View File

@ -9,10 +9,10 @@ class GoalProvider extends ChangeNotifier {
final UserProvider _user; final UserProvider _user;
late bool _done = false; late bool _done = false;
late Subject? _doneSubject; late GradeSubject? _doneSubject;
bool get hasDoneGoals => _done; bool get hasDoneGoals => _done;
Subject? get doneSubject => _doneSubject; GradeSubject? get doneSubject => _doneSubject;
GoalProvider({ GoalProvider({
required DatabaseProvider database, required DatabaseProvider database,
@ -24,7 +24,7 @@ class GoalProvider extends ChangeNotifier {
var goalAvgs = await _db.userQuery.subjectGoalAverages(userId: _user.id!); var goalAvgs = await _db.userQuery.subjectGoalAverages(userId: _user.id!);
var beforeAvgs = await _db.userQuery.subjectGoalBefores(userId: _user.id!); var beforeAvgs = await _db.userQuery.subjectGoalBefores(userId: _user.id!);
List<Subject> subjects = gradeProvider.grades List<GradeSubject> subjects = gradeProvider.grades
.map((e) => e.subject) .map((e) => e.subject)
.toSet() .toSet()
.toList() .toList()
@ -45,7 +45,7 @@ class GoalProvider extends ChangeNotifier {
_doneSubject = null; _doneSubject = null;
} }
Future<void> clearGoal(Subject subject) async { Future<void> clearGoal(GradeSubject subject) async {
final goalPlans = await _db.userQuery.subjectGoalPlans(userId: _user.id!); final goalPlans = await _db.userQuery.subjectGoalPlans(userId: _user.id!);
final goalAvgs = await _db.userQuery.subjectGoalAverages(userId: _user.id!); final goalAvgs = await _db.userQuery.subjectGoalAverages(userId: _user.id!);
final goalBeforeGrades = final goalBeforeGrades =

View File

@ -40,6 +40,11 @@ class ShareProvider extends ChangeNotifier {
SettingsProvider.defaultSettings().customAccentColor) SettingsProvider.defaultSettings().customAccentColor)
?.value ?? ?.value ??
const Color(0xFF3D7BF4).value, const Color(0xFF3D7BF4).value,
'icon_color': (settings.customIconColor ??
SettingsProvider.defaultSettings().customIconColor)
?.value ??
const Color(0x00000000).value,
'shadow_effect': settings.shadowEffect,
}; };
SharedTheme theme = SharedTheme.fromJson(themeJson, gradeColors); SharedTheme theme = SharedTheme.fromJson(themeJson, gradeColors);

View File

@ -262,6 +262,7 @@ class _FilcColorPickerState extends State<FilcColorPicker> {
if (theme != null) { if (theme != null) {
widget.onThemeIdProvided(theme); widget.onThemeIdProvided(theme);
idController.clear();
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
CustomSnackBar( CustomSnackBar(

View File

@ -20,7 +20,7 @@ class GoalCompleteModal extends StatelessWidget {
final UserProvider user; final UserProvider user;
final DatabaseProvider database; final DatabaseProvider database;
final Subject subject; final GradeSubject subject;
final double goalAverage; final double goalAverage;
final double beforeAverage; final double beforeAverage;
@ -218,7 +218,7 @@ class GoalCompleteModal extends StatelessWidget {
} }
static Future<T?> show<T>( static Future<T?> show<T>(
Subject subject, { GradeSubject subject, {
required BuildContext context, required BuildContext context,
}) async { }) async {
UserProvider user = Provider.of<UserProvider>(context, listen: false); UserProvider user = Provider.of<UserProvider>(context, listen: false);

View File

@ -64,7 +64,7 @@ class GoalPlanner {
form: '', form: '',
groupId: '', groupId: '',
type: GradeType.midYear, type: GradeType.midYear,
subject: Subject.fromJson({}), subject: GradeSubject.fromJson({}),
mode: Category.fromJson({}), mode: Category.fromJson({}),
seenDate: DateTime(0), seenDate: DateTime(0),
writeDate: DateTime(0), writeDate: DateTime(0),

View File

@ -25,7 +25,7 @@ enum PlanResult {
} }
class GoalPlannerScreen extends StatefulWidget { class GoalPlannerScreen extends StatefulWidget {
final Subject subject; final GradeSubject subject;
const GoalPlannerScreen({Key? key, required this.subject}) : super(key: key); const GoalPlannerScreen({Key? key, required this.subject}) : super(key: key);
@ -42,7 +42,7 @@ class _GoalPlannerScreenState extends State<GoalPlannerScreen> {
bool gradeCalcMode = false; bool gradeCalcMode = false;
List<Grade> getSubjectGrades(Subject subject) => !gradeCalcMode List<Grade> getSubjectGrades(GradeSubject subject) => !gradeCalcMode
? gradeProvider.grades.where((e) => e.subject == subject).toList() ? gradeProvider.grades.where((e) => e.subject == subject).toList()
: calculatorProvider.grades.where((e) => e.subject == subject).toList(); : calculatorProvider.grades.where((e) => e.subject == subject).toList();

View File

@ -6,10 +6,12 @@ import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart'; import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo_kreta_api/models/subject.dart'; import 'package:filcnaplo_kreta_api/models/subject.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:filcnaplo_mobile_ui/common/action_button.dart';
import 'package:filcnaplo_mobile_ui/common/average_display.dart'; import 'package:filcnaplo_mobile_ui/common/average_display.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart'; import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
import 'package:filcnaplo_mobile_ui/common/progress_bar.dart'; import 'package:filcnaplo_mobile_ui/common/progress_bar.dart';
import 'package:filcnaplo_mobile_ui/common/round_border_icon.dart'; import 'package:filcnaplo_mobile_ui/common/round_border_icon.dart';
import 'package:filcnaplo_premium/providers/goal_provider.dart';
import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner.dart'; import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_planner.dart';
import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_state_screen.i18n.dart'; import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_state_screen.i18n.dart';
import 'package:filcnaplo_premium/ui/mobile/goal_planner/route_option.dart'; import 'package:filcnaplo_premium/ui/mobile/goal_planner/route_option.dart';
@ -22,7 +24,7 @@ import 'goal_planner_screen.dart';
import 'graph.dart'; import 'graph.dart';
class GoalStateScreen extends StatefulWidget { class GoalStateScreen extends StatefulWidget {
final Subject subject; final GradeSubject subject;
const GoalStateScreen({Key? key, required this.subject}) : super(key: key); const GoalStateScreen({Key? key, required this.subject}) : super(key: key);
@ -77,10 +79,10 @@ class _GoalStateScreenState extends State<GoalStateScreen> {
setState(() {}); setState(() {});
} }
List<Grade> getSubjectGrades(Subject subject) => List<Grade> getSubjectGrades(GradeSubject subject) =>
gradeProvider.grades.where((e) => (e.subject == subject)).toList(); gradeProvider.grades.where((e) => (e.subject == subject)).toList();
List<Grade> getAfterGoalGrades(Subject subject) => gradeProvider.grades List<Grade> getAfterGoalGrades(GradeSubject subject) => gradeProvider.grades
.where((e) => (e.subject == subject && e.date.isAfter(goalPinDate))) .where((e) => (e.subject == subject && e.date.isAfter(goalPinDate)))
.toList(); .toList();
@ -199,11 +201,39 @@ class _GoalStateScreenState extends State<GoalStateScreen> {
), ),
child: Column( child: Column(
children: [ children: [
const Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
BackButton(), const BackButton(),
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: Text("attention".i18n),
content: Text("attention_body".i18n),
actions: [
ActionButton(
label: "delete".i18n,
onTap: () async {
// clear the goal
await Provider.of<GoalProvider>(context,
listen: false)
.clearGoal(widget.subject);
// close the modal and the goal page
Navigator.of(context).pop();
Navigator.of(context).pop();
},
),
],
),
);
},
icon: const Icon(FeatherIcons.x),
),
], ],
), ),
const SizedBox(height: 22.0), const SizedBox(height: 22.0),

View File

@ -22,6 +22,11 @@ extension Localization on String {
"improved_by": "and improved your grade by %s", "improved_by": "and improved your grade by %s",
"detailed_stats": "See my detailed stats", "detailed_stats": "See my detailed stats",
"later": "Yay! I'll see my stats later.", "later": "Yay! I'll see my stats later.",
// sure delete modal
"delete": "Delete",
"attention": "Attention!",
"attention_body":
"Your goal and progress will be lost forever and cannot be restored.",
}, },
"hu_hu": { "hu_hu": {
// base page // base page
@ -42,6 +47,11 @@ extension Localization on String {
"improved_by": "%s-os javulást értél el!", "improved_by": "%s-os javulást értél el!",
"detailed_stats": "Részletes statisztikám", "detailed_stats": "Részletes statisztikám",
"later": "Hurrá! Megnézem máskor.", "later": "Hurrá! Megnézem máskor.",
// sure delete modal
"delete": "Törlés",
"attention": "Figyelem!",
"attention_body":
"A kitűzött célod és haladásod örökre elveszik és nem lesz visszaállítható.",
}, },
"de_de": { "de_de": {
// base page // base page
@ -62,6 +72,11 @@ extension Localization on String {
"improved_by": "Sie haben %s Verbesserung erreicht!", "improved_by": "Sie haben %s Verbesserung erreicht!",
"detailed_stats": "Detaillierte Statistiken", "detailed_stats": "Detaillierte Statistiken",
"later": "Hurra! Ich schaue später nach.", "later": "Hurra! Ich schaue später nach.",
// sure delete modal
"delete": "Löschen",
"attention": "Achtung!",
"attention_body":
"Ihr Ziel und Ihr Fortschritt gehen für immer verloren und können nicht wiederhergestellt werden.",
}, },
}; };

View File

@ -91,7 +91,7 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
final _subjectName = TextEditingController(); final _subjectName = TextEditingController();
String? selectedSubjectId; String? selectedSubjectId;
late List<Subject> subjects; late List<GradeSubject> subjects;
late UserProvider user; late UserProvider user;
late DatabaseProvider dbProvider; late DatabaseProvider dbProvider;
late SettingsProvider settings; late SettingsProvider settings;
@ -295,8 +295,10 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
Panel( Panel(
child: SwitchListTile( child: SwitchListTile(
title: Text("italics_toggle".i18n), title: Text("italics_toggle".i18n),
onChanged: (value) => settings.update(renamedSubjectsItalics: value), onChanged: (value) =>
value: settings.renamedSubjectsItalics,), settings.update(renamedSubjectsItalics: value),
value: settings.renamedSubjectsItalics,
),
), ),
const SizedBox( const SizedBox(
height: 20, height: 20,
@ -339,7 +341,7 @@ class _ModifySubjectNamesState extends State<ModifySubjectNames> {
child: Column( child: Column(
children: snapshot.data!.keys.map( children: snapshot.data!.keys.map(
(key) { (key) {
Subject? subject = subjects GradeSubject? subject = subjects
.firstWhere((element) => key == element.id); .firstWhere((element) => key == element.id);
String renameTo = snapshot.data![key]!; String renameTo = snapshot.data![key]!;
return RenamedSubjectItem( return RenamedSubjectItem(
@ -385,7 +387,7 @@ class RenamedSubjectItem extends StatelessWidget {
required this.removeCallback, required this.removeCallback,
}) : super(key: key); }) : super(key: key);
final Subject subject; final GradeSubject subject;
final String renamedTo; final String renamedTo;
final void Function() modifyCallback; final void Function() modifyCallback;
final void Function() removeCallback; final void Function() removeCallback;

View File

@ -39,6 +39,7 @@ enum CustomColorMode {
accent, accent,
background, background,
highlight, highlight,
icon,
enterId, enterId,
} }
@ -146,6 +147,8 @@ class _PremiumCustomAccentColorSettingState
return settings.customHighlightColor; return settings.customHighlightColor;
case CustomColorMode.accent: case CustomColorMode.accent:
return settings.customAccentColor; return settings.customAccentColor;
case CustomColorMode.icon:
return settings.customIconColor;
case CustomColorMode.enterId: case CustomColorMode.enterId:
// do nothing here lol // do nothing here lol
break; break;
@ -153,7 +156,7 @@ class _PremiumCustomAccentColorSettingState
} }
void updateCustomColor(dynamic v, bool store, void updateCustomColor(dynamic v, bool store,
{Color? accent, Color? background, Color? panels}) { {Color? accent, Color? background, Color? panels, Color? icon}) {
if (colorMode != CustomColorMode.theme) { if (colorMode != CustomColorMode.theme) {
settings.update(accentColor: AccentColor.custom, store: store); settings.update(accentColor: AccentColor.custom, store: store);
} }
@ -186,10 +189,14 @@ class _PremiumCustomAccentColorSettingState
case CustomColorMode.accent: case CustomColorMode.accent:
settings.update(customAccentColor: v, store: store); settings.update(customAccentColor: v, store: store);
break; break;
case CustomColorMode.icon:
settings.update(customIconColor: v, store: store);
break;
case CustomColorMode.enterId: case CustomColorMode.enterId:
settings.update(customBackgroundColor: background, store: store); settings.update(customBackgroundColor: background, store: store);
settings.update(customHighlightColor: panels, store: store); settings.update(customHighlightColor: panels, store: store);
settings.update(customAccentColor: accent, store: store); settings.update(customAccentColor: accent, store: store);
settings.update(customIconColor: icon, store: store);
break; break;
} }
} }
@ -329,7 +336,8 @@ class _PremiumCustomAccentColorSettingState
width: double.infinity, width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
gradient: LinearGradient( // https://discord.com/channels/1111649116020285532/1153619667848548452 gradient: LinearGradient(
// https://discord.com/channels/1111649116020285532/1153619667848548452
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
stops: const [ stops: const [
@ -337,8 +345,8 @@ class _PremiumCustomAccentColorSettingState
0.75 0.75
], ],
colors: [ colors: [
settings.customBackgroundColor settings.customBackgroundColor ??
?? Theme.of(context).colorScheme.background, Theme.of(context).colorScheme.background,
isBackgroundDifferent isBackgroundDifferent
? HSVColor.fromColor(Theme.of(context) ? HSVColor.fromColor(Theme.of(context)
.colorScheme .colorScheme
@ -704,6 +712,13 @@ class _PremiumCustomAccentColorSettingState
tab: Tab( tab: Tab(
text: "colorpicker_accent" text: "colorpicker_accent"
.i18n)), .i18n)),
// ColorTab(
// unlocked: hasAccess,
// color: settings.customIconColor ??
// unknownColor,
// tab: Tab(
// text:
// "colorpicker_icon".i18n)),
], ],
onTap: (index) { onTap: (index) {
if (!hasAccess) { if (!hasAccess) {
@ -754,6 +769,12 @@ class _PremiumCustomAccentColorSettingState
CustomColorMode.accent; CustomColorMode.accent;
}); });
break; break;
case 5:
setState(() {
colorMode =
CustomColorMode.icon;
});
break;
} }
}, },
controller: _colorsTabController, controller: _colorsTabController,
@ -781,8 +802,14 @@ class _PremiumCustomAccentColorSettingState
.accentColor] ?? .accentColor] ??
AppColors.of(context) AppColors.of(context)
.text) // idk what else .text) // idk what else
: settings : colorMode ==
CustomColorMode
.highlight
? settings
.customHighlightColor ?? .customHighlightColor ??
unknownColor
: settings
.customIconColor ??
unknownColor, unknownColor,
onColorChanged: (c) { onColorChanged: (c) {
setState(() { setState(() {
@ -806,6 +833,10 @@ class _PremiumCustomAccentColorSettingState
AppColors.of(context) AppColors.of(context)
.highlight, .highlight,
store: true); store: true);
settings.update(
customIconColor:
const Color(0x00000000),
store: true);
} else { } else {
updateCustomColor(c, true); updateCustomColor(c, true);
} }
@ -824,6 +855,11 @@ class _PremiumCustomAccentColorSettingState
settings.update( settings.update(
gradeColors: colors); gradeColors: colors);
// changing shadow effect
settings.update(
shadowEffect:
theme.shadowEffect);
// changing theme // changing theme
setState(() { setState(() {
updateCustomColor( updateCustomColor(
@ -833,6 +869,7 @@ class _PremiumCustomAccentColorSettingState
background: background:
theme.backgroundColor, theme.backgroundColor,
panels: theme.panelsColor, panels: theme.panelsColor,
icon: theme.iconColor,
); );
}); });
setTheme(settings.theme, true); setTheme(settings.theme, true);

View File

@ -9,6 +9,7 @@ extension SettingsLocalization on String {
"colorpicker_background": "Background", "colorpicker_background": "Background",
"colorpicker_panels": "Panels", "colorpicker_panels": "Panels",
"colorpicker_accent": "Accent", "colorpicker_accent": "Accent",
"colorpicker_icon": "Icon",
"need_sub": "You need Kupak subscription to use modify this.", "need_sub": "You need Kupak subscription to use modify this.",
"advanced": "Advanced", "advanced": "Advanced",
"enter_id": "Enter ID", "enter_id": "Enter ID",
@ -18,6 +19,7 @@ extension SettingsLocalization on String {
"share_disclaimer": "share_disclaimer":
"By sharing the theme, you agree that the nickname you set and all settings of the theme will be shared publicly.", "By sharing the theme, you agree that the nickname you set and all settings of the theme will be shared publicly.",
"understand": "I understand", "understand": "I understand",
"share_subj_theme": "Share Theme",
}, },
"hu_hu": { "hu_hu": {
"theme_prev": "Előnézet", "theme_prev": "Előnézet",
@ -25,6 +27,7 @@ extension SettingsLocalization on String {
"colorpicker_background": "Háttér", "colorpicker_background": "Háttér",
"colorpicker_panels": "Panelek", "colorpicker_panels": "Panelek",
"colorpicker_accent": "Színtónus", "colorpicker_accent": "Színtónus",
"colorpicker_icon": "Ikon",
"need_sub": "A módosításhoz Kupak szintű támogatás szükséges.", "need_sub": "A módosításhoz Kupak szintű támogatás szükséges.",
"advanced": "Haladó", "advanced": "Haladó",
"enter_id": "ID megadása", "enter_id": "ID megadása",
@ -34,6 +37,7 @@ extension SettingsLocalization on String {
"share_disclaimer": "share_disclaimer":
"A téma megosztásával elfogadod, hogy az általad beállított becenév és a téma minden beállítása nyilvánosan megosztásra kerüljön.", "A téma megosztásával elfogadod, hogy az általad beállított becenév és a téma minden beállítása nyilvánosan megosztásra kerüljön.",
"understand": "Értem", "understand": "Értem",
"share_subj_theme": "Téma Megosztás",
}, },
"de_de": { "de_de": {
"theme_prev": "Vorschau", "theme_prev": "Vorschau",
@ -41,6 +45,7 @@ extension SettingsLocalization on String {
"colorpicker_background": "Hintergrund", "colorpicker_background": "Hintergrund",
"colorpicker_panels": "Tafeln", "colorpicker_panels": "Tafeln",
"colorpicker_accent": "Akzent", "colorpicker_accent": "Akzent",
"colorpicker_icon": "Ikone",
"need_sub": "need_sub":
"Sie benötigen ein Kupak-Abonnement, um diese Funktion zu ändern.", "Sie benötigen ein Kupak-Abonnement, um diese Funktion zu ändern.",
"advanced": "Fortschrittlich", "advanced": "Fortschrittlich",
@ -51,6 +56,7 @@ extension SettingsLocalization on String {
"share_disclaimer": "share_disclaimer":
"Durch das Teilen des Themes erklären Sie sich damit einverstanden, dass der von Ihnen festgelegte Spitzname und alle Einstellungen des Themes öffentlich geteilt werden.", "Durch das Teilen des Themes erklären Sie sich damit einverstanden, dass der von Ihnen festgelegte Spitzname und alle Einstellungen des Themes öffentlich geteilt werden.",
"understand": "Ich verstehe", "understand": "Ich verstehe",
"share_subj_theme": "Thema Teilen",
}, },
}; };