forked from firka/student-legacy
Merge branch 'dev' of github.com:refilc/naplo into dev
This commit is contained in:
commit
856b48675b
@ -12,7 +12,7 @@
|
|||||||
### Clone the project
|
### Clone the project
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone --recursive https://github.com/refilc/naplo
|
git clone https://github.com/refilc/naplo
|
||||||
cd naplo
|
cd naplo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
// ignore_for_file: use_build_context_synchronously
|
// ignore_for_file: use_build_context_synchronously
|
||||||
|
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:filcnaplo/api/client.dart';
|
import 'package:filcnaplo/api/client.dart';
|
||||||
import 'package:filcnaplo/models/news.dart';
|
import 'package:filcnaplo/models/news.dart';
|
||||||
import 'package:filcnaplo/models/settings.dart';
|
import 'package:filcnaplo/models/settings.dart';
|
||||||
@ -11,7 +8,7 @@ import 'package:provider/provider.dart';
|
|||||||
class NewsProvider extends ChangeNotifier {
|
class NewsProvider extends ChangeNotifier {
|
||||||
// Private
|
// Private
|
||||||
late List<News> _news;
|
late List<News> _news;
|
||||||
late int _state;
|
//late int _state;
|
||||||
late int _fresh;
|
late int _fresh;
|
||||||
bool show = false;
|
bool show = false;
|
||||||
late BuildContext _context;
|
late BuildContext _context;
|
||||||
@ -30,56 +27,83 @@ class NewsProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
Future<void> restore() async {
|
Future<void> restore() async {
|
||||||
// Load news state from the database
|
// Load news state from the database
|
||||||
var state_ = Provider.of<SettingsProvider>(_context, listen: false).newsState;
|
var seen_ = Provider.of<SettingsProvider>(_context, listen: false).seenNews;
|
||||||
|
|
||||||
if (state_ == -1) {
|
if (seen_.isEmpty) {
|
||||||
var news_ = await FilcAPI.getNews();
|
var news_ = await FilcAPI.getNews();
|
||||||
if (news_ != null) {
|
if (news_ != null) {
|
||||||
state_ = news_.length;
|
|
||||||
_news = news_;
|
_news = news_;
|
||||||
|
show = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_state = state_;
|
//_state = seen_;
|
||||||
Provider.of<SettingsProvider>(_context, listen: false).update(newsState: _state);
|
// Provider.of<SettingsProvider>(_context, listen: false)
|
||||||
|
// .update(seenNewsId: news_.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetch() async {
|
Future<void> fetch() async {
|
||||||
var news_ = await FilcAPI.getNews();
|
var news_ = await FilcAPI.getNews();
|
||||||
if (news_ == null) return;
|
if (news_ == null) return;
|
||||||
|
|
||||||
|
show = false;
|
||||||
|
|
||||||
_news = news_;
|
_news = news_;
|
||||||
_fresh = news_.length - _state;
|
|
||||||
|
|
||||||
if (_fresh < 0) {
|
for (var news in news_) {
|
||||||
_state = news_.length;
|
if (news.expireDate.isAfter(DateTime.now()) &&
|
||||||
Provider.of<SettingsProvider>(_context, listen: false).update(newsState: _state);
|
Provider.of<SettingsProvider>(_context, listen: false)
|
||||||
}
|
.seenNews
|
||||||
|
.contains(news.id) ==
|
||||||
_fresh = max(_fresh, 0);
|
false) {
|
||||||
|
|
||||||
if (_fresh > 0) {
|
|
||||||
show = true;
|
show = true;
|
||||||
|
Provider.of<SettingsProvider>(_context, listen: false)
|
||||||
|
.update(seenNewsId: news.id);
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// print(news_.length);
|
||||||
|
// print(_state);
|
||||||
|
|
||||||
|
// _news = news_;
|
||||||
|
// _fresh = news_.length - _state;
|
||||||
|
|
||||||
|
// if (_fresh < 0) {
|
||||||
|
// _state = news_.length;
|
||||||
|
// Provider.of<SettingsProvider>(_context, listen: false)
|
||||||
|
// .update(newsState: _state);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// _fresh = max(_fresh, 0);
|
||||||
|
|
||||||
|
// if (_fresh > 0) {
|
||||||
|
// show = true;
|
||||||
|
// notifyListeners();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// print(_fresh);
|
||||||
|
// print(_state);
|
||||||
|
// print(show);
|
||||||
|
}
|
||||||
|
|
||||||
void lock() => show = false;
|
void lock() => show = false;
|
||||||
|
|
||||||
void release() {
|
void release() {
|
||||||
if (_fresh == 0) return;
|
// if (_fresh == 0) return;
|
||||||
|
|
||||||
_fresh--;
|
// _fresh--;
|
||||||
_state++;
|
// //_state++;
|
||||||
|
|
||||||
Provider.of<SettingsProvider>(_context, listen: false).update(newsState: _state);
|
// // Provider.of<SettingsProvider>(_context, listen: false)
|
||||||
|
// // .update(seenNewsId: _state);
|
||||||
|
|
||||||
if (_fresh > 0) {
|
// if (_fresh > 0) {
|
||||||
show = true;
|
// show = true;
|
||||||
} else {
|
// } else {
|
||||||
show = false;
|
// show = false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
notifyListeners();
|
// notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,8 @@ import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';
|
|||||||
|
|
||||||
const settingsDB = DatabaseStruct("settings", {
|
const settingsDB = DatabaseStruct("settings", {
|
||||||
"language": String, "start_page": int, "rounding": int, "theme": int,
|
"language": String, "start_page": int, "rounding": int, "theme": int,
|
||||||
"accent_color": int, "news": int, "news_state": int, "developer_mode": int,
|
"accent_color": int, "news": int, "seen_news": String,
|
||||||
|
"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, // general
|
||||||
"grade_color1": int, "grade_color2": int, "grade_color3": int,
|
"grade_color1": int, "grade_color2": int, "grade_color3": int,
|
||||||
@ -25,7 +26,8 @@ const settingsDB = DatabaseStruct("settings", {
|
|||||||
"grade_opening_fun": int, "icon_pack": String, "premium_scopes": String,
|
"grade_opening_fun": int, "icon_pack": String, "premium_scopes": String,
|
||||||
"premium_token": String, "premium_login": String,
|
"premium_token": String, "premium_login": String,
|
||||||
"last_account_id": String, "renamed_subjects_enabled": int,
|
"last_account_id": String, "renamed_subjects_enabled": int,
|
||||||
"renamed_subjects_italics": int,
|
"renamed_subjects_italics": int, "renamed_teachers_enabled": int,
|
||||||
|
"renamed_teachers_italics": int,
|
||||||
});
|
});
|
||||||
// DON'T FORGET TO UPDATE DEFAULT VALUES IN `initDB` MIGRATION OR ELSE PARENTS WILL COMPLAIN ABOUT THEIR CHILDREN MISSING
|
// DON'T FORGET TO UPDATE DEFAULT VALUES IN `initDB` MIGRATION OR ELSE PARENTS WILL COMPLAIN ABOUT THEIR CHILDREN MISSING
|
||||||
// YOU'VE BEEN WARNED!!!
|
// YOU'VE BEEN WARNED!!!
|
||||||
@ -40,6 +42,8 @@ const userDataDB = DatabaseStruct("user_data", {
|
|||||||
"events": String, "absences": String, "group_averages": String,
|
"events": String, "absences": String, "group_averages": String,
|
||||||
// renamed subjects // non kreta data
|
// renamed subjects // non kreta data
|
||||||
"renamed_subjects": String,
|
"renamed_subjects": String,
|
||||||
|
// renamed teachers // non kreta data
|
||||||
|
"renamed_teachers": String,
|
||||||
// "subject_lesson_count": String, // non kreta data
|
// "subject_lesson_count": String, // non kreta data
|
||||||
"last_seen_grade": int,
|
"last_seen_grade": int,
|
||||||
});
|
});
|
||||||
@ -89,6 +93,8 @@ Future<Database> initDB(DatabaseProvider database) async {
|
|||||||
"group_averages": "[]",
|
"group_averages": "[]",
|
||||||
// renamed subjects // non kreta data
|
// renamed subjects // non kreta data
|
||||||
"renamed_subjects": "{}",
|
"renamed_subjects": "{}",
|
||||||
|
// renamed teachers // non kreta data
|
||||||
|
"renamed_teachers": "{}",
|
||||||
// "subject_lesson_count": "{}", // non kreta data
|
// "subject_lesson_count": "{}", // non kreta data
|
||||||
"last_seen_grade": 0,
|
"last_seen_grade": 0,
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,8 @@ class DatabaseQuery {
|
|||||||
|
|
||||||
Future<SettingsProvider> getSettings(DatabaseProvider database) async {
|
Future<SettingsProvider> getSettings(DatabaseProvider database) async {
|
||||||
Map settingsMap = (await db.query("settings")).elementAt(0);
|
Map settingsMap = (await db.query("settings")).elementAt(0);
|
||||||
SettingsProvider settings = SettingsProvider.fromMap(settingsMap, database: database);
|
SettingsProvider settings =
|
||||||
|
SettingsProvider.fromMap(settingsMap, database: database);
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +37,10 @@ class DatabaseQuery {
|
|||||||
for (var user in usersMap) {
|
for (var user in usersMap) {
|
||||||
userProvider.addUser(User.fromMap(user));
|
userProvider.addUser(User.fromMap(user));
|
||||||
}
|
}
|
||||||
if (userProvider.getUsers().map((e) => e.id).contains(settings.lastAccountId)) {
|
if (userProvider
|
||||||
|
.getUsers()
|
||||||
|
.map((e) => e.id)
|
||||||
|
.contains(settings.lastAccountId)) {
|
||||||
userProvider.setUser(settings.lastAccountId);
|
userProvider.setUser(settings.lastAccountId);
|
||||||
} else {
|
} else {
|
||||||
if (usersMap.isNotEmpty) {
|
if (usersMap.isNotEmpty) {
|
||||||
@ -54,100 +58,133 @@ class UserDatabaseQuery {
|
|||||||
final Database db;
|
final Database db;
|
||||||
|
|
||||||
Future<List<Grade>> getGrades({required String userId}) async {
|
Future<List<Grade>> getGrades({required String userId}) async {
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return [];
|
if (userData.isEmpty) return [];
|
||||||
String? gradesJson = userData.elementAt(0)["grades"] as String?;
|
String? gradesJson = userData.elementAt(0)["grades"] as String?;
|
||||||
if (gradesJson == null) return [];
|
if (gradesJson == null) return [];
|
||||||
List<Grade> grades = (jsonDecode(gradesJson) as List).map((e) => Grade.fromJson(e)).toList();
|
List<Grade> grades =
|
||||||
|
(jsonDecode(gradesJson) as List).map((e) => Grade.fromJson(e)).toList();
|
||||||
return grades;
|
return grades;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<Week, List<Lesson>>> getLessons({required String userId}) async {
|
Future<Map<Week, List<Lesson>>> getLessons({required String userId}) async {
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return {};
|
if (userData.isEmpty) return {};
|
||||||
String? lessonsJson = userData.elementAt(0)["timetable"] as String?;
|
String? lessonsJson = userData.elementAt(0)["timetable"] as String?;
|
||||||
if (lessonsJson == null) return {};
|
if (lessonsJson == null) return {};
|
||||||
if (jsonDecode(lessonsJson) is List) return {};
|
if (jsonDecode(lessonsJson) is List) return {};
|
||||||
Map<Week, List<Lesson>> lessons = (jsonDecode(lessonsJson) as Map).cast<String, List>().map((key, value) {
|
Map<Week, List<Lesson>> lessons =
|
||||||
return MapEntry(Week.fromId(int.parse(key)), value.cast<Map<String, Object?>>().map((e) => Lesson.fromJson(e)).toList());
|
(jsonDecode(lessonsJson) as Map).cast<String, List>().map((key, value) {
|
||||||
|
return MapEntry(
|
||||||
|
Week.fromId(int.parse(key)),
|
||||||
|
value
|
||||||
|
.cast<Map<String, Object?>>()
|
||||||
|
.map((e) => Lesson.fromJson(e))
|
||||||
|
.toList());
|
||||||
}).cast();
|
}).cast();
|
||||||
return lessons;
|
return lessons;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Exam>> getExams({required String userId}) async {
|
Future<List<Exam>> getExams({required String userId}) async {
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return [];
|
if (userData.isEmpty) return [];
|
||||||
String? examsJson = userData.elementAt(0)["exams"] as String?;
|
String? examsJson = userData.elementAt(0)["exams"] as String?;
|
||||||
if (examsJson == null) return [];
|
if (examsJson == null) return [];
|
||||||
List<Exam> exams = (jsonDecode(examsJson) as List).map((e) => Exam.fromJson(e)).toList();
|
List<Exam> exams =
|
||||||
|
(jsonDecode(examsJson) as List).map((e) => Exam.fromJson(e)).toList();
|
||||||
return exams;
|
return exams;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Homework>> getHomework({required String userId}) async {
|
Future<List<Homework>> getHomework({required String userId}) async {
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return [];
|
if (userData.isEmpty) return [];
|
||||||
String? homeworkJson = userData.elementAt(0)["homework"] as String?;
|
String? homeworkJson = userData.elementAt(0)["homework"] as String?;
|
||||||
if (homeworkJson == null) return [];
|
if (homeworkJson == null) return [];
|
||||||
List<Homework> homework = (jsonDecode(homeworkJson) as List).map((e) => Homework.fromJson(e)).toList();
|
List<Homework> homework = (jsonDecode(homeworkJson) as List)
|
||||||
|
.map((e) => Homework.fromJson(e))
|
||||||
|
.toList();
|
||||||
return homework;
|
return homework;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Message>> getMessages({required String userId}) async {
|
Future<List<Message>> getMessages({required String userId}) async {
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return [];
|
if (userData.isEmpty) return [];
|
||||||
String? messagesJson = userData.elementAt(0)["messages"] as String?;
|
String? messagesJson = userData.elementAt(0)["messages"] as String?;
|
||||||
if (messagesJson == null) return [];
|
if (messagesJson == null) return [];
|
||||||
List<Message> messages = (jsonDecode(messagesJson) as List).map((e) => Message.fromJson(e)).toList();
|
List<Message> messages = (jsonDecode(messagesJson) as List)
|
||||||
|
.map((e) => Message.fromJson(e))
|
||||||
|
.toList();
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Note>> getNotes({required String userId}) async {
|
Future<List<Note>> getNotes({required String userId}) async {
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return [];
|
if (userData.isEmpty) return [];
|
||||||
String? notesJson = userData.elementAt(0)["notes"] as String?;
|
String? notesJson = userData.elementAt(0)["notes"] as String?;
|
||||||
if (notesJson == null) return [];
|
if (notesJson == null) return [];
|
||||||
List<Note> notes = (jsonDecode(notesJson) as List).map((e) => Note.fromJson(e)).toList();
|
List<Note> notes =
|
||||||
|
(jsonDecode(notesJson) as List).map((e) => Note.fromJson(e)).toList();
|
||||||
return notes;
|
return notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Event>> getEvents({required String userId}) async {
|
Future<List<Event>> getEvents({required String userId}) async {
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return [];
|
if (userData.isEmpty) return [];
|
||||||
String? eventsJson = userData.elementAt(0)["events"] as String?;
|
String? eventsJson = userData.elementAt(0)["events"] as String?;
|
||||||
if (eventsJson == null) return [];
|
if (eventsJson == null) return [];
|
||||||
List<Event> events = (jsonDecode(eventsJson) as List).map((e) => Event.fromJson(e)).toList();
|
List<Event> events =
|
||||||
|
(jsonDecode(eventsJson) as List).map((e) => Event.fromJson(e)).toList();
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Absence>> getAbsences({required String userId}) async {
|
Future<List<Absence>> getAbsences({required String userId}) async {
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return [];
|
if (userData.isEmpty) return [];
|
||||||
String? absencesJson = userData.elementAt(0)["absences"] as String?;
|
String? absencesJson = userData.elementAt(0)["absences"] as String?;
|
||||||
if (absencesJson == null) return [];
|
if (absencesJson == null) return [];
|
||||||
List<Absence> absences = (jsonDecode(absencesJson) as List).map((e) => Absence.fromJson(e)).toList();
|
List<Absence> absences = (jsonDecode(absencesJson) as List)
|
||||||
|
.map((e) => Absence.fromJson(e))
|
||||||
|
.toList();
|
||||||
return absences;
|
return absences;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<GroupAverage>> getGroupAverages({required String userId}) async {
|
Future<List<GroupAverage>> getGroupAverages({required String userId}) async {
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return [];
|
if (userData.isEmpty) return [];
|
||||||
String? groupAveragesJson = userData.elementAt(0)["group_averages"] as String?;
|
String? groupAveragesJson =
|
||||||
|
userData.elementAt(0)["group_averages"] as String?;
|
||||||
if (groupAveragesJson == null) return [];
|
if (groupAveragesJson == null) return [];
|
||||||
List<GroupAverage> groupAverages = (jsonDecode(groupAveragesJson) as List).map((e) => GroupAverage.fromJson(e)).toList();
|
List<GroupAverage> groupAverages = (jsonDecode(groupAveragesJson) as List)
|
||||||
|
.map((e) => GroupAverage.fromJson(e))
|
||||||
|
.toList();
|
||||||
return groupAverages;
|
return groupAverages;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SubjectLessonCount> getSubjectLessonCount({required String userId}) async {
|
Future<SubjectLessonCount> getSubjectLessonCount(
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
{required String userId}) async {
|
||||||
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return SubjectLessonCount.fromMap({});
|
if (userData.isEmpty) return SubjectLessonCount.fromMap({});
|
||||||
String? lessonCountJson = userData.elementAt(0)["subject_lesson_count"] as String?;
|
String? lessonCountJson =
|
||||||
|
userData.elementAt(0)["subject_lesson_count"] as String?;
|
||||||
if (lessonCountJson == null) return SubjectLessonCount.fromMap({});
|
if (lessonCountJson == null) return SubjectLessonCount.fromMap({});
|
||||||
SubjectLessonCount lessonCount = SubjectLessonCount.fromMap(jsonDecode(lessonCountJson) as Map);
|
SubjectLessonCount lessonCount =
|
||||||
|
SubjectLessonCount.fromMap(jsonDecode(lessonCountJson) as Map);
|
||||||
return lessonCount;
|
return lessonCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<DateTime> lastSeenGrade({required String userId}) async {
|
Future<DateTime> lastSeenGrade({required String userId}) async {
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return DateTime(0);
|
if (userData.isEmpty) return DateTime(0);
|
||||||
int? lastSeenDate = userData.elementAt(0)["last_seen_grade"] as int?;
|
int? lastSeenDate = userData.elementAt(0)["last_seen_grade"] as int?;
|
||||||
if (lastSeenDate == null) return DateTime(0);
|
if (lastSeenDate == null) return DateTime(0);
|
||||||
@ -156,10 +193,24 @@ class UserDatabaseQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, String>> renamedSubjects({required String userId}) async {
|
Future<Map<String, String>> renamedSubjects({required String userId}) async {
|
||||||
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
if (userData.isEmpty) return {};
|
if (userData.isEmpty) return {};
|
||||||
String? renamedSubjectsJson = userData.elementAt(0)["renamed_subjects"] as String?;
|
String? renamedSubjectsJson =
|
||||||
|
userData.elementAt(0)["renamed_subjects"] as String?;
|
||||||
if (renamedSubjectsJson == null) return {};
|
if (renamedSubjectsJson == null) return {};
|
||||||
return (jsonDecode(renamedSubjectsJson) as Map).map((key, value) => MapEntry(key.toString(), value.toString()));
|
return (jsonDecode(renamedSubjectsJson) as Map)
|
||||||
|
.map((key, value) => MapEntry(key.toString(), value.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, String>> renamedTeachers({required String userId}) async {
|
||||||
|
List<Map> userData =
|
||||||
|
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
|
||||||
|
if (userData.isEmpty) return {};
|
||||||
|
String? renamedTeachersJson =
|
||||||
|
userData.elementAt(0)["renamed_teachers"] as String?;
|
||||||
|
if (renamedTeachersJson == null) return {};
|
||||||
|
return (jsonDecode(renamedTeachersJson) as Map)
|
||||||
|
.map((key, value) => MapEntry(key.toString(), value.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,11 @@ class DatabaseStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeUser(User user) async {
|
Future<void> storeUser(User user) async {
|
||||||
List userRes = await db.query("users", where: "id = ?", whereArgs: [user.id]);
|
List userRes =
|
||||||
|
await db.query("users", where: "id = ?", whereArgs: [user.id]);
|
||||||
if (userRes.isNotEmpty) {
|
if (userRes.isNotEmpty) {
|
||||||
await db.update("users", user.toMap(), where: "id = ?", whereArgs: [user.id]);
|
await db
|
||||||
|
.update("users", user.toMap(), where: "id = ?", whereArgs: [user.id]);
|
||||||
} else {
|
} else {
|
||||||
await db.insert("users", user.toMap());
|
await db.insert("users", user.toMap());
|
||||||
await db.insert("user_data", {"id": user.id});
|
await db.insert("user_data", {"id": user.id});
|
||||||
@ -49,64 +51,93 @@ class UserDatabaseStore {
|
|||||||
|
|
||||||
Future<void> storeGrades(List<Grade> grades, {required String userId}) async {
|
Future<void> storeGrades(List<Grade> grades, {required String userId}) async {
|
||||||
String gradesJson = jsonEncode(grades.map((e) => e.json).toList());
|
String gradesJson = jsonEncode(grades.map((e) => e.json).toList());
|
||||||
await db.update("user_data", {"grades": gradesJson}, where: "id = ?", whereArgs: [userId]);
|
await db.update("user_data", {"grades": gradesJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeLessons(Map<Week, List<Lesson>?> lessons, {required String userId}) async {
|
Future<void> storeLessons(Map<Week, List<Lesson>?> lessons,
|
||||||
|
{required String userId}) async {
|
||||||
final map = lessons.map<String, List<Map<String, Object?>>>(
|
final map = lessons.map<String, List<Map<String, Object?>>>(
|
||||||
(k, v) => MapEntry(k.id.toString(), v!.where((e) => e.json != null).map((e) => e.json!).toList().cast()),
|
(k, v) => MapEntry(k.id.toString(),
|
||||||
|
v!.where((e) => e.json != null).map((e) => e.json!).toList().cast()),
|
||||||
);
|
);
|
||||||
String lessonsJson = jsonEncode(map);
|
String lessonsJson = jsonEncode(map);
|
||||||
await db.update("user_data", {"timetable": lessonsJson}, where: "id = ?", whereArgs: [userId]);
|
await db.update("user_data", {"timetable": lessonsJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeExams(List<Exam> exams, {required String userId}) async {
|
Future<void> storeExams(List<Exam> exams, {required String userId}) async {
|
||||||
String examsJson = jsonEncode(exams.map((e) => e.json).toList());
|
String examsJson = jsonEncode(exams.map((e) => e.json).toList());
|
||||||
await db.update("user_data", {"exams": examsJson}, where: "id = ?", whereArgs: [userId]);
|
await db.update("user_data", {"exams": examsJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeHomework(List<Homework> homework, {required String userId}) async {
|
Future<void> storeHomework(List<Homework> homework,
|
||||||
|
{required String userId}) async {
|
||||||
String homeworkJson = jsonEncode(homework.map((e) => e.json).toList());
|
String homeworkJson = jsonEncode(homework.map((e) => e.json).toList());
|
||||||
await db.update("user_data", {"homework": homeworkJson}, where: "id = ?", whereArgs: [userId]);
|
await db.update("user_data", {"homework": homeworkJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeMessages(List<Message> messages, {required String userId}) async {
|
Future<void> storeMessages(List<Message> messages,
|
||||||
|
{required String userId}) async {
|
||||||
String messagesJson = jsonEncode(messages.map((e) => e.json).toList());
|
String messagesJson = jsonEncode(messages.map((e) => e.json).toList());
|
||||||
await db.update("user_data", {"messages": messagesJson}, where: "id = ?", whereArgs: [userId]);
|
await db.update("user_data", {"messages": messagesJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeNotes(List<Note> notes, {required String userId}) async {
|
Future<void> storeNotes(List<Note> notes, {required String userId}) async {
|
||||||
String notesJson = jsonEncode(notes.map((e) => e.json).toList());
|
String notesJson = jsonEncode(notes.map((e) => e.json).toList());
|
||||||
await db.update("user_data", {"notes": notesJson}, where: "id = ?", whereArgs: [userId]);
|
await db.update("user_data", {"notes": notesJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeEvents(List<Event> events, {required String userId}) async {
|
Future<void> storeEvents(List<Event> events, {required String userId}) async {
|
||||||
String eventsJson = jsonEncode(events.map((e) => e.json).toList());
|
String eventsJson = jsonEncode(events.map((e) => e.json).toList());
|
||||||
await db.update("user_data", {"events": eventsJson}, where: "id = ?", whereArgs: [userId]);
|
await db.update("user_data", {"events": eventsJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeAbsences(List<Absence> absences, {required String userId}) async {
|
Future<void> storeAbsences(List<Absence> absences,
|
||||||
|
{required String userId}) async {
|
||||||
String absencesJson = jsonEncode(absences.map((e) => e.json).toList());
|
String absencesJson = jsonEncode(absences.map((e) => e.json).toList());
|
||||||
await db.update("user_data", {"absences": absencesJson}, where: "id = ?", whereArgs: [userId]);
|
await db.update("user_data", {"absences": absencesJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeGroupAverages(List<GroupAverage> groupAverages, {required String userId}) async {
|
Future<void> storeGroupAverages(List<GroupAverage> groupAverages,
|
||||||
String groupAveragesJson = jsonEncode(groupAverages.map((e) => e.json).toList());
|
{required String userId}) async {
|
||||||
await db.update("user_data", {"group_averages": groupAveragesJson}, where: "id = ?", whereArgs: [userId]);
|
String groupAveragesJson =
|
||||||
|
jsonEncode(groupAverages.map((e) => e.json).toList());
|
||||||
|
await db.update("user_data", {"group_averages": groupAveragesJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeSubjectLessonCount(SubjectLessonCount lessonCount, {required String userId}) async {
|
Future<void> storeSubjectLessonCount(SubjectLessonCount lessonCount,
|
||||||
|
{required String userId}) async {
|
||||||
String lessonCountJson = jsonEncode(lessonCount.toMap());
|
String lessonCountJson = jsonEncode(lessonCount.toMap());
|
||||||
await db.update("user_data", {"subject_lesson_count": lessonCountJson}, where: "id = ?", whereArgs: [userId]);
|
await db.update("user_data", {"subject_lesson_count": lessonCountJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeLastSeenGrade(DateTime date, {required String userId}) async {
|
Future<void> storeLastSeenGrade(DateTime date,
|
||||||
|
{required String userId}) async {
|
||||||
int lastSeenDate = date.millisecondsSinceEpoch;
|
int lastSeenDate = date.millisecondsSinceEpoch;
|
||||||
await db.update("user_data", {"last_seen_grade": lastSeenDate}, where: "id = ?", whereArgs: [userId]);
|
await db.update("user_data", {"last_seen_grade": lastSeenDate},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeRenamedSubjects(Map<String, String> subjects, {required String userId}) async {
|
Future<void> storeRenamedSubjects(Map<String, String> subjects,
|
||||||
|
{required String userId}) async {
|
||||||
String renamedSubjectsJson = jsonEncode(subjects);
|
String renamedSubjectsJson = jsonEncode(subjects);
|
||||||
await db.update("user_data", {"renamed_subjects": renamedSubjectsJson}, where: "id = ?", whereArgs: [userId]);
|
await db.update("user_data", {"renamed_subjects": renamedSubjectsJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> storeRenamedTeachers(Map<String, String> teachers,
|
||||||
|
{required String userId}) async {
|
||||||
|
String renamedTeachersJson = jsonEncode(teachers);
|
||||||
|
await db.update("user_data", {"renamed_teachers": renamedTeachersJson},
|
||||||
|
where: "id = ?", whereArgs: [userId]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,17 +42,23 @@ class NotificationsHelper {
|
|||||||
|
|
||||||
// loop through grades and see which hasn't been seen yet
|
// loop through grades and see which hasn't been seen yet
|
||||||
for (Grade grade in grades) {
|
for (Grade grade in grades) {
|
||||||
|
// if grade is not a normal grade (1-5), don't show it
|
||||||
|
if ([1, 2, 3, 4, 5].contains(grade.value.value)) {
|
||||||
// if the grade was added over a week ago, don't show it to avoid notification spam
|
// if the grade was added over a week ago, don't show it to avoid notification spam
|
||||||
if (grade.seenDate.isAfter(lastSeenGrade) &&
|
if (grade.seenDate.isAfter(lastSeenGrade) &&
|
||||||
grade.date.difference(DateTime.now()).inDays * -1 < 7) {
|
grade.date.difference(DateTime.now()).inDays * -1 < 7) {
|
||||||
// send notificiation about new grade
|
// send notificiation about new grade
|
||||||
const AndroidNotificationDetails androidNotificationDetails =
|
const AndroidNotificationDetails androidNotificationDetails =
|
||||||
AndroidNotificationDetails('GRADES', 'Jegyek',
|
AndroidNotificationDetails(
|
||||||
|
'GRADES',
|
||||||
|
'Jegyek',
|
||||||
channelDescription: 'Értesítés jegyek beírásakor',
|
channelDescription: 'Értesítés jegyek beírásakor',
|
||||||
importance: Importance.max,
|
importance: Importance.max,
|
||||||
priority: Priority.max,
|
priority: Priority.max,
|
||||||
color: Color(0xFF3D7BF4),
|
color: Color(0xFF3D7BF4),
|
||||||
ticker: 'Jegyek');
|
ticker: 'Jegyek',
|
||||||
|
groupKey: 'refilc.notifications.GRADES_GROUP',
|
||||||
|
);
|
||||||
const NotificationDetails notificationDetails =
|
const NotificationDetails notificationDetails =
|
||||||
NotificationDetails(android: androidNotificationDetails);
|
NotificationDetails(android: androidNotificationDetails);
|
||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
@ -69,6 +75,7 @@ class NotificationsHelper {
|
|||||||
notificationDetails);
|
notificationDetails);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// set grade seen status
|
// set grade seen status
|
||||||
gradeProvider.seenAll();
|
gradeProvider.seenAll();
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,36 @@
|
|||||||
class News {
|
class News {
|
||||||
|
String id;
|
||||||
String title;
|
String title;
|
||||||
String content;
|
String content;
|
||||||
String link;
|
String link;
|
||||||
String openLabel;
|
String openLabel;
|
||||||
String platform;
|
String platform;
|
||||||
bool emergency;
|
bool emergency;
|
||||||
|
DateTime expireDate;
|
||||||
Map? json;
|
Map? json;
|
||||||
|
|
||||||
News({
|
News({
|
||||||
|
required this.id,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.content,
|
required this.content,
|
||||||
required this.link,
|
required this.link,
|
||||||
required this.openLabel,
|
required this.openLabel,
|
||||||
required this.platform,
|
required this.platform,
|
||||||
required this.emergency,
|
required this.emergency,
|
||||||
|
required this.expireDate,
|
||||||
this.json,
|
this.json,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory News.fromJson(Map json) {
|
factory News.fromJson(Map json) {
|
||||||
return News(
|
return News(
|
||||||
|
id: json["id"] ?? "",
|
||||||
title: json["title"] ?? "",
|
title: json["title"] ?? "",
|
||||||
content: json["content"] ?? "",
|
content: json["content"] ?? "",
|
||||||
link: json["link"] ?? "",
|
link: json["link"] ?? "",
|
||||||
openLabel: json["open_label"] ?? "",
|
openLabel: json["open_label"] ?? "",
|
||||||
platform: json["platform"] ?? "",
|
platform: json["platform"] ?? "",
|
||||||
emergency: json["emergency"] ?? false,
|
emergency: json["emergency"] ?? false,
|
||||||
|
expireDate: DateTime.parse(json["expire_date"] ?? ''),
|
||||||
json: json,
|
json: json,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
// zero is one, ...
|
// zero is one, ...
|
||||||
List<Color> _gradeColors;
|
List<Color> _gradeColors;
|
||||||
bool _newsEnabled;
|
bool _newsEnabled;
|
||||||
int _newsState;
|
String _seenNews;
|
||||||
bool _notificationsEnabled;
|
bool _notificationsEnabled;
|
||||||
/*
|
/*
|
||||||
notificationsBitfield values:
|
notificationsBitfield values:
|
||||||
@ -69,6 +69,8 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
String _lastAccountId;
|
String _lastAccountId;
|
||||||
bool _renamedSubjectsEnabled;
|
bool _renamedSubjectsEnabled;
|
||||||
bool _renamedSubjectsItalics;
|
bool _renamedSubjectsItalics;
|
||||||
|
bool _renamedTeachersEnabled;
|
||||||
|
bool _renamedTeachersItalics;
|
||||||
|
|
||||||
SettingsProvider({
|
SettingsProvider({
|
||||||
DatabaseProvider? database,
|
DatabaseProvider? database,
|
||||||
@ -79,7 +81,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
required AccentColor accentColor,
|
required AccentColor accentColor,
|
||||||
required List<Color> gradeColors,
|
required List<Color> gradeColors,
|
||||||
required bool newsEnabled,
|
required bool newsEnabled,
|
||||||
required int newsState,
|
required String seenNews,
|
||||||
required bool notificationsEnabled,
|
required bool notificationsEnabled,
|
||||||
required int notificationsBitfield,
|
required int notificationsBitfield,
|
||||||
required bool developerMode,
|
required bool developerMode,
|
||||||
@ -106,6 +108,8 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
required String lastAccountId,
|
required String lastAccountId,
|
||||||
required bool renameSubjectsEnabled,
|
required bool renameSubjectsEnabled,
|
||||||
required bool renameSubjectsItalics,
|
required bool renameSubjectsItalics,
|
||||||
|
required bool renameTeachersEnabled,
|
||||||
|
required bool renameTeachersItalics,
|
||||||
}) : _database = database,
|
}) : _database = database,
|
||||||
_language = language,
|
_language = language,
|
||||||
_startPage = startPage,
|
_startPage = startPage,
|
||||||
@ -114,7 +118,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
_accentColor = accentColor,
|
_accentColor = accentColor,
|
||||||
_gradeColors = gradeColors,
|
_gradeColors = gradeColors,
|
||||||
_newsEnabled = newsEnabled,
|
_newsEnabled = newsEnabled,
|
||||||
_newsState = newsState,
|
_seenNews = seenNews,
|
||||||
_notificationsEnabled = notificationsEnabled,
|
_notificationsEnabled = notificationsEnabled,
|
||||||
_notificationsBitfield = notificationsBitfield,
|
_notificationsBitfield = notificationsBitfield,
|
||||||
_developerMode = developerMode,
|
_developerMode = developerMode,
|
||||||
@ -140,7 +144,9 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
_premiumLogin = premiumLogin,
|
_premiumLogin = premiumLogin,
|
||||||
_lastAccountId = lastAccountId,
|
_lastAccountId = lastAccountId,
|
||||||
_renamedSubjectsEnabled = renameSubjectsEnabled,
|
_renamedSubjectsEnabled = renameSubjectsEnabled,
|
||||||
_renamedSubjectsItalics = renameSubjectsItalics;
|
_renamedSubjectsItalics = renameSubjectsItalics,
|
||||||
|
_renamedTeachersEnabled = renameTeachersEnabled,
|
||||||
|
_renamedTeachersItalics = renameTeachersItalics;
|
||||||
|
|
||||||
factory SettingsProvider.fromMap(Map map,
|
factory SettingsProvider.fromMap(Map map,
|
||||||
{required DatabaseProvider database}) {
|
{required DatabaseProvider database}) {
|
||||||
@ -152,6 +158,8 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
log("[ERROR] SettingsProvider.fromMap: $e");
|
log("[ERROR] SettingsProvider.fromMap: $e");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print(map['seen_news']);
|
||||||
|
|
||||||
return SettingsProvider(
|
return SettingsProvider(
|
||||||
database: database,
|
database: database,
|
||||||
language: map["language"],
|
language: map["language"],
|
||||||
@ -167,7 +175,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
Color(map["grade_color5"]),
|
Color(map["grade_color5"]),
|
||||||
],
|
],
|
||||||
newsEnabled: map["news"] == 1,
|
newsEnabled: map["news"] == 1,
|
||||||
newsState: map["news_state"],
|
seenNews: map["seen_news"],
|
||||||
notificationsEnabled: map["notifications"] == 1,
|
notificationsEnabled: map["notifications"] == 1,
|
||||||
notificationsBitfield: map["notifications_bitfield"],
|
notificationsBitfield: map["notifications_bitfield"],
|
||||||
notificationPollInterval: map["notification_poll_interval"],
|
notificationPollInterval: map["notification_poll_interval"],
|
||||||
@ -195,6 +203,8 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
lastAccountId: map["last_account_id"],
|
lastAccountId: map["last_account_id"],
|
||||||
renameSubjectsEnabled: map["renamed_subjects_enabled"] == 1,
|
renameSubjectsEnabled: map["renamed_subjects_enabled"] == 1,
|
||||||
renameSubjectsItalics: map["renamed_subjects_italics"] == 1,
|
renameSubjectsItalics: map["renamed_subjects_italics"] == 1,
|
||||||
|
renameTeachersEnabled: map["renamed_teachers_enabled"] == 1,
|
||||||
|
renameTeachersItalics: map["renamed_teachers_italics"] == 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +216,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
"theme": _theme.index,
|
"theme": _theme.index,
|
||||||
"accent_color": _accentColor.index,
|
"accent_color": _accentColor.index,
|
||||||
"news": _newsEnabled ? 1 : 0,
|
"news": _newsEnabled ? 1 : 0,
|
||||||
"news_state": _newsState,
|
"seen_news": _seenNews,
|
||||||
"notifications": _notificationsEnabled ? 1 : 0,
|
"notifications": _notificationsEnabled ? 1 : 0,
|
||||||
"notifications_bitfield": _notificationsBitfield,
|
"notifications_bitfield": _notificationsBitfield,
|
||||||
"developer_mode": _developerMode ? 1 : 0,
|
"developer_mode": _developerMode ? 1 : 0,
|
||||||
@ -236,7 +246,9 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
"premium_login": _premiumLogin,
|
"premium_login": _premiumLogin,
|
||||||
"last_account_id": _lastAccountId,
|
"last_account_id": _lastAccountId,
|
||||||
"renamed_subjects_enabled": _renamedSubjectsEnabled ? 1 : 0,
|
"renamed_subjects_enabled": _renamedSubjectsEnabled ? 1 : 0,
|
||||||
"renamed_subjects_italics": _renamedSubjectsItalics ? 1 : 0
|
"renamed_subjects_italics": _renamedSubjectsItalics ? 1 : 0,
|
||||||
|
"renamed_teachers_enabled": _renamedTeachersEnabled ? 1 : 0,
|
||||||
|
"renamed_teachers_italics": _renamedTeachersItalics ? 1 : 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,7 +268,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
DarkMobileAppColors().gradeFive,
|
DarkMobileAppColors().gradeFive,
|
||||||
],
|
],
|
||||||
newsEnabled: true,
|
newsEnabled: true,
|
||||||
newsState: -1,
|
seenNews: '',
|
||||||
notificationsEnabled: true,
|
notificationsEnabled: true,
|
||||||
notificationsBitfield: 255,
|
notificationsBitfield: 255,
|
||||||
developerMode: false,
|
developerMode: false,
|
||||||
@ -283,6 +295,8 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
lastAccountId: "",
|
lastAccountId: "",
|
||||||
renameSubjectsEnabled: false,
|
renameSubjectsEnabled: false,
|
||||||
renameSubjectsItalics: false,
|
renameSubjectsItalics: false,
|
||||||
|
renameTeachersEnabled: false,
|
||||||
|
renameTeachersItalics: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +308,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
AccentColor get accentColor => _accentColor;
|
AccentColor get accentColor => _accentColor;
|
||||||
List<Color> get gradeColors => _gradeColors;
|
List<Color> get gradeColors => _gradeColors;
|
||||||
bool get newsEnabled => _newsEnabled;
|
bool get newsEnabled => _newsEnabled;
|
||||||
int get newsState => _newsState;
|
List<String> get seenNews => _seenNews.split(',');
|
||||||
bool get notificationsEnabled => _notificationsEnabled;
|
bool get notificationsEnabled => _notificationsEnabled;
|
||||||
int get notificationsBitfield => _notificationsBitfield;
|
int get notificationsBitfield => _notificationsBitfield;
|
||||||
bool get developerMode => _developerMode;
|
bool get developerMode => _developerMode;
|
||||||
@ -324,6 +338,8 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
String get lastAccountId => _lastAccountId;
|
String get lastAccountId => _lastAccountId;
|
||||||
bool get renamedSubjectsEnabled => _renamedSubjectsEnabled;
|
bool get renamedSubjectsEnabled => _renamedSubjectsEnabled;
|
||||||
bool get renamedSubjectsItalics => _renamedSubjectsItalics;
|
bool get renamedSubjectsItalics => _renamedSubjectsItalics;
|
||||||
|
bool get renamedTeachersEnabled => _renamedTeachersEnabled;
|
||||||
|
bool get renamedTeachersItalics => _renamedTeachersItalics;
|
||||||
|
|
||||||
Future<void> update({
|
Future<void> update({
|
||||||
bool store = true,
|
bool store = true,
|
||||||
@ -334,7 +350,7 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
AccentColor? accentColor,
|
AccentColor? accentColor,
|
||||||
List<Color>? gradeColors,
|
List<Color>? gradeColors,
|
||||||
bool? newsEnabled,
|
bool? newsEnabled,
|
||||||
int? newsState,
|
String? seenNewsId,
|
||||||
bool? notificationsEnabled,
|
bool? notificationsEnabled,
|
||||||
int? notificationsBitfield,
|
int? notificationsBitfield,
|
||||||
bool? developerMode,
|
bool? developerMode,
|
||||||
@ -361,6 +377,8 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
String? lastAccountId,
|
String? lastAccountId,
|
||||||
bool? renamedSubjectsEnabled,
|
bool? renamedSubjectsEnabled,
|
||||||
bool? renamedSubjectsItalics,
|
bool? renamedSubjectsItalics,
|
||||||
|
bool? renamedTeachersEnabled,
|
||||||
|
bool? renamedTeachersItalics,
|
||||||
}) async {
|
}) async {
|
||||||
if (language != null && language != _language) _language = language;
|
if (language != null && language != _language) _language = language;
|
||||||
if (startPage != null && startPage != _startPage) _startPage = startPage;
|
if (startPage != null && startPage != _startPage) _startPage = startPage;
|
||||||
@ -375,7 +393,11 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
if (newsEnabled != null && newsEnabled != _newsEnabled) {
|
if (newsEnabled != null && newsEnabled != _newsEnabled) {
|
||||||
_newsEnabled = newsEnabled;
|
_newsEnabled = newsEnabled;
|
||||||
}
|
}
|
||||||
if (newsState != null && newsState != _newsState) _newsState = newsState;
|
if (seenNewsId != null && !_seenNews.split(',').contains(seenNewsId)) {
|
||||||
|
var tempList = _seenNews.split(',');
|
||||||
|
tempList.add(seenNewsId);
|
||||||
|
_seenNews = tempList.join(',');
|
||||||
|
}
|
||||||
if (notificationsEnabled != null &&
|
if (notificationsEnabled != null &&
|
||||||
notificationsEnabled != _notificationsEnabled) {
|
notificationsEnabled != _notificationsEnabled) {
|
||||||
_notificationsEnabled = notificationsEnabled;
|
_notificationsEnabled = notificationsEnabled;
|
||||||
@ -448,6 +470,14 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
renamedSubjectsItalics != _renamedSubjectsItalics) {
|
renamedSubjectsItalics != _renamedSubjectsItalics) {
|
||||||
_renamedSubjectsItalics = renamedSubjectsItalics;
|
_renamedSubjectsItalics = renamedSubjectsItalics;
|
||||||
}
|
}
|
||||||
|
if (renamedTeachersEnabled != null &&
|
||||||
|
renamedTeachersEnabled != _renamedTeachersEnabled) {
|
||||||
|
_renamedTeachersEnabled = renamedTeachersEnabled;
|
||||||
|
}
|
||||||
|
if (renamedTeachersItalics != null &&
|
||||||
|
renamedTeachersItalics != _renamedTeachersItalics) {
|
||||||
|
_renamedTeachersItalics = renamedTeachersItalics;
|
||||||
|
}
|
||||||
if (store) await _database?.store.storeSettings(this);
|
if (store) await _database?.store.storeSettings(this);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,8 @@ import 'package:filcnaplo_mobile_ui/common/widgets/lesson/changed_lesson_tile.da
|
|||||||
import 'package:filcnaplo/utils/format.dart';
|
import 'package:filcnaplo/utils/format.dart';
|
||||||
|
|
||||||
// difference.inDays is not reliable
|
// difference.inDays is not reliable
|
||||||
bool _sameDate(DateTime a, DateTime b) => (a.year == b.year && a.month == b.month && a.day == b.day);
|
bool _sameDate(DateTime a, DateTime b) =>
|
||||||
|
(a.year == b.year && a.month == b.month && a.day == b.day);
|
||||||
|
|
||||||
List<Widget> sortDateWidgets(
|
List<Widget> sortDateWidgets(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
@ -35,13 +36,16 @@ List<Widget> sortDateWidgets(
|
|||||||
if (message.conversationId != null) {
|
if (message.conversationId != null) {
|
||||||
convMessages.add(w);
|
convMessages.add(w);
|
||||||
|
|
||||||
Conversation conv = conversations.firstWhere((e) => e.id == message.conversationId, orElse: () => Conversation(id: message.conversationId!));
|
Conversation conv = conversations.firstWhere(
|
||||||
|
(e) => e.id == message.conversationId,
|
||||||
|
orElse: () => Conversation(id: message.conversationId!));
|
||||||
conv.add(message);
|
conv.add(message);
|
||||||
if (conv.messages.length == 1) conversations.add(conv);
|
if (conv.messages.length == 1) conversations.add(conv);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversations.any((c) => c.id == message.messageId)) {
|
if (conversations.any((c) => c.id == message.messageId)) {
|
||||||
Conversation conv = conversations.firstWhere((e) => e.id == message.messageId);
|
Conversation conv =
|
||||||
|
conversations.firstWhere((e) => e.id == message.messageId);
|
||||||
convMessages.add(w);
|
convMessages.add(w);
|
||||||
conv.add(message);
|
conv.add(message);
|
||||||
}
|
}
|
||||||
@ -87,26 +91,41 @@ List<Widget> sortDateWidgets(
|
|||||||
|
|
||||||
// Group Absence Tiles
|
// Group Absence Tiles
|
||||||
List<DateWidget> absenceTileWidgets = elements.where((element) {
|
List<DateWidget> absenceTileWidgets = elements.where((element) {
|
||||||
return element.widget is AbsenceViewable && (element.widget as AbsenceViewable).absence.delay == 0;
|
return element.widget is AbsenceViewable &&
|
||||||
|
(element.widget as AbsenceViewable).absence.delay == 0;
|
||||||
}).toList();
|
}).toList();
|
||||||
List<AbsenceViewable> absenceTiles = absenceTileWidgets.map((e) => e.widget as AbsenceViewable).toList();
|
List<AbsenceViewable> absenceTiles =
|
||||||
|
absenceTileWidgets.map((e) => e.widget as AbsenceViewable).toList();
|
||||||
if (absenceTiles.length > 1) {
|
if (absenceTiles.length > 1) {
|
||||||
elements.removeWhere((element) => element.widget.runtimeType == AbsenceViewable && (element.widget as AbsenceViewable).absence.delay == 0);
|
elements.removeWhere((element) =>
|
||||||
|
element.widget.runtimeType == AbsenceViewable &&
|
||||||
|
(element.widget as AbsenceViewable).absence.delay == 0);
|
||||||
if (elements.isEmpty) {
|
if (elements.isEmpty) {
|
||||||
cst = false;
|
cst = false;
|
||||||
}
|
}
|
||||||
elements.add(DateWidget(
|
elements.add(
|
||||||
widget: AbsenceGroupTile(absenceTiles, showDate: !cst),
|
DateWidget(
|
||||||
|
widget: AbsenceGroupTile(
|
||||||
|
absenceTiles,
|
||||||
|
showDate: !cst,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||||
|
),
|
||||||
date: absenceTileWidgets.first.date,
|
date: absenceTileWidgets.first.date,
|
||||||
key: "${absenceTileWidgets.first.date.millisecondsSinceEpoch}-absence-group"));
|
key:
|
||||||
|
"${absenceTileWidgets.first.date.millisecondsSinceEpoch}-absence-group"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bring Lesson Tiles to front & sort by index asc
|
// Bring Lesson Tiles to front & sort by index asc
|
||||||
List<DateWidget> lessonTiles = elements.where((element) {
|
List<DateWidget> lessonTiles = elements.where((element) {
|
||||||
return element.widget.runtimeType == ChangedLessonTile;
|
return element.widget.runtimeType == ChangedLessonTile;
|
||||||
}).toList();
|
}).toList();
|
||||||
lessonTiles.sort((a, b) => (a.widget as ChangedLessonTile).lesson.lessonIndex.compareTo((b.widget as ChangedLessonTile).lesson.lessonIndex));
|
lessonTiles.sort((a, b) => (a.widget as ChangedLessonTile)
|
||||||
elements.removeWhere((element) => element.widget.runtimeType == ChangedLessonTile);
|
.lesson
|
||||||
|
.lessonIndex
|
||||||
|
.compareTo((b.widget as ChangedLessonTile).lesson.lessonIndex));
|
||||||
|
elements.removeWhere(
|
||||||
|
(element) => element.widget.runtimeType == ChangedLessonTile);
|
||||||
elements.insertAll(0, lessonTiles);
|
elements.insertAll(0, lessonTiles);
|
||||||
|
|
||||||
final date = (elements + absenceTileWidgets).first.date;
|
final date = (elements + absenceTileWidgets).first.date;
|
||||||
@ -122,7 +141,8 @@ List<Widget> sortDateWidgets(
|
|||||||
spawnIsolate: false,
|
spawnIsolate: false,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, animation, item, index) => filterItemBuilder(context, animation, item.widget, index),
|
itemBuilder: (context, animation, item, index) =>
|
||||||
|
filterItemBuilder(context, animation, item.widget, index),
|
||||||
items: elements,
|
items: elements,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -131,9 +151,12 @@ List<Widget> sortDateWidgets(
|
|||||||
}
|
}
|
||||||
|
|
||||||
final nh = DateTime.now();
|
final nh = DateTime.now();
|
||||||
final now = DateTime(nh.year, nh.month, nh.day).subtract(const Duration(seconds: 1));
|
final now =
|
||||||
|
DateTime(nh.year, nh.month, nh.day).subtract(const Duration(seconds: 1));
|
||||||
|
|
||||||
if (showDivider && items.any((i) => i.date.isBefore(now)) && items.any((i) => i.date.isAfter(now))) {
|
if (showDivider &&
|
||||||
|
items.any((i) => i.date.isBefore(now)) &&
|
||||||
|
items.any((i) => i.date.isAfter(now))) {
|
||||||
items.add(
|
items.add(
|
||||||
DateWidget(
|
DateWidget(
|
||||||
date: now,
|
date: now,
|
||||||
@ -153,7 +176,9 @@ List<Widget> sortDateWidgets(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sort future dates asc, past dates desc
|
// Sort future dates asc, past dates desc
|
||||||
items.sort((a, b) => (a.date.isAfter(now) && b.date.isAfter(now) ? 1 : -1) * a.date.compareTo(b.date));
|
items.sort((a, b) =>
|
||||||
|
(a.date.isAfter(now) && b.date.isAfter(now) ? 1 : -1) *
|
||||||
|
a.date.compareTo(b.date));
|
||||||
|
|
||||||
return items.map((e) => e.widget).toList();
|
return items.map((e) => e.widget).toList();
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,8 @@ class SubjectAbsence {
|
|||||||
List<Absence> absences;
|
List<Absence> absences;
|
||||||
double percentage;
|
double percentage;
|
||||||
|
|
||||||
SubjectAbsence({required this.subject, this.absences = const [], this.percentage = 0.0});
|
SubjectAbsence(
|
||||||
|
{required this.subject, this.absences = const [], this.percentage = 0.0});
|
||||||
}
|
}
|
||||||
|
|
||||||
class AbsencesPage extends StatefulWidget {
|
class AbsencesPage extends StatefulWidget {
|
||||||
@ -45,7 +46,8 @@ class AbsencesPage extends StatefulWidget {
|
|||||||
_AbsencesPageState createState() => _AbsencesPageState();
|
_AbsencesPageState createState() => _AbsencesPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMixin {
|
class _AbsencesPageState extends State<AbsencesPage>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
late UserProvider user;
|
late UserProvider user;
|
||||||
late AbsenceProvider absenceProvider;
|
late AbsenceProvider absenceProvider;
|
||||||
late TimetableProvider timetableProvider;
|
late TimetableProvider timetableProvider;
|
||||||
@ -65,7 +67,9 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
for (final lesson in timetableProvider.getWeek(Week.current()) ?? []) {
|
for (final lesson in timetableProvider.getWeek(Week.current()) ?? []) {
|
||||||
if (!lesson.isEmpty && lesson.subject.id != '' && lesson.lessonYearIndex != null) {
|
if (!lesson.isEmpty &&
|
||||||
|
lesson.subject.id != '' &&
|
||||||
|
lesson.lessonYearIndex != null) {
|
||||||
_lessonCount.update(
|
_lessonCount.update(
|
||||||
lesson.subject,
|
lesson.subject,
|
||||||
(value) {
|
(value) {
|
||||||
@ -89,25 +93,30 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
if (absence.delay != 0) continue;
|
if (absence.delay != 0) continue;
|
||||||
|
|
||||||
if (!_absences.containsKey(absence.subject)) {
|
if (!_absences.containsKey(absence.subject)) {
|
||||||
_absences[absence.subject] = SubjectAbsence(subject: absence.subject, absences: [absence]);
|
_absences[absence.subject] =
|
||||||
|
SubjectAbsence(subject: absence.subject, absences: [absence]);
|
||||||
} else {
|
} else {
|
||||||
_absences[absence.subject]?.absences.add(absence);
|
_absences[absence.subject]?.absences.add(absence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_absences.forEach((subject, absence) {
|
_absences.forEach((subject, absence) {
|
||||||
final absentLessonsOfSubject = absenceProvider.absences.where((e) => e.subject == subject && e.delay == 0).length;
|
final absentLessonsOfSubject = absenceProvider.absences
|
||||||
|
.where((e) => e.subject == subject && e.delay == 0)
|
||||||
|
.length;
|
||||||
final totalLessonsOfSubject = _lessonCount[subject]?.lessonYearIndex ?? 0;
|
final totalLessonsOfSubject = _lessonCount[subject]?.lessonYearIndex ?? 0;
|
||||||
|
|
||||||
double absentLessonsOfSubjectPercentage;
|
double absentLessonsOfSubjectPercentage;
|
||||||
|
|
||||||
if (absentLessonsOfSubject <= totalLessonsOfSubject) {
|
if (absentLessonsOfSubject <= totalLessonsOfSubject) {
|
||||||
absentLessonsOfSubjectPercentage = absentLessonsOfSubject / totalLessonsOfSubject * 100;
|
absentLessonsOfSubjectPercentage =
|
||||||
|
absentLessonsOfSubject / totalLessonsOfSubject * 100;
|
||||||
} else {
|
} else {
|
||||||
absentLessonsOfSubjectPercentage = -1;
|
absentLessonsOfSubjectPercentage = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_absences[subject]?.percentage = absentLessonsOfSubjectPercentage.clamp(-1, 100.0);
|
_absences[subject]?.percentage =
|
||||||
|
absentLessonsOfSubjectPercentage.clamp(-1, 100.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
absences = _absences.values.toList();
|
absences = _absences.values.toList();
|
||||||
@ -131,7 +140,8 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.only(top: 12.0),
|
padding: const EdgeInsets.only(top: 12.0),
|
||||||
child: NestedScrollView(
|
child: NestedScrollView(
|
||||||
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
|
physics: const BouncingScrollPhysics(
|
||||||
|
parent: AlwaysScrollableScrollPhysics()),
|
||||||
headerSliverBuilder: (context, _) => [
|
headerSliverBuilder: (context, _) => [
|
||||||
SliverAppBar(
|
SliverAppBar(
|
||||||
pinned: true,
|
pinned: true,
|
||||||
@ -145,7 +155,10 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Absences".i18n,
|
"Absences".i18n,
|
||||||
style: TextStyle(color: AppColors.of(context).text, fontSize: 32.0, fontWeight: FontWeight.bold),
|
style: TextStyle(
|
||||||
|
color: AppColors.of(context).text,
|
||||||
|
fontSize: 32.0,
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
bottom: FilterBar(items: [
|
bottom: FilterBar(items: [
|
||||||
@ -158,7 +171,8 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
body: TabBarView(
|
body: TabBarView(
|
||||||
physics: const BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(),
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
children: List.generate(3, (index) => filterViewBuilder(context, index))),
|
children: List.generate(
|
||||||
|
3, (index) => filterViewBuilder(context, index))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -174,10 +188,17 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
widget: AbsenceSubjectTile(
|
widget: AbsenceSubjectTile(
|
||||||
a.subject,
|
a.subject,
|
||||||
percentage: a.percentage,
|
percentage: a.percentage,
|
||||||
excused: a.absences.where((a) => a.state == Justification.excused).length,
|
excused: a.absences
|
||||||
unexcused: a.absences.where((a) => a.state == Justification.unexcused).length,
|
.where((a) => a.state == Justification.excused)
|
||||||
pending: a.absences.where((a) => a.state == Justification.pending).length,
|
.length,
|
||||||
onTap: () => AbsenceSubjectView.show(a.subject, a.absences, context: context),
|
unexcused: a.absences
|
||||||
|
.where((a) => a.state == Justification.unexcused)
|
||||||
|
.length,
|
||||||
|
pending: a.absences
|
||||||
|
.where((a) => a.state == Justification.pending)
|
||||||
|
.length,
|
||||||
|
onTap: () => AbsenceSubjectView.show(a.subject, a.absences,
|
||||||
|
context: context),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -187,14 +208,17 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
if (absence.delay != 0) {
|
if (absence.delay != 0) {
|
||||||
items.add(DateWidget(
|
items.add(DateWidget(
|
||||||
date: absence.date,
|
date: absence.date,
|
||||||
widget: AbsenceViewable(absence, padding: EdgeInsets.zero),
|
widget: AbsenceViewable(
|
||||||
));
|
absence,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AbsenceFilter.misses:
|
case AbsenceFilter.misses:
|
||||||
for (var note in noteProvider.notes) {
|
for (var note in noteProvider.notes) {
|
||||||
if (note.type?.name == "HaziFeladatHiany" || note.type?.name == "Felszereleshiany") {
|
if (note.type?.name == "HaziFeladatHiany" ||
|
||||||
|
note.type?.name == "Felszereleshiany") {
|
||||||
items.add(DateWidget(
|
items.add(DateWidget(
|
||||||
date: note.date,
|
date: note.date,
|
||||||
widget: MissTile(note),
|
widget: MissTile(note),
|
||||||
@ -232,10 +256,15 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12.0)),
|
||||||
title: Text("attention".i18n),
|
title: Text("attention".i18n),
|
||||||
content: Text("attention_body".i18n),
|
content: Text("attention_body".i18n),
|
||||||
actions: [ActionButton(label: "Ok", onTap: () => Navigator.of(context).pop())],
|
actions: [
|
||||||
|
ActionButton(
|
||||||
|
label: "Ok",
|
||||||
|
onTap: () => Navigator.of(context).pop())
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -262,7 +291,10 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
children: getFilterWidgets(AbsenceFilter.values[activeData]).map((e) => e.widget).cast<Widget>().toList(),
|
children: getFilterWidgets(AbsenceFilter.values[activeData])
|
||||||
|
.map((e) => e.widget)
|
||||||
|
.cast<Widget>()
|
||||||
|
.toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -284,7 +316,8 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
itemCount: max(filterWidgets.length + (activeData <= 1 ? 1 : 0), 1),
|
itemCount: max(filterWidgets.length + (activeData <= 1 ? 1 : 0), 1),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (filterWidgets.isNotEmpty) {
|
if (filterWidgets.isNotEmpty) {
|
||||||
if ((index == 0 && activeData == 1) || (index == 0 && activeData == 0)) {
|
if ((index == 0 && activeData == 1) ||
|
||||||
|
(index == 0 && activeData == 0)) {
|
||||||
int value1 = 0;
|
int value1 = 0;
|
||||||
int value2 = 0;
|
int value2 = 0;
|
||||||
String title1 = "";
|
String title1 = "";
|
||||||
@ -292,18 +325,26 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
String suffix = "";
|
String suffix = "";
|
||||||
|
|
||||||
if (activeData == AbsenceFilter.absences.index) {
|
if (activeData == AbsenceFilter.absences.index) {
|
||||||
value1 = absenceProvider.absences.where((e) => e.delay == 0 && e.state == Justification.excused).length;
|
value1 = absenceProvider.absences
|
||||||
value2 = absenceProvider.absences.where((e) => e.delay == 0 && e.state == Justification.unexcused).length;
|
.where((e) =>
|
||||||
|
e.delay == 0 && e.state == Justification.excused)
|
||||||
|
.length;
|
||||||
|
value2 = absenceProvider.absences
|
||||||
|
.where((e) =>
|
||||||
|
e.delay == 0 && e.state == Justification.unexcused)
|
||||||
|
.length;
|
||||||
title1 = "stat_1".i18n;
|
title1 = "stat_1".i18n;
|
||||||
title2 = "stat_2".i18n;
|
title2 = "stat_2".i18n;
|
||||||
suffix = " " + "hr".i18n;
|
suffix = " " + "hr".i18n;
|
||||||
} else if (activeData == AbsenceFilter.delays.index) {
|
} else if (activeData == AbsenceFilter.delays.index) {
|
||||||
value1 = absenceProvider.absences
|
value1 = absenceProvider.absences
|
||||||
.where((e) => e.delay != 0 && e.state == Justification.excused)
|
.where((e) =>
|
||||||
|
e.delay != 0 && e.state == Justification.excused)
|
||||||
.map((e) => e.delay)
|
.map((e) => e.delay)
|
||||||
.fold(0, (a, b) => a + b);
|
.fold(0, (a, b) => a + b);
|
||||||
value2 = absenceProvider.absences
|
value2 = absenceProvider.absences
|
||||||
.where((e) => e.delay != 0 && e.state == Justification.unexcused)
|
.where((e) =>
|
||||||
|
e.delay != 0 && e.state == Justification.unexcused)
|
||||||
.map((e) => e.delay)
|
.map((e) => e.delay)
|
||||||
.fold(0, (a, b) => a + b);
|
.fold(0, (a, b) => a + b);
|
||||||
title1 = "stat_3".i18n;
|
title1 = "stat_3".i18n;
|
||||||
@ -312,7 +353,8 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 24.0, left: 24.0, right: 24.0),
|
padding: const EdgeInsets.only(
|
||||||
|
bottom: 24.0, left: 24.0, right: 24.0),
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StatisticsTile(
|
child: StatisticsTile(
|
||||||
@ -348,7 +390,8 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 6.0),
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 24.0, vertical: 6.0),
|
||||||
child: filterWidgets[index - (activeData <= 1 ? 1 : 0)],
|
child: filterWidgets[index - (activeData <= 1 ? 1 : 0)],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import 'package:filcnaplo/api/providers/update_provider.dart';
|
import 'package:filcnaplo/api/providers/update_provider.dart';
|
||||||
import 'package:filcnaplo/theme/colors/accent.dart';
|
|
||||||
import 'package:filcnaplo/theme/observer.dart';
|
|
||||||
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
|
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
|
||||||
import 'package:filcnaplo_kreta_api/providers/event_provider.dart';
|
import 'package:filcnaplo_kreta_api/providers/event_provider.dart';
|
||||||
import 'package:filcnaplo_kreta_api/providers/exam_provider.dart';
|
import 'package:filcnaplo_kreta_api/providers/exam_provider.dart';
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import "category.dart";
|
import "category.dart";
|
||||||
import "subject.dart";
|
import "subject.dart";
|
||||||
|
import "teacher.dart";
|
||||||
|
|
||||||
class Absence {
|
class Absence {
|
||||||
Map? json;
|
Map? json;
|
||||||
@ -7,7 +8,7 @@ class Absence {
|
|||||||
DateTime date;
|
DateTime date;
|
||||||
int delay;
|
int delay;
|
||||||
DateTime submitDate;
|
DateTime submitDate;
|
||||||
String teacher;
|
Teacher teacher;
|
||||||
Justification state;
|
Justification state;
|
||||||
Category? justification;
|
Category? justification;
|
||||||
Category? type;
|
Category? type;
|
||||||
@ -41,8 +42,12 @@ class Absence {
|
|||||||
DateTime lessonEnd;
|
DateTime lessonEnd;
|
||||||
int? lessonIndex;
|
int? lessonIndex;
|
||||||
if (json["Ora"] != null) {
|
if (json["Ora"] != null) {
|
||||||
lessonStart = json["Ora"]["KezdoDatum"] != null ? DateTime.parse(json["Ora"]["KezdoDatum"]).toLocal() : DateTime(0);
|
lessonStart = json["Ora"]["KezdoDatum"] != null
|
||||||
lessonEnd = json["Ora"]["VegDatum"] != null ? DateTime.parse(json["Ora"]["VegDatum"]).toLocal() : DateTime(0);
|
? DateTime.parse(json["Ora"]["KezdoDatum"]).toLocal()
|
||||||
|
: DateTime(0);
|
||||||
|
lessonEnd = json["Ora"]["VegDatum"] != null
|
||||||
|
? DateTime.parse(json["Ora"]["VegDatum"]).toLocal()
|
||||||
|
: DateTime(0);
|
||||||
lessonIndex = json["Ora"]["Oraszam"];
|
lessonIndex = json["Ora"]["Oraszam"];
|
||||||
} else {
|
} else {
|
||||||
lessonStart = DateTime(0);
|
lessonStart = DateTime(0);
|
||||||
@ -51,23 +56,30 @@ class Absence {
|
|||||||
|
|
||||||
return Absence(
|
return Absence(
|
||||||
id: json["Uid"],
|
id: json["Uid"],
|
||||||
date: json["Datum"] != null ? DateTime.parse(json["Datum"]).toLocal() : DateTime(0),
|
date: json["Datum"] != null
|
||||||
|
? DateTime.parse(json["Datum"]).toLocal()
|
||||||
|
: DateTime(0),
|
||||||
delay: json["KesesPercben"] ?? 0,
|
delay: json["KesesPercben"] ?? 0,
|
||||||
submitDate: json["KeszitesDatuma"] != null ? DateTime.parse(json["KeszitesDatuma"]).toLocal() : DateTime(0),
|
submitDate: json["KeszitesDatuma"] != null
|
||||||
teacher: (json["RogzitoTanarNeve"] ?? "").trim(),
|
? DateTime.parse(json["KeszitesDatuma"]).toLocal()
|
||||||
|
: DateTime(0),
|
||||||
|
teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()),
|
||||||
state: json["IgazolasAllapota"] == "Igazolt"
|
state: json["IgazolasAllapota"] == "Igazolt"
|
||||||
? Justification.excused
|
? Justification.excused
|
||||||
: json["IgazolasAllapota"] == "Igazolando"
|
: json["IgazolasAllapota"] == "Igazolando"
|
||||||
? Justification.pending
|
? Justification.pending
|
||||||
: Justification.unexcused,
|
: Justification.unexcused,
|
||||||
justification: json["IgazolasTipusa"] != null ? Category.fromJson(json["IgazolasTipusa"]) : null,
|
justification: json["IgazolasTipusa"] != null
|
||||||
|
? Category.fromJson(json["IgazolasTipusa"])
|
||||||
|
: 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: Subject.fromJson(json["Tantargy"] ?? {}),
|
||||||
lessonStart: lessonStart,
|
lessonStart: lessonStart,
|
||||||
lessonEnd: lessonEnd,
|
lessonEnd: lessonEnd,
|
||||||
lessonIndex: lessonIndex,
|
lessonIndex: lessonIndex,
|
||||||
group: json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] : "",
|
group:
|
||||||
|
json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] : "",
|
||||||
json: json,
|
json: json,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'category.dart';
|
import 'category.dart';
|
||||||
|
import 'teacher.dart';
|
||||||
|
|
||||||
class Exam {
|
class Exam {
|
||||||
Map? json;
|
Map? json;
|
||||||
@ -7,7 +8,7 @@ class Exam {
|
|||||||
Category? mode;
|
Category? mode;
|
||||||
int? subjectIndex;
|
int? subjectIndex;
|
||||||
String subjectName;
|
String subjectName;
|
||||||
String teacher;
|
Teacher teacher;
|
||||||
String description;
|
String description;
|
||||||
String group;
|
String group;
|
||||||
String id;
|
String id;
|
||||||
@ -28,14 +29,20 @@ class Exam {
|
|||||||
factory Exam.fromJson(Map json) {
|
factory Exam.fromJson(Map json) {
|
||||||
return Exam(
|
return Exam(
|
||||||
id: json["Uid"] ?? "",
|
id: json["Uid"] ?? "",
|
||||||
date: json["BejelentesDatuma"] != null ? DateTime.parse(json["BejelentesDatuma"]).toLocal() : DateTime(0),
|
date: json["BejelentesDatuma"] != null
|
||||||
writeDate: json["Datum"] != null ? DateTime.parse(json["Datum"]).toLocal() : DateTime(0),
|
? DateTime.parse(json["BejelentesDatuma"]).toLocal()
|
||||||
|
: DateTime(0),
|
||||||
|
writeDate: json["Datum"] != null
|
||||||
|
? DateTime.parse(json["Datum"]).toLocal()
|
||||||
|
: 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"] ?? "",
|
||||||
teacher: (json["RogzitoTanarNeve"] ?? "").trim(),
|
teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()),
|
||||||
description: (json["Temaja"] ?? "").trim(),
|
description: (json["Temaja"] ?? "").trim(),
|
||||||
group: json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] ?? "" : "",
|
group: json["OsztalyCsoport"] != null
|
||||||
|
? json["OsztalyCsoport"]["Uid"] ?? ""
|
||||||
|
: "",
|
||||||
json: json,
|
json: json,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import 'package:filcnaplo/utils/format.dart';
|
import 'package:filcnaplo/utils/format.dart';
|
||||||
import 'category.dart';
|
import 'category.dart';
|
||||||
import 'subject.dart';
|
import 'subject.dart';
|
||||||
|
import 'teacher.dart';
|
||||||
|
|
||||||
class Grade {
|
class Grade {
|
||||||
Map? json;
|
Map? json;
|
||||||
String id;
|
String id;
|
||||||
DateTime date;
|
DateTime date;
|
||||||
GradeValue value;
|
GradeValue value;
|
||||||
String teacher;
|
Teacher teacher;
|
||||||
String description;
|
String description;
|
||||||
GradeType type;
|
GradeType type;
|
||||||
String groupId;
|
String groupId;
|
||||||
@ -38,23 +39,35 @@ class Grade {
|
|||||||
factory Grade.fromJson(Map json) {
|
factory Grade.fromJson(Map json) {
|
||||||
return Grade(
|
return Grade(
|
||||||
id: json["Uid"] ?? "",
|
id: json["Uid"] ?? "",
|
||||||
date: json["KeszitesDatuma"] != null ? DateTime.parse(json["KeszitesDatuma"]).toLocal() : DateTime(0),
|
date: json["KeszitesDatuma"] != null
|
||||||
|
? DateTime.parse(json["KeszitesDatuma"]).toLocal()
|
||||||
|
: DateTime(0),
|
||||||
value: GradeValue(
|
value: GradeValue(
|
||||||
json["SzamErtek"] ?? 0,
|
json["SzamErtek"] ?? 0,
|
||||||
json["SzovegesErtek"] ?? "",
|
json["SzovegesErtek"] ?? "",
|
||||||
json["SzovegesErtekelesRovidNev"] ?? "",
|
json["SzovegesErtekelesRovidNev"] ?? "",
|
||||||
json["SulySzazalekErteke"] ?? 0,
|
json["SulySzazalekErteke"] ?? 0,
|
||||||
percentage: json["ErtekFajta"] != null ? json["ErtekFajta"]["Uid"] == "3,Szazalekos" : false,
|
percentage: json["ErtekFajta"] != null
|
||||||
|
? json["ErtekFajta"]["Uid"] == "3,Szazalekos"
|
||||||
|
: false,
|
||||||
),
|
),
|
||||||
teacher: (json["ErtekeloTanarNeve"] ?? "").trim(),
|
teacher: Teacher.fromString((json["ErtekeloTanarNeve"] ?? "").trim()),
|
||||||
description: json["Tema"] ?? "",
|
description: json["Tema"] ?? "",
|
||||||
type: json["Tipus"] != null ? Category.getGradeType(json["Tipus"]["Nev"]) : GradeType.unknown,
|
type: json["Tipus"] != null
|
||||||
|
? Category.getGradeType(json["Tipus"]["Nev"])
|
||||||
|
: GradeType.unknown,
|
||||||
groupId: (json["OsztalyCsoport"] ?? {})["Uid"] ?? "",
|
groupId: (json["OsztalyCsoport"] ?? {})["Uid"] ?? "",
|
||||||
subject: Subject.fromJson(json["Tantargy"] ?? {}),
|
subject: Subject.fromJson(json["Tantargy"] ?? {}),
|
||||||
gradeType: json["ErtekFajta"] != null ? Category.fromJson(json["ErtekFajta"]) : null,
|
gradeType: json["ErtekFajta"] != null
|
||||||
|
? Category.fromJson(json["ErtekFajta"])
|
||||||
|
: null,
|
||||||
mode: Category.fromJson(json["Mod"] ?? {}),
|
mode: Category.fromJson(json["Mod"] ?? {}),
|
||||||
writeDate: json["RogzitesDatuma"] != null ? DateTime.parse(json["RogzitesDatuma"]).toLocal() : DateTime(0),
|
writeDate: json["RogzitesDatuma"] != null
|
||||||
seenDate: json["LattamozasDatuma"] != null ? DateTime.parse(json["LattamozasDatuma"]).toLocal() : DateTime(0),
|
? DateTime.parse(json["RogzitesDatuma"]).toLocal()
|
||||||
|
: DateTime(0),
|
||||||
|
seenDate: json["LattamozasDatuma"] != null
|
||||||
|
? DateTime.parse(json["LattamozasDatuma"]).toLocal()
|
||||||
|
: DateTime(0),
|
||||||
form: (json["Jelleg"] ?? "Na") != "Na" ? json["Jelleg"] : "",
|
form: (json["Jelleg"] ?? "Na") != "Na" ? json["Jelleg"] : "",
|
||||||
json: json,
|
json: json,
|
||||||
);
|
);
|
||||||
@ -76,7 +89,8 @@ class GradeValue {
|
|||||||
set value(int v) => _value = v;
|
set value(int v) => _value = v;
|
||||||
int get value {
|
int get value {
|
||||||
String _valueName = valueName.toLowerCase().specialChars();
|
String _valueName = valueName.toLowerCase().specialChars();
|
||||||
if (_value == 0 && ["peldas", "jo", "valtozo", "rossz", "hanyag"].contains(_valueName)) {
|
if (_value == 0 &&
|
||||||
|
["peldas", "jo", "valtozo", "rossz", "hanyag"].contains(_valueName)) {
|
||||||
switch (_valueName) {
|
switch (_valueName) {
|
||||||
case "peldas":
|
case "peldas":
|
||||||
return 5;
|
return 5;
|
||||||
@ -87,7 +101,16 @@ class GradeValue {
|
|||||||
case "rossz":
|
case "rossz":
|
||||||
return 2;
|
return 2;
|
||||||
case "hanyag":
|
case "hanyag":
|
||||||
|
return 1;
|
||||||
|
// other
|
||||||
|
case "jeles":
|
||||||
|
return 5;
|
||||||
|
case "kozepes":
|
||||||
|
return 3;
|
||||||
|
case "elegseges":
|
||||||
return 2;
|
return 2;
|
||||||
|
case "elegtelen":
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _value;
|
return _value;
|
||||||
@ -101,7 +124,8 @@ class GradeValue {
|
|||||||
set weight(int v) => _weight = v;
|
set weight(int v) => _weight = v;
|
||||||
int get weight {
|
int get weight {
|
||||||
String _valueName = valueName.toLowerCase().specialChars();
|
String _valueName = valueName.toLowerCase().specialChars();
|
||||||
if (_value == 0 && ["peldas", "jo", "valtozo", "rossz", "hanyag"].contains(_valueName)) {
|
if (_value == 0 &&
|
||||||
|
["peldas", "jo", "valtozo", "rossz", "hanyag"].contains(_valueName)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return _weight;
|
return _weight;
|
||||||
@ -110,11 +134,23 @@ class GradeValue {
|
|||||||
final bool _percentage;
|
final bool _percentage;
|
||||||
bool get percentage => _percentage;
|
bool get percentage => _percentage;
|
||||||
|
|
||||||
GradeValue(int value, String valueName, this.shortName, int weight, {bool percentage = false})
|
GradeValue(int value, String valueName, this.shortName, int weight,
|
||||||
|
{bool percentage = false})
|
||||||
: _value = value,
|
: _value = value,
|
||||||
_valueName = valueName,
|
_valueName = valueName,
|
||||||
_weight = weight,
|
_weight = weight,
|
||||||
_percentage = percentage;
|
_percentage = percentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GradeType { midYear, firstQ, secondQ, halfYear, thirdQ, fourthQ, endYear, levelExam, ghost, unknown }
|
enum GradeType {
|
||||||
|
midYear,
|
||||||
|
firstQ,
|
||||||
|
secondQ,
|
||||||
|
halfYear,
|
||||||
|
thirdQ,
|
||||||
|
fourthQ,
|
||||||
|
endYear,
|
||||||
|
levelExam,
|
||||||
|
ghost,
|
||||||
|
unknown
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:filcnaplo_kreta_api/client/api.dart';
|
import 'package:filcnaplo_kreta_api/client/api.dart';
|
||||||
|
|
||||||
import 'subject.dart';
|
import 'subject.dart';
|
||||||
|
import 'teacher.dart';
|
||||||
|
|
||||||
class Homework {
|
class Homework {
|
||||||
Map? json;
|
Map? json;
|
||||||
@ -9,7 +10,7 @@ class Homework {
|
|||||||
DateTime deadline;
|
DateTime deadline;
|
||||||
bool byTeacher;
|
bool byTeacher;
|
||||||
bool homeworkEnabled;
|
bool homeworkEnabled;
|
||||||
String teacher;
|
Teacher teacher;
|
||||||
String content;
|
String content;
|
||||||
Subject subject;
|
Subject subject;
|
||||||
String group;
|
String group;
|
||||||
@ -45,7 +46,7 @@ class Homework {
|
|||||||
: DateTime(0),
|
: DateTime(0),
|
||||||
byTeacher: json["IsTanarRogzitette"] ?? true,
|
byTeacher: json["IsTanarRogzitette"] ?? true,
|
||||||
homeworkEnabled: json["IsTanuloHaziFeladatEnabled"] ?? false,
|
homeworkEnabled: json["IsTanuloHaziFeladatEnabled"] ?? false,
|
||||||
teacher: (json["RogzitoTanarNeve"] ?? "").trim(),
|
teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()),
|
||||||
content: (json["Szoveg"] ?? "").trim(),
|
content: (json["Szoveg"] ?? "").trim(),
|
||||||
subject: Subject.fromJson(json["Tantargy"] ?? {}),
|
subject: Subject.fromJson(json["Tantargy"] ?? {}),
|
||||||
group: json["OsztalyCsoport"] != null
|
group: json["OsztalyCsoport"] != null
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'subject.dart';
|
import 'subject.dart';
|
||||||
import 'category.dart';
|
import 'category.dart';
|
||||||
|
import 'teacher.dart';
|
||||||
|
|
||||||
class Lesson {
|
class Lesson {
|
||||||
Map? json;
|
Map? json;
|
||||||
@ -8,8 +9,8 @@ class Lesson {
|
|||||||
Subject subject;
|
Subject subject;
|
||||||
String lessonIndex;
|
String lessonIndex;
|
||||||
int? lessonYearIndex;
|
int? lessonYearIndex;
|
||||||
String substituteTeacher;
|
Teacher? substituteTeacher;
|
||||||
String teacher;
|
Teacher teacher;
|
||||||
bool homeworkEnabled;
|
bool homeworkEnabled;
|
||||||
DateTime start;
|
DateTime start;
|
||||||
DateTime end;
|
DateTime end;
|
||||||
@ -31,7 +32,7 @@ class Lesson {
|
|||||||
required this.subject,
|
required this.subject,
|
||||||
required this.lessonIndex,
|
required this.lessonIndex,
|
||||||
this.lessonYearIndex,
|
this.lessonYearIndex,
|
||||||
this.substituteTeacher = "",
|
this.substituteTeacher,
|
||||||
required this.teacher,
|
required this.teacher,
|
||||||
this.homeworkEnabled = false,
|
this.homeworkEnabled = false,
|
||||||
required this.start,
|
required this.start,
|
||||||
@ -53,27 +54,38 @@ class Lesson {
|
|||||||
factory Lesson.fromJson(Map json) {
|
factory Lesson.fromJson(Map json) {
|
||||||
return Lesson(
|
return Lesson(
|
||||||
id: json["Uid"] ?? "",
|
id: json["Uid"] ?? "",
|
||||||
status: json["Allapot"] != null ? Category.fromJson(json["Allapot"]) : null,
|
status:
|
||||||
date: json["Datum"] != null ? DateTime.parse(json["Datum"]).toLocal() : DateTime(0),
|
json["Allapot"] != null ? Category.fromJson(json["Allapot"]) : null,
|
||||||
|
date: json["Datum"] != null
|
||||||
|
? DateTime.parse(json["Datum"]).toLocal()
|
||||||
|
: DateTime(0),
|
||||||
subject: Subject.fromJson(json["Tantargy"] ?? {}),
|
subject: Subject.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"] ?? "").trim(),
|
substituteTeacher:
|
||||||
teacher: (json["TanarNeve"] ?? "").trim(),
|
Teacher.fromString((json["HelyettesTanarNeve"] ?? "").trim()),
|
||||||
|
teacher: Teacher.fromString((json["TanarNeve"] ?? "").trim()),
|
||||||
homeworkEnabled: json["IsTanuloHaziFeladatEnabled"] ?? false,
|
homeworkEnabled: json["IsTanuloHaziFeladatEnabled"] ?? false,
|
||||||
start: json["KezdetIdopont"] != null ? DateTime.parse(json["KezdetIdopont"]).toLocal() : DateTime(0),
|
start: json["KezdetIdopont"] != null
|
||||||
|
? DateTime.parse(json["KezdetIdopont"]).toLocal()
|
||||||
|
: DateTime(0),
|
||||||
studentPresence: json["TanuloJelenlet"] != null
|
studentPresence: json["TanuloJelenlet"] != null
|
||||||
? (json["TanuloJelenlet"]["Nev"] ?? "") == "Hianyzas"
|
? (json["TanuloJelenlet"]["Nev"] ?? "") == "Hianyzas"
|
||||||
? false
|
? false
|
||||||
: true
|
: true
|
||||||
: true,
|
: true,
|
||||||
end: json["VegIdopont"] != null ? DateTime.parse(json["VegIdopont"]).toLocal() : DateTime(0),
|
end: json["VegIdopont"] != null
|
||||||
|
? DateTime.parse(json["VegIdopont"]).toLocal()
|
||||||
|
: DateTime(0),
|
||||||
homeworkId: json["HaziFeladatUid"] ?? "",
|
homeworkId: json["HaziFeladatUid"] ?? "",
|
||||||
exam: json["BejelentettSzamonkeresUid"] ?? "",
|
exam: json["BejelentettSzamonkeresUid"] ?? "",
|
||||||
type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null,
|
type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null,
|
||||||
description: json["Tema"] ?? "",
|
description: json["Tema"] ?? "",
|
||||||
room: ((json["TeremNeve"] ?? "").split("_").join(" ") as String).replaceAll(RegExp(r" ?terem ?", caseSensitive: false), ""),
|
room: ((json["TeremNeve"] ?? "").split("_").join(" ") as String)
|
||||||
groupName: json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Nev"] ?? "" : "",
|
.replaceAll(RegExp(r" ?terem ?", caseSensitive: false), ""),
|
||||||
|
groupName: json["OsztalyCsoport"] != null
|
||||||
|
? json["OsztalyCsoport"]["Nev"] ?? ""
|
||||||
|
: "",
|
||||||
name: json["Nev"] ?? "",
|
name: json["Nev"] ?? "",
|
||||||
online: json["IsDigitalisOra"] ?? false,
|
online: json["IsDigitalisOra"] ?? false,
|
||||||
isEmpty: json['isEmpty'] ?? false,
|
isEmpty: json['isEmpty'] ?? false,
|
||||||
@ -92,6 +104,6 @@ class Lesson {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isChanged => status?.name == "Elmaradt" || substituteTeacher != "";
|
bool get isChanged => status?.name == "Elmaradt" || substituteTeacher != null;
|
||||||
bool get swapDesc => room.length > 8;
|
bool get swapDesc => room.length > 8;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'category.dart';
|
import 'category.dart';
|
||||||
|
import 'teacher.dart';
|
||||||
|
|
||||||
class Note {
|
class Note {
|
||||||
Map? json;
|
Map? json;
|
||||||
@ -6,7 +7,7 @@ class Note {
|
|||||||
String title;
|
String title;
|
||||||
DateTime date;
|
DateTime date;
|
||||||
DateTime submitDate;
|
DateTime submitDate;
|
||||||
String teacher;
|
Teacher teacher;
|
||||||
DateTime seenDate;
|
DateTime seenDate;
|
||||||
String groupId;
|
String groupId;
|
||||||
String content;
|
String content;
|
||||||
@ -29,11 +30,19 @@ class Note {
|
|||||||
return Note(
|
return Note(
|
||||||
id: json["Uid"] ?? "",
|
id: json["Uid"] ?? "",
|
||||||
title: json["Cim"] ?? "",
|
title: json["Cim"] ?? "",
|
||||||
date: json["Datum"] != null ? DateTime.parse(json["Datum"]).toLocal() : DateTime(0),
|
date: json["Datum"] != null
|
||||||
submitDate: json["KeszitesDatuma"] != null ? DateTime.parse(json["KeszitesDatuma"]).toLocal() : DateTime(0),
|
? DateTime.parse(json["Datum"]).toLocal()
|
||||||
teacher: (json["KeszitoTanarNeve"] ?? "").trim(),
|
: DateTime(0),
|
||||||
seenDate: json["LattamozasDatuma"] != null ? DateTime.parse(json["LattamozasDatuma"]).toLocal() : DateTime(0),
|
submitDate: json["KeszitesDatuma"] != null
|
||||||
groupId: json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] ?? "" : "",
|
? DateTime.parse(json["KeszitesDatuma"]).toLocal()
|
||||||
|
: DateTime(0),
|
||||||
|
teacher: Teacher.fromString((json["KeszitoTanarNeve"] ?? "").trim()),
|
||||||
|
seenDate: json["LattamozasDatuma"] != null
|
||||||
|
? DateTime.parse(json["LattamozasDatuma"]).toLocal()
|
||||||
|
: DateTime(0),
|
||||||
|
groupId: json["OsztalyCsoport"] != null
|
||||||
|
? json["OsztalyCsoport"]["Uid"] ?? ""
|
||||||
|
: "",
|
||||||
content: json["Tartalom"].replaceAll("\r", "") ?? "",
|
content: json["Tartalom"].replaceAll("\r", "") ?? "",
|
||||||
type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null,
|
type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null,
|
||||||
json: json,
|
json: json,
|
||||||
|
34
filcnaplo_kreta_api/lib/models/teacher.dart
Normal file
34
filcnaplo_kreta_api/lib/models/teacher.dart
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import 'package:filcnaplo/utils/format.dart';
|
||||||
|
|
||||||
|
class Teacher {
|
||||||
|
String id;
|
||||||
|
String name;
|
||||||
|
String? renamedTo;
|
||||||
|
|
||||||
|
bool get isRenamed => renamedTo != null;
|
||||||
|
|
||||||
|
Teacher({required this.id, required this.name, this.renamedTo});
|
||||||
|
|
||||||
|
factory Teacher.fromJson(Map json) {
|
||||||
|
return Teacher(
|
||||||
|
id: json["Uid"] ?? "",
|
||||||
|
name: (json["Nev"] ?? "").trim(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Teacher.fromString(String string) {
|
||||||
|
return Teacher(
|
||||||
|
id: string.trim().replaceAll(' ', '').toLowerCase().specialChars(),
|
||||||
|
name: string.trim(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) {
|
||||||
|
if (other is! Teacher) return false;
|
||||||
|
return id == other.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => id.hashCode;
|
||||||
|
}
|
@ -27,7 +27,10 @@ class AbsenceProvider with ChangeNotifier {
|
|||||||
|
|
||||||
// Load absences from the database
|
// Load absences from the database
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
var dbAbsences = await Provider.of<DatabaseProvider>(_context, listen: false).userQuery.getAbsences(userId: userId);
|
var dbAbsences =
|
||||||
|
await Provider.of<DatabaseProvider>(_context, listen: false)
|
||||||
|
.userQuery
|
||||||
|
.getAbsences(userId: userId);
|
||||||
_absences = dbAbsences;
|
_absences = dbAbsences;
|
||||||
await convertBySettings();
|
await convertBySettings();
|
||||||
}
|
}
|
||||||
@ -36,12 +39,26 @@ class AbsenceProvider with ChangeNotifier {
|
|||||||
// for renamed subjects
|
// for renamed subjects
|
||||||
Future<void> convertBySettings() async {
|
Future<void> convertBySettings() async {
|
||||||
final _database = Provider.of<DatabaseProvider>(_context, listen: false);
|
final _database = Provider.of<DatabaseProvider>(_context, listen: false);
|
||||||
Map<String, String> renamedSubjects = (await _database.query.getSettings(_database)).renamedSubjectsEnabled
|
Map<String, String> renamedSubjects =
|
||||||
? await _database.userQuery.renamedSubjects(userId: Provider.of<UserProvider>(_context, listen: false).user!.id)
|
(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 (Absence absence in _absences) {
|
for (Absence absence in _absences) {
|
||||||
absence.subject.renamedTo = renamedSubjects.isNotEmpty ? renamedSubjects[absence.subject.id] : null;
|
absence.subject.renamedTo = renamedSubjects.isNotEmpty
|
||||||
|
? renamedSubjects[absence.subject.id]
|
||||||
|
: null;
|
||||||
|
absence.teacher.renamedTo = renamedTeachers.isNotEmpty
|
||||||
|
? renamedTeachers[absence.teacher.id]
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -53,9 +70,11 @@ class AbsenceProvider with ChangeNotifier {
|
|||||||
if (user == null) throw "Cannot fetch Absences for User null";
|
if (user == null) throw "Cannot fetch Absences for User null";
|
||||||
String iss = user.instituteCode;
|
String iss = user.instituteCode;
|
||||||
|
|
||||||
List? absencesJson = await Provider.of<KretaClient>(_context, listen: false).getAPI(KretaAPI.absences(iss));
|
List? absencesJson = await Provider.of<KretaClient>(_context, listen: false)
|
||||||
|
.getAPI(KretaAPI.absences(iss));
|
||||||
if (absencesJson == null) throw "Cannot fetch Absences for User ${user.id}";
|
if (absencesJson == null) throw "Cannot fetch Absences for User ${user.id}";
|
||||||
List<Absence> absences = absencesJson.map((e) => Absence.fromJson(e)).toList();
|
List<Absence> absences =
|
||||||
|
absencesJson.map((e) => Absence.fromJson(e)).toList();
|
||||||
|
|
||||||
if (absences.isNotEmpty || _absences.isNotEmpty) await store(absences);
|
if (absences.isNotEmpty || _absences.isNotEmpty) await store(absences);
|
||||||
}
|
}
|
||||||
@ -66,7 +85,9 @@ class AbsenceProvider with ChangeNotifier {
|
|||||||
if (user == null) throw "Cannot store Absences for User null";
|
if (user == null) throw "Cannot store Absences for User null";
|
||||||
String userId = user.id;
|
String userId = user.id;
|
||||||
|
|
||||||
await Provider.of<DatabaseProvider>(_context, listen: false).userStore.storeAbsences(absences, userId: userId);
|
await Provider.of<DatabaseProvider>(_context, listen: false)
|
||||||
|
.userStore
|
||||||
|
.storeAbsences(absences, userId: userId);
|
||||||
_absences = absences;
|
_absences = absences;
|
||||||
await convertBySettings();
|
await convertBySettings();
|
||||||
}
|
}
|
||||||
|
@ -83,10 +83,15 @@ class GradeProvider with ChangeNotifier {
|
|||||||
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)
|
||||||
: {};
|
: {};
|
||||||
|
Map<String, String> renamedTeachers = _settings.renamedTeachersEnabled
|
||||||
|
? await _database.userQuery.renamedTeachers(userId: _user.user!.id)
|
||||||
|
: {};
|
||||||
|
|
||||||
for (Grade grade in _grades) {
|
for (Grade grade in _grades) {
|
||||||
grade.subject.renamedTo =
|
grade.subject.renamedTo =
|
||||||
renamedSubjects.isNotEmpty ? renamedSubjects[grade.subject.id] : null;
|
renamedSubjects.isNotEmpty ? renamedSubjects[grade.subject.id] : null;
|
||||||
|
grade.teacher.renamedTo =
|
||||||
|
renamedTeachers.isNotEmpty ? renamedTeachers[grade.teacher.id] : null;
|
||||||
|
|
||||||
grade.value.value =
|
grade.value.value =
|
||||||
_settings.goodStudent ? 5 : grade.json!["SzamErtek"] ?? 0;
|
_settings.goodStudent ? 5 : grade.json!["SzamErtek"] ?? 0;
|
||||||
|
@ -48,11 +48,18 @@ class HomeworkProvider with ChangeNotifier {
|
|||||||
(await _database.query.getSettings(_database)).renamedSubjectsEnabled
|
(await _database.query.getSettings(_database)).renamedSubjectsEnabled
|
||||||
? await _database.userQuery.renamedSubjects(userId: _user.id!)
|
? await _database.userQuery.renamedSubjects(userId: _user.id!)
|
||||||
: {};
|
: {};
|
||||||
|
Map<String, String> renamedTeachers =
|
||||||
|
(await _database.query.getSettings(_database)).renamedTeachersEnabled
|
||||||
|
? await _database.userQuery.renamedTeachers(userId: _user.id!)
|
||||||
|
: {};
|
||||||
|
|
||||||
for (Homework homework in _homework) {
|
for (Homework homework in _homework) {
|
||||||
homework.subject.renamedTo = renamedSubjects.isNotEmpty
|
homework.subject.renamedTo = renamedSubjects.isNotEmpty
|
||||||
? renamedSubjects[homework.subject.id]
|
? renamedSubjects[homework.subject.id]
|
||||||
: null;
|
: null;
|
||||||
|
homework.teacher.renamedTo = renamedTeachers.isNotEmpty
|
||||||
|
? renamedTeachers[homework.teacher.id]
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
@ -40,11 +40,18 @@ class TimetableProvider with ChangeNotifier {
|
|||||||
(await _database.query.getSettings(_database)).renamedSubjectsEnabled
|
(await _database.query.getSettings(_database)).renamedSubjectsEnabled
|
||||||
? await _database.userQuery.renamedSubjects(userId: _user.id!)
|
? await _database.userQuery.renamedSubjects(userId: _user.id!)
|
||||||
: {};
|
: {};
|
||||||
|
Map<String, String> renamedTeachers =
|
||||||
|
(await _database.query.getSettings(_database)).renamedTeachersEnabled
|
||||||
|
? await _database.userQuery.renamedTeachers(userId: _user.id!)
|
||||||
|
: {};
|
||||||
|
|
||||||
for (Lesson lesson in _lessons.values.expand((e) => e)) {
|
for (Lesson lesson in _lessons.values.expand((e) => e)) {
|
||||||
lesson.subject.renamedTo = renamedSubjects.isNotEmpty
|
lesson.subject.renamedTo = renamedSubjects.isNotEmpty
|
||||||
? renamedSubjects[lesson.subject.id]
|
? renamedSubjects[lesson.subject.id]
|
||||||
: null;
|
: null;
|
||||||
|
lesson.teacher.renamedTo = renamedTeachers.isNotEmpty
|
||||||
|
? renamedTeachers[lesson.teacher.id]
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
41
filcnaplo_mobile_ui/lib/common/beta_chip.dart
Normal file
41
filcnaplo_mobile_ui/lib/common/beta_chip.dart
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class BetaChip extends StatelessWidget {
|
||||||
|
const BetaChip({Key? key, this.disabled = false}) : super(key: key);
|
||||||
|
|
||||||
|
final bool disabled;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 25,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8, right: 8),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
"BETA",
|
||||||
|
softWrap: true,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: disabled
|
||||||
|
? AppColors.of(context).text.withOpacity(.5)
|
||||||
|
: Colors.white,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: !disabled
|
||||||
|
? Theme.of(context).colorScheme.secondary
|
||||||
|
: AppColors.of(context).text.withOpacity(.25),
|
||||||
|
borderRadius: BorderRadius.circular(40),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,9 @@ import 'package:provider/provider.dart';
|
|||||||
import 'absence_tile.i18n.dart';
|
import 'absence_tile.i18n.dart';
|
||||||
|
|
||||||
class AbsenceTile extends StatelessWidget {
|
class AbsenceTile extends StatelessWidget {
|
||||||
const AbsenceTile(this.absence, {Key? key, this.onTap, this.elevation = 0.0, this.padding}) : super(key: key);
|
const AbsenceTile(this.absence,
|
||||||
|
{Key? key, this.onTap, this.elevation = 0.0, this.padding})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
final Absence absence;
|
final Absence absence;
|
||||||
final void Function()? onTap;
|
final void Function()? onTap;
|
||||||
@ -37,39 +39,61 @@ class AbsenceTile extends StatelessWidget {
|
|||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: padding ?? (group ? EdgeInsets.zero : const EdgeInsets.symmetric(horizontal: 8.0)),
|
padding: padding ??
|
||||||
|
(group
|
||||||
|
? EdgeInsets.zero
|
||||||
|
: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8.0,
|
||||||
|
)),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
dense: group,
|
dense: group,
|
||||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
contentPadding: const EdgeInsets.only(
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(!group ? 14.0 : 12.0)),
|
left: 14.0, right: 12.0, top: 2.0, bottom: 2.0),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(!group ? 14.0 : 12.0)),
|
||||||
leading: Container(
|
leading: Container(
|
||||||
width: 44.0,
|
width: 44.0,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: !group ? color.withOpacity(.25) : null,
|
color: !group ? color.withOpacity(.25) : null,
|
||||||
),
|
),
|
||||||
child: Center(child: Icon(justificationIcon(absence.state), color: color)),
|
child: Center(
|
||||||
|
child: Icon(justificationIcon(absence.state), color: color)),
|
||||||
),
|
),
|
||||||
title: !group
|
title: !group
|
||||||
? Text.rich(TextSpan(
|
? Text.rich(TextSpan(
|
||||||
text: "${absence.delay == 0 ? "" : absence.delay}",
|
text: "${absence.delay == 0 ? "" : absence.delay}",
|
||||||
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 15.5),
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w700, fontSize: 15.5),
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: absence.delay == 0
|
text: absence.delay == 0
|
||||||
? justificationName(absence.state).fill(["absence".i18n]).capital()
|
? justificationName(absence.state)
|
||||||
: 'minute'.plural(absence.delay) + justificationName(absence.state).fill(["delay".i18n]),
|
.fill(["absence".i18n]).capital()
|
||||||
|
: 'minute'.plural(absence.delay) +
|
||||||
|
justificationName(absence.state)
|
||||||
|
.fill(["delay".i18n]),
|
||||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
: Text(
|
: Text(
|
||||||
(absence.lessonIndex != null ? "${absence.lessonIndex}. " : "") + (absence.subject.renamedTo ?? absence.subject.name.capital()),
|
(absence.lessonIndex != null
|
||||||
|
? "${absence.lessonIndex}. "
|
||||||
|
: "") +
|
||||||
|
(absence.subject.renamedTo ??
|
||||||
|
absence.subject.name.capital()),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 14.0, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 14.0,
|
||||||
|
fontStyle: absence.subject.isRenamed &&
|
||||||
|
settingsProvider.renamedSubjectsItalics
|
||||||
|
? FontStyle.italic
|
||||||
|
: null),
|
||||||
),
|
),
|
||||||
subtitle: !group
|
subtitle: !group
|
||||||
? Text(
|
? Text(
|
||||||
@ -77,7 +101,12 @@ class AbsenceTile extends StatelessWidget {
|
|||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
// DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date),
|
// DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date),
|
||||||
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontStyle: absence.subject.isRenamed &&
|
||||||
|
settingsProvider.renamedSubjectsItalics
|
||||||
|
? FontStyle.italic
|
||||||
|
: null),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
@ -97,7 +126,8 @@ class AbsenceTile extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Color justificationColor(Justification state, {required BuildContext context}) {
|
static Color justificationColor(Justification state,
|
||||||
|
{required BuildContext context}) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Justification.excused:
|
case Justification.excused:
|
||||||
return AppColors.of(context).green;
|
return AppColors.of(context).green;
|
||||||
|
@ -17,19 +17,23 @@ import 'package:provider/provider.dart';
|
|||||||
import 'absence_view.i18n.dart';
|
import 'absence_view.i18n.dart';
|
||||||
|
|
||||||
class AbsenceView extends StatelessWidget {
|
class AbsenceView extends StatelessWidget {
|
||||||
const AbsenceView(this.absence, {Key? key, this.outsideContext, this.viewable = false}) : super(key: key);
|
const AbsenceView(this.absence,
|
||||||
|
{Key? key, this.outsideContext, this.viewable = false})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
final Absence absence;
|
final Absence absence;
|
||||||
final BuildContext? outsideContext;
|
final BuildContext? outsideContext;
|
||||||
final bool viewable;
|
final bool viewable;
|
||||||
|
|
||||||
static show(Absence absence, {required BuildContext context}) {
|
static show(Absence absence, {required BuildContext context}) {
|
||||||
showBottomCard(context: context, child: AbsenceView(absence, outsideContext: context));
|
showBottomCard(
|
||||||
|
context: context, child: AbsenceView(absence, outsideContext: context));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color color = AbsenceTile.justificationColor(absence.state, context: context);
|
Color color =
|
||||||
|
AbsenceTile.justificationColor(absence.state, context: context);
|
||||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
@ -41,7 +45,8 @@ class AbsenceView extends StatelessWidget {
|
|||||||
ListTile(
|
ListTile(
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
contentPadding: const EdgeInsets.only(left: 16.0, right: 12.0),
|
contentPadding: const EdgeInsets.only(left: 16.0, right: 12.0),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8.0)),
|
||||||
leading: Container(
|
leading: Container(
|
||||||
width: 44.0,
|
width: 44.0,
|
||||||
height: 44.0,
|
height: 44.0,
|
||||||
@ -60,10 +65,18 @@ class AbsenceView extends StatelessWidget {
|
|||||||
absence.subject.renamedTo ?? absence.subject.name.capital(),
|
absence.subject.renamedTo ?? absence.subject.name.capital(),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(fontWeight: FontWeight.w700, fontStyle: absence.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
fontStyle: absence.subject.isRenamed &&
|
||||||
|
settingsProvider.renamedSubjectsItalics
|
||||||
|
? FontStyle.italic
|
||||||
|
: null),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
absence.teacher,
|
(absence.teacher.isRenamed
|
||||||
|
? absence.teacher.renamedTo
|
||||||
|
: absence.teacher.name) ??
|
||||||
|
'',
|
||||||
// DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date),
|
// DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date),
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
@ -77,12 +90,15 @@ class AbsenceView extends StatelessWidget {
|
|||||||
if (absence.delay > 0)
|
if (absence.delay > 0)
|
||||||
Detail(
|
Detail(
|
||||||
title: "delay".i18n,
|
title: "delay".i18n,
|
||||||
description: absence.delay.toString() + " " + "minutes".i18n.plural(absence.delay),
|
description: absence.delay.toString() +
|
||||||
|
" " +
|
||||||
|
"minutes".i18n.plural(absence.delay),
|
||||||
),
|
),
|
||||||
if (absence.lessonIndex != null)
|
if (absence.lessonIndex != null)
|
||||||
Detail(
|
Detail(
|
||||||
title: "Lesson".i18n,
|
title: "Lesson".i18n,
|
||||||
description: "${absence.lessonIndex}. (${absence.lessonStart.format(context, timeOnly: true)}"
|
description:
|
||||||
|
"${absence.lessonIndex}. (${absence.lessonStart.format(context, timeOnly: true)}"
|
||||||
" - "
|
" - "
|
||||||
"${absence.lessonEnd.format(context, timeOnly: true)})",
|
"${absence.lessonEnd.format(context, timeOnly: true)})",
|
||||||
),
|
),
|
||||||
@ -91,13 +107,19 @@ class AbsenceView extends StatelessWidget {
|
|||||||
title: "Excuse".i18n,
|
title: "Excuse".i18n,
|
||||||
description: absence.justification?.description ?? "",
|
description: absence.justification?.description ?? "",
|
||||||
),
|
),
|
||||||
if (absence.mode != null) Detail(title: "Mode".i18n, description: absence.mode?.description ?? ""),
|
if (absence.mode != null)
|
||||||
Detail(title: "Submit date".i18n, description: absence.submitDate.format(context)),
|
Detail(
|
||||||
|
title: "Mode".i18n,
|
||||||
|
description: absence.mode?.description ?? ""),
|
||||||
|
Detail(
|
||||||
|
title: "Submit date".i18n,
|
||||||
|
description: absence.submitDate.format(context)),
|
||||||
|
|
||||||
// Show in timetable
|
// Show in timetable
|
||||||
if (!viewable)
|
if (!viewable)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 6.0, top: 12.0),
|
padding: const EdgeInsets.only(
|
||||||
|
left: 16.0, right: 16.0, bottom: 6.0, top: 12.0),
|
||||||
child: PanelActionButton(
|
child: PanelActionButton(
|
||||||
leading: const Icon(FeatherIcons.calendar),
|
leading: const Icon(FeatherIcons.calendar),
|
||||||
title: Text(
|
title: Text(
|
||||||
@ -109,12 +131,15 @@ class AbsenceView extends StatelessWidget {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
if (outsideContext != null) {
|
if (outsideContext != null) {
|
||||||
ReverseSearch.getLessonByAbsence(absence, context).then((lesson) {
|
ReverseSearch.getLessonByAbsence(absence, context)
|
||||||
|
.then((lesson) {
|
||||||
if (lesson != null) {
|
if (lesson != null) {
|
||||||
TimetablePage.jump(outsideContext!, lesson: lesson);
|
TimetablePage.jump(outsideContext!, lesson: lesson);
|
||||||
} else {
|
} else {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
ScaffoldMessenger.of(context)
|
||||||
content: Text("Cannot find lesson".i18n, style: const TextStyle(color: Colors.white)),
|
.showSnackBar(CustomSnackBar(
|
||||||
|
content: Text("Cannot find lesson".i18n,
|
||||||
|
style: const TextStyle(color: Colors.white)),
|
||||||
backgroundColor: AppColors.of(context).red,
|
backgroundColor: AppColors.of(context).red,
|
||||||
context: context,
|
context: context,
|
||||||
));
|
));
|
||||||
|
@ -5,6 +5,8 @@ import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_c
|
|||||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_tile.dart';
|
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_tile.dart';
|
||||||
import 'package:filcnaplo/utils/format.dart';
|
import 'package:filcnaplo/utils/format.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||||
|
import 'package:rounded_expansion_tile/rounded_expansion_tile.dart';
|
||||||
import 'absence_group_tile.i18n.dart';
|
import 'absence_group_tile.i18n.dart';
|
||||||
import 'package:rounded_expansion_tile/rounded_expansion_tile.dart';
|
import 'package:rounded_expansion_tile/rounded_expansion_tile.dart';
|
||||||
|
|
||||||
@ -31,13 +33,17 @@ class AbsenceGroupTile extends StatelessWidget {
|
|||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
padding: padding ??
|
||||||
|
const EdgeInsets.symmetric(horizontal: 0.0, vertical: 0.0),
|
||||||
child: AbsenceGroupContainer(
|
child: AbsenceGroupContainer(
|
||||||
child: RoundedExpansionTile(
|
child: RoundedExpansionTile(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(10)),
|
borderRadius: BorderRadius.circular(10)),
|
||||||
tilePadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
childrenPadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
backgroundColor: Colors.transparent,
|
tileColor: Colors.transparent,
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
trailingDuration: 0.5,
|
||||||
|
trailing: const Icon(FeatherIcons.chevronDown),
|
||||||
leading: Container(
|
leading: Container(
|
||||||
width: 44.0,
|
width: 44.0,
|
||||||
height: 44.0,
|
height: 44.0,
|
||||||
|
@ -12,7 +12,8 @@ class ExamView extends StatelessWidget {
|
|||||||
|
|
||||||
final Exam exam;
|
final Exam exam;
|
||||||
|
|
||||||
static show(Exam exam, {required BuildContext context}) => showBottomCard(context: context, child: ExamView(exam));
|
static show(Exam exam, {required BuildContext context}) =>
|
||||||
|
showBottomCard(context: context, child: ExamView(exam));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -27,7 +28,8 @@ class ExamView extends StatelessWidget {
|
|||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: const EdgeInsets.only(left: 6.0),
|
padding: const EdgeInsets.only(left: 6.0),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
SubjectIcon.resolveVariant(subjectName: exam.subjectName, context: context),
|
SubjectIcon.resolveVariant(
|
||||||
|
subjectName: exam.subjectName, context: context),
|
||||||
size: 36.0,
|
size: 36.0,
|
||||||
color: AppColors.of(context).text.withOpacity(.75),
|
color: AppColors.of(context).text.withOpacity(.75),
|
||||||
),
|
),
|
||||||
@ -39,7 +41,10 @@ class ExamView extends StatelessWidget {
|
|||||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
exam.teacher,
|
(exam.teacher.isRenamed
|
||||||
|
? exam.teacher.renamedTo
|
||||||
|
: exam.teacher.name) ??
|
||||||
|
'',
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
@ -51,9 +56,14 @@ class ExamView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// Details
|
// Details
|
||||||
if (exam.writeDate.year != 0) Detail(title: "date".i18n, description: exam.writeDate.format(context)),
|
if (exam.writeDate.year != 0)
|
||||||
if (exam.description != "") Detail(title: "description".i18n, description: exam.description),
|
Detail(
|
||||||
if (exam.mode != null) Detail(title: "mode".i18n, description: exam.mode!.description),
|
title: "date".i18n,
|
||||||
|
description: exam.writeDate.format(context)),
|
||||||
|
if (exam.description != "")
|
||||||
|
Detail(title: "description".i18n, description: exam.description),
|
||||||
|
if (exam.mode != null)
|
||||||
|
Detail(title: "mode".i18n, description: exam.mode!.description),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -11,7 +11,8 @@ import 'grade_view.i18n.dart';
|
|||||||
class GradeView extends StatelessWidget {
|
class GradeView extends StatelessWidget {
|
||||||
const GradeView(this.grade, {Key? key}) : super(key: key);
|
const GradeView(this.grade, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
static show(Grade grade, {required BuildContext context}) => showBottomCard(context: context, child: GradeView(grade));
|
static show(Grade grade, {required BuildContext context}) =>
|
||||||
|
showBottomCard(context: context, child: GradeView(grade));
|
||||||
|
|
||||||
final Grade grade;
|
final Grade grade;
|
||||||
|
|
||||||
@ -30,10 +31,21 @@ class GradeView extends StatelessWidget {
|
|||||||
grade.subject.renamedTo ?? grade.subject.name.capital(),
|
grade.subject.renamedTo ?? grade.subject.name.capital(),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: grade.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontStyle: grade.subject.isRenamed &&
|
||||||
|
settingsProvider.renamedSubjectsItalics
|
||||||
|
? FontStyle.italic
|
||||||
|
: null),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
!Provider.of<SettingsProvider>(context, listen: false).presentationMode ? grade.teacher : "Tanár",
|
!Provider.of<SettingsProvider>(context, listen: false)
|
||||||
|
.presentationMode
|
||||||
|
? (grade.teacher.isRenamed
|
||||||
|
? grade.teacher.renamedTo
|
||||||
|
: grade.teacher.name) ??
|
||||||
|
''
|
||||||
|
: "Tanár",
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
@ -49,13 +61,20 @@ class GradeView extends StatelessWidget {
|
|||||||
title: "value".i18n,
|
title: "value".i18n,
|
||||||
description: "${grade.value.valueName} " + percentText(),
|
description: "${grade.value.valueName} " + percentText(),
|
||||||
),
|
),
|
||||||
if (grade.description != "") Detail(title: "description".i18n, description: grade.description),
|
if (grade.description != "")
|
||||||
if (grade.mode.description != "") Detail(title: "mode".i18n, description: grade.mode.description),
|
Detail(title: "description".i18n, description: grade.description),
|
||||||
if (grade.writeDate.year != 0) Detail(title: "date".i18n, description: grade.writeDate.format(context)),
|
if (grade.mode.description != "")
|
||||||
|
Detail(title: "mode".i18n, description: grade.mode.description),
|
||||||
|
if (grade.writeDate.year != 0)
|
||||||
|
Detail(
|
||||||
|
title: "date".i18n,
|
||||||
|
description: grade.writeDate.format(context)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String percentText() => grade.value.weight != 100 && grade.value.weight > 0 ? "${grade.value.weight}%" : "";
|
String percentText() => grade.value.weight != 100 && grade.value.weight > 0
|
||||||
|
? "${grade.value.weight}%"
|
||||||
|
: "";
|
||||||
}
|
}
|
||||||
|
@ -51,10 +51,18 @@ class HomeworkView extends StatelessWidget {
|
|||||||
homework.subject.renamedTo ?? homework.subject.name.capital(),
|
homework.subject.renamedTo ?? homework.subject.name.capital(),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
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: Text(
|
subtitle: Text(
|
||||||
homework.teacher,
|
(homework.teacher.isRenamed
|
||||||
|
? homework.teacher.renamedTo
|
||||||
|
: homework.teacher.name) ??
|
||||||
|
'',
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
|
@ -54,10 +54,23 @@ class LessonView extends StatelessWidget {
|
|||||||
lesson.subject.renamedTo ?? lesson.subject.name.capital(),
|
lesson.subject.renamedTo ?? lesson.subject.name.capital(),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed && settingsProvider.renamedSubjectsItalics ? FontStyle.italic : null),
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontStyle: lesson.subject.isRenamed &&
|
||||||
|
settingsProvider.renamedSubjectsItalics
|
||||||
|
? FontStyle.italic
|
||||||
|
: null),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
lesson.substituteTeacher == "" ? lesson.teacher : lesson.substituteTeacher,
|
((lesson.substituteTeacher == null ||
|
||||||
|
lesson.substituteTeacher!.name == "")
|
||||||
|
? (lesson.teacher.isRenamed
|
||||||
|
? lesson.teacher.renamedTo
|
||||||
|
: lesson.teacher.name)
|
||||||
|
: (lesson.substituteTeacher!.isRenamed
|
||||||
|
? lesson.substituteTeacher!.renamedTo
|
||||||
|
: lesson.substituteTeacher!.name)) ??
|
||||||
|
'',
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
@ -69,10 +82,18 @@ class LessonView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// Details
|
// Details
|
||||||
if (lesson.room != "") Detail(title: "Room".i18n, description: lesson.room.replaceAll("_", " ")),
|
if (lesson.room != "")
|
||||||
if (lesson.description != "") Detail(title: "Description".i18n, description: lesson.description),
|
Detail(
|
||||||
if (lesson.lessonYearIndex != null) Detail(title: "Lesson Number".i18n, description: "${lesson.lessonYearIndex}."),
|
title: "Room".i18n,
|
||||||
if (lesson.groupName != "") Detail(title: "Group".i18n, description: lesson.groupName),
|
description: lesson.room.replaceAll("_", " ")),
|
||||||
|
if (lesson.description != "")
|
||||||
|
Detail(title: "Description".i18n, description: lesson.description),
|
||||||
|
if (lesson.lessonYearIndex != null)
|
||||||
|
Detail(
|
||||||
|
title: "Lesson Number".i18n,
|
||||||
|
description: "${lesson.lessonYearIndex}."),
|
||||||
|
if (lesson.groupName != "")
|
||||||
|
Detail(title: "Group".i18n, description: lesson.groupName),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -4,7 +4,8 @@ import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class NoteTile extends StatelessWidget {
|
class NoteTile extends StatelessWidget {
|
||||||
const NoteTile(this.note, {Key? key, this.onTap, this.padding}) : super(key: key);
|
const NoteTile(this.note, {Key? key, this.onTap, this.padding})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
final Note note;
|
final Note note;
|
||||||
final void Function()? onTap;
|
final void Function()? onTap;
|
||||||
@ -20,11 +21,20 @@ class NoteTile 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: ProfileImage(
|
leading: ProfileImage(
|
||||||
name: note.teacher,
|
name: (note.teacher.isRenamed
|
||||||
|
? note.teacher.renamedTo
|
||||||
|
: note.teacher.name) ??
|
||||||
|
'',
|
||||||
radius: 22.0,
|
radius: 22.0,
|
||||||
backgroundColor: ColorUtils.stringToColor(note.teacher),
|
backgroundColor: ColorUtils.stringToColor(
|
||||||
|
(note.teacher.isRenamed
|
||||||
|
? note.teacher.renamedTo
|
||||||
|
: note.teacher.name) ??
|
||||||
|
'',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
note.title,
|
note.title,
|
||||||
|
@ -12,7 +12,8 @@ class NoteView extends StatelessWidget {
|
|||||||
|
|
||||||
final Note note;
|
final Note note;
|
||||||
|
|
||||||
static void show(Note note, {required BuildContext context}) => showSlidingBottomSheet(context: context, child: NoteView(note));
|
static void show(Note note, {required BuildContext context}) =>
|
||||||
|
showSlidingBottomSheet(context: context, child: NoteView(note));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -25,9 +26,17 @@ class NoteView extends StatelessWidget {
|
|||||||
// Header
|
// Header
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: ProfileImage(
|
leading: ProfileImage(
|
||||||
name: note.teacher,
|
name: (note.teacher.isRenamed
|
||||||
|
? note.teacher.renamedTo
|
||||||
|
: note.teacher.name) ??
|
||||||
|
'',
|
||||||
radius: 22.0,
|
radius: 22.0,
|
||||||
backgroundColor: ColorUtils.stringToColor(note.teacher),
|
backgroundColor: ColorUtils.stringToColor(
|
||||||
|
(note.teacher.isRenamed
|
||||||
|
? note.teacher.renamedTo
|
||||||
|
: note.teacher.name) ??
|
||||||
|
'',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
note.title,
|
note.title,
|
||||||
@ -36,7 +45,10 @@ class NoteView extends StatelessWidget {
|
|||||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
note.teacher,
|
(note.teacher.isRenamed
|
||||||
|
? note.teacher.renamedTo
|
||||||
|
: note.teacher.name) ??
|
||||||
|
'',
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
|
@ -19,14 +19,18 @@ import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_view.i18n.dar
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class AbsenceSubjectView extends StatelessWidget {
|
class AbsenceSubjectView extends StatelessWidget {
|
||||||
const AbsenceSubjectView(this.subject, {Key? key, this.absences = const []}) : super(key: key);
|
const AbsenceSubjectView(this.subject, {Key? key, this.absences = const []})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
final Subject subject;
|
final Subject subject;
|
||||||
final List<Absence> absences;
|
final List<Absence> absences;
|
||||||
|
|
||||||
static void show(Subject subject, List<Absence> absences, {required BuildContext context}) {
|
static void show(Subject subject, List<Absence> absences,
|
||||||
|
{required BuildContext context}) {
|
||||||
Navigator.of(context, rootNavigator: true)
|
Navigator.of(context, rootNavigator: true)
|
||||||
.push<Absence>(CupertinoPageRoute(builder: (context) => AbsenceSubjectView(subject, absences: absences)))
|
.push<Absence>(CupertinoPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
AbsenceSubjectView(subject, absences: absences)))
|
||||||
.then((value) {
|
.then((value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
|
|
||||||
@ -36,7 +40,8 @@ class AbsenceSubjectView extends StatelessWidget {
|
|||||||
TimetablePage.jump(context, lesson: lesson);
|
TimetablePage.jump(context, lesson: lesson);
|
||||||
} else {
|
} else {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
||||||
content: Text("Cannot find lesson".i18n, style: const TextStyle(color: Colors.white)),
|
content: Text("Cannot find lesson".i18n,
|
||||||
|
style: const TextStyle(color: Colors.white)),
|
||||||
backgroundColor: AppColors.of(context).red,
|
backgroundColor: AppColors.of(context).red,
|
||||||
context: context,
|
context: context,
|
||||||
));
|
));
|
||||||
@ -54,7 +59,10 @@ class AbsenceSubjectView extends StatelessWidget {
|
|||||||
date: a.date,
|
date: a.date,
|
||||||
))
|
))
|
||||||
.toList();
|
.toList();
|
||||||
List<Widget> absenceTiles = sortDateWidgets(context, dateWidgets: dateWidgets, padding: EdgeInsets.zero, hasShadow: true);
|
List<Widget> absenceTiles = sortDateWidgets(context,
|
||||||
|
dateWidgets: dateWidgets,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
||||||
|
hasShadow: true);
|
||||||
|
|
||||||
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import 'dart:math';
|
|||||||
import 'package:filcnaplo_kreta_api/models/category.dart';
|
import 'package:filcnaplo_kreta_api/models/category.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/models/teacher.dart';
|
||||||
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
|
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
|
||||||
import 'package:filcnaplo_mobile_ui/common/material_action_button.dart';
|
import 'package:filcnaplo_mobile_ui/common/material_action_button.dart';
|
||||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||||
@ -42,7 +43,8 @@ class _GradeCalculatorState extends State<GradeCalculator> {
|
|||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Grade Calculator".i18n,
|
"Grade Calculator".i18n,
|
||||||
style: const TextStyle(fontSize: 20.0, fontWeight: FontWeight.w600),
|
style:
|
||||||
|
const TextStyle(fontSize: 20.0, fontWeight: FontWeight.w600),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -63,7 +65,9 @@ class _GradeCalculatorState extends State<GradeCalculator> {
|
|||||||
Container(
|
Container(
|
||||||
width: 80.0,
|
width: 80.0,
|
||||||
padding: const EdgeInsets.only(right: 12.0),
|
padding: const EdgeInsets.only(right: 12.0),
|
||||||
child: Center(child: GradeValueWidget(GradeValue(newValue.toInt(), "", "", 0))),
|
child: Center(
|
||||||
|
child: GradeValueWidget(
|
||||||
|
GradeValue(newValue.toInt(), "", "", 0))),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -90,7 +94,8 @@ class _GradeCalculatorState extends State<GradeCalculator> {
|
|||||||
child: Center(
|
child: Center(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _weightController,
|
controller: _weightController,
|
||||||
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 22.0),
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w600, fontSize: 22.0),
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
@ -122,7 +127,8 @@ class _GradeCalculatorState extends State<GradeCalculator> {
|
|||||||
child: Text("Add Grade".i18n),
|
child: Text("Add Grade".i18n),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (calculatorProvider.ghosts.length >= 50) {
|
if (calculatorProvider.ghosts.length >= 50) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(content: Text("limit_reached".i18n), context: context));
|
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
||||||
|
content: Text("limit_reached".i18n), context: context));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +139,11 @@ class _GradeCalculatorState extends State<GradeCalculator> {
|
|||||||
grades.sort((a, b) => -a.writeDate.compareTo(b.writeDate));
|
grades.sort((a, b) => -a.writeDate.compareTo(b.writeDate));
|
||||||
date = grades.first.date.add(const Duration(days: 7));
|
date = grades.first.date.add(const Duration(days: 7));
|
||||||
} else {
|
} else {
|
||||||
List<Grade> grades = calculatorProvider.grades.where((e) => e.type == GradeType.midYear && e.subject == widget.subject).toList();
|
List<Grade> grades = calculatorProvider.grades
|
||||||
|
.where((e) =>
|
||||||
|
e.type == GradeType.midYear &&
|
||||||
|
e.subject == widget.subject)
|
||||||
|
.toList();
|
||||||
grades.sort((a, b) => -a.writeDate.compareTo(b.writeDate));
|
grades.sort((a, b) => -a.writeDate.compareTo(b.writeDate));
|
||||||
date = grades.first.date;
|
date = grades.first.date;
|
||||||
}
|
}
|
||||||
@ -143,8 +153,9 @@ class _GradeCalculatorState extends State<GradeCalculator> {
|
|||||||
date: date,
|
date: date,
|
||||||
writeDate: date,
|
writeDate: date,
|
||||||
description: "Ghost Grade".i18n,
|
description: "Ghost Grade".i18n,
|
||||||
value: GradeValue(newValue.toInt(), "", "", newWeight.toInt()),
|
value:
|
||||||
teacher: "Ghost",
|
GradeValue(newValue.toInt(), "", "", newWeight.toInt()),
|
||||||
|
teacher: Teacher.fromString("Ghost"),
|
||||||
type: GradeType.ghost,
|
type: GradeType.ghost,
|
||||||
form: "",
|
form: "",
|
||||||
subject: widget.subject,
|
subject: widget.subject,
|
||||||
|
@ -21,6 +21,10 @@ import 'package:filcnaplo_mobile_ui/pages/grades/calculator/grade_calculator_pro
|
|||||||
import 'package:filcnaplo_mobile_ui/pages/grades/grades_count.dart';
|
import 'package:filcnaplo_mobile_ui/pages/grades/grades_count.dart';
|
||||||
import 'package:filcnaplo_mobile_ui/pages/grades/graph.dart';
|
import 'package:filcnaplo_mobile_ui/pages/grades/graph.dart';
|
||||||
import 'package:filcnaplo_mobile_ui/pages/grades/subject_grades_container.dart';
|
import 'package:filcnaplo_mobile_ui/pages/grades/subject_grades_container.dart';
|
||||||
|
import 'package:filcnaplo_premium/ui/mobile/goal_planner/test.dart';
|
||||||
|
import 'package:filcnaplo_premium/models/premium_scopes.dart';
|
||||||
|
import 'package:filcnaplo_premium/providers/premium_provider.dart';
|
||||||
|
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
|
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
|
||||||
@ -215,25 +219,25 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
|
|||||||
gradeCalc(context);
|
gradeCalc(context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// FloatingActionButton.small(
|
FloatingActionButton.small(
|
||||||
// child: const Icon(FeatherIcons.flag, size: 20.0),
|
child: const Icon(FeatherIcons.flag, size: 20.0),
|
||||||
// backgroundColor: Theme.of(context).colorScheme.secondary,
|
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
// onPressed: () {
|
onPressed: () {
|
||||||
// if (!Provider.of<PremiumProvider>(context, listen: false)
|
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||||
// .hasScope(PremiumScopes.goalPlanner)) {
|
.hasScope(PremiumScopes.goalPlanner)) {
|
||||||
// PremiumLockedFeatureUpsell.show(
|
PremiumLockedFeatureUpsell.show(
|
||||||
// context: context, feature: PremiumFeature.goalplanner);
|
context: context, feature: PremiumFeature.goalplanner);
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// ScaffoldMessenger.of(context).showSnackBar(
|
// ScaffoldMessenger.of(context).showSnackBar(
|
||||||
// const SnackBar(content: Text("Hamarosan...")));
|
// const SnackBar(content: Text("Hamarosan...")));
|
||||||
|
|
||||||
// Navigator.of(context).push(CupertinoPageRoute(
|
Navigator.of(context).push(CupertinoPageRoute(
|
||||||
// builder: (context) => PremiumGoalplannerNewGoalScreen(
|
builder: (context) =>
|
||||||
// subject: widget.subject)));
|
GoalPlannerTest(subject: widget.subject)));
|
||||||
// },
|
},
|
||||||
// ),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -263,7 +267,8 @@ class _GradeSubjectViewState extends State<GradeSubjectView> {
|
|||||||
subject: widget.subject, context: context),
|
subject: widget.subject, context: context),
|
||||||
scrollController: _scrollController,
|
scrollController: _scrollController,
|
||||||
title: widget.subject.renamedTo ?? widget.subject.name.capital(),
|
title: widget.subject.renamedTo ?? widget.subject.name.capital(),
|
||||||
italic: settingsProvider.renamedSubjectsItalics && widget.subject.isRenamed,
|
italic: settingsProvider.renamedSubjectsItalics &&
|
||||||
|
widget.subject.isRenamed,
|
||||||
child: SubjectGradesContainer(
|
child: SubjectGradesContainer(
|
||||||
child: CupertinoScrollbar(
|
child: CupertinoScrollbar(
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
@ -356,14 +356,14 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
|
|||||||
// confetti 🎊
|
// confetti 🎊
|
||||||
if (_confettiController != null)
|
if (_confettiController != null)
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.topCenter,
|
||||||
child: ConfettiWidget(
|
child: ConfettiWidget(
|
||||||
confettiController: _confettiController!,
|
confettiController: _confettiController!,
|
||||||
blastDirection: -pi / 2,
|
blastDirectionality: BlastDirectionality.explosive,
|
||||||
emissionFrequency: 0.01,
|
emissionFrequency: 0.02,
|
||||||
numberOfParticles: 80,
|
numberOfParticles: 120,
|
||||||
maxBlastForce: 100,
|
maxBlastForce: 20,
|
||||||
minBlastForce: 90,
|
minBlastForce: 10,
|
||||||
gravity: 0.3,
|
gravity: 0.3,
|
||||||
minimumSize: const Size(5, 5),
|
minimumSize: const Size(5, 5),
|
||||||
maximumSize: const Size(20, 20),
|
maximumSize: const Size(20, 20),
|
||||||
|
@ -139,7 +139,6 @@ class NavigationScreenState extends State<NavigationScreen>
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
|
|
||||||
HomeWidget.setAppGroupId('hu.refilc.naplo.group');
|
HomeWidget.setAppGroupId('hu.refilc.naplo.group');
|
||||||
|
|
||||||
_checkForWidgetLaunch();
|
_checkForWidgetLaunch();
|
||||||
@ -199,9 +198,9 @@ class NavigationScreenState extends State<NavigationScreen>
|
|||||||
// Show news
|
// Show news
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (newsProvider.show) {
|
if (newsProvider.show) {
|
||||||
newsProvider.lock();
|
NewsView.show(newsProvider.news[0], context: context)
|
||||||
NewsView.show(newsProvider.news[newsProvider.state], context: context)
|
|
||||||
.then((value) => newsProvider.release());
|
.then((value) => newsProvider.release());
|
||||||
|
newsProvider.lock();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -31,23 +31,30 @@ class NewsView extends StatelessWidget {
|
|||||||
physics: const BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(),
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0, right: 6.0, top: 14.0, bottom: 8.0),
|
padding: const EdgeInsets.only(
|
||||||
|
left: 8.0, right: 6.0, top: 14.0, bottom: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
news.title,
|
news.title,
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 18.0),
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w600, fontSize: 18.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SelectableLinkify(
|
SelectableLinkify(
|
||||||
text: news.content.escapeHtml(),
|
text: news.content.escapeHtml(),
|
||||||
options: const LinkifyOptions(looseUrl: true, removeWww: true),
|
options:
|
||||||
|
const LinkifyOptions(looseUrl: true, removeWww: true),
|
||||||
onOpen: (link) {
|
onOpen: (link) {
|
||||||
launch(
|
launch(
|
||||||
link.url,
|
link.url,
|
||||||
customTabsOption: CustomTabsOption(showPageTitle: true, toolbarColor: Theme.of(context).scaffoldBackgroundColor),
|
customTabsOption: CustomTabsOption(
|
||||||
|
showPageTitle: true,
|
||||||
|
toolbarColor:
|
||||||
|
Theme.of(context).scaffoldBackgroundColor),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 14.0),
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w500, fontSize: 14.0),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -59,10 +66,15 @@ class NewsView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (news.link != "")
|
if (news.link != "")
|
||||||
DialogButton(
|
DialogButton(
|
||||||
label: news.openLabel != "" ? news.openLabel : "open".i18n.toUpperCase(),
|
label: news.openLabel != ""
|
||||||
|
? news.openLabel
|
||||||
|
: "open".i18n.toUpperCase(),
|
||||||
onTap: () => launch(
|
onTap: () => launch(
|
||||||
news.link,
|
news.link,
|
||||||
customTabsOption: CustomTabsOption(showPageTitle: true, toolbarColor: Theme.of(context).scaffoldBackgroundColor),
|
customTabsOption: CustomTabsOption(
|
||||||
|
showPageTitle: true,
|
||||||
|
toolbarColor:
|
||||||
|
Theme.of(context).scaffoldBackgroundColor),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DialogButton(
|
DialogButton(
|
||||||
@ -78,12 +90,15 @@ class NewsView extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<T?> show<T>(News news, {required BuildContext context, bool force = false}) {
|
static Future<T?> show<T>(News news,
|
||||||
|
{required BuildContext context, bool force = false}) {
|
||||||
if (news.title == "") return Future<T?>.value(null);
|
if (news.title == "") return Future<T?>.value(null);
|
||||||
|
|
||||||
bool popup = news.platform == '' || force;
|
bool popup = news.platform == 'all' || force;
|
||||||
|
|
||||||
if (Provider.of<SettingsProvider>(context, listen: false).newsEnabled || news.emergency || force) {
|
if (Provider.of<SettingsProvider>(context, listen: false).newsEnabled ||
|
||||||
|
news.emergency ||
|
||||||
|
force) {
|
||||||
switch (news.platform.trim().toLowerCase()) {
|
switch (news.platform.trim().toLowerCase()) {
|
||||||
case "android":
|
case "android":
|
||||||
if (Platform.isAndroid) popup = true;
|
if (Platform.isAndroid) popup = true;
|
||||||
@ -100,6 +115,9 @@ class NewsView extends StatelessWidget {
|
|||||||
case "macos":
|
case "macos":
|
||||||
if (Platform.isMacOS) popup = true;
|
if (Platform.isMacOS) popup = true;
|
||||||
break;
|
break;
|
||||||
|
case "all":
|
||||||
|
popup = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
popup = true;
|
popup = true;
|
||||||
}
|
}
|
||||||
@ -108,7 +126,10 @@ class NewsView extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (popup) {
|
if (popup) {
|
||||||
return showDialog<T?>(context: context, builder: (context) => NewsView(news), barrierDismissible: true);
|
return showDialog<T?>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => NewsView(news),
|
||||||
|
barrierDismissible: true);
|
||||||
} else {
|
} else {
|
||||||
return Future<T?>.value(null);
|
return Future<T?>.value(null);
|
||||||
}
|
}
|
||||||
|
@ -28,27 +28,22 @@ class PrivacyView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
SelectableLinkify(
|
SelectableLinkify(
|
||||||
text: """
|
text: """
|
||||||
A reFilc egy kliensalkalmazás, segítségével az e-Kréta rendszeréből letöltheted és felhasználóbarát módon megjelenítheted az adataidat.
|
• A reFilc (továbbiakban alkalmazás) egy mobilos, asztali és webes kliensalkalmazás, segítségével az e-Kréta rendszeréből letöltheted és felhasználóbarát módon megjelenítheted az adataidat. Tanulmányi adataid csak közvetlenül az alkalmazás és a Kréta-szerverek között közlekednek, titkosított kapcsolaton keresztül.
|
||||||
Tanulmányi adataid csak közvetlenül az alkalmazás és a Kréta-szerverek között közlekednek, titkosított kapcsolaton keresztül.
|
|
||||||
|
|
||||||
A reFilc fejlesztői és üzemeltetői a tanulmányi adataidat semmilyen célból nem másolják, nem tárolják és harmadik félnek nem továbbítják. Ezeket így az e-Kréta Informatikai Zrt. kezeli, az ő tájékoztatójukat itt találod: https://tudasbazis.ekreta.hu/pages/viewpage.action?pageId=4065038.
|
• A reFilc fejlesztői és/vagy üzemeltetői, valamint az alkalmazás a tanulmányi és személyes adataidat semmilyen célból és semmilyen körülmények között nem másolják, nem tárolják és harmadik félnek nem továbbítják. Ezeket így az Educational Development Informatikai Zrt. kezeli, az Ő adatkezeléssel kapcsolatos tájékoztatójukat itt találod: https://tudasbazis.ekreta.hu/pages/viewpage.action?pageId=4065038
|
||||||
Azok törlésével vagy módosítával kapcsolatban keresd az osztályfőnöködet vagy az iskolád rendszergazdáját.
|
|
||||||
|
|
||||||
Az alkalmazás névtelen használati statisztikákat gyűjt, ezek alapján tudjuk meghatározni a felhasználók és a telepítések számát. Ezt a beállításokban kikapcsolhatod.
|
• Azok törlésével vagy módosítával kapcsolatban keresd az osztályfőnöködet vagy az iskolád rendszergazdáját.
|
||||||
Kérünk, hogy ha csak teheted, hagyd ezt a funkciót bekapcsolva.
|
|
||||||
|
|
||||||
Amikor az alkalmazás hibába ütközik, lehetőség van hibajelentés küldésére.
|
• Az alkalmazás névtelen használati statisztikákat gyűjt, ezek alapján tudjuk meghatározni a felhasználók és a telepítések számát, valamint az eszközük platformját. Ezt a beállításokban kikapcsolhatod. Kérünk, hogy ha csak teheted, hagyd ezt a funkciót bekapcsolva, hogy pontosabb információnk legyen a felhasználóink platform-megoszlásáról.
|
||||||
Ez személyes- vagy tanulmányi adatokat nem tartalmaz, viszont részletes információval szolgál a hibáról és eszközödről.
|
|
||||||
A küldés előtt megjelenő képernyőn a te felelősséged átnézni a továbbításra kerülő adatsort.
|
|
||||||
A hibajelentéseket a reFilc fejlesztői felületén és egy privát Discord szobában tároljuk, ezekhez csak az app fejlesztői férnek hozzá.
|
|
||||||
Az alkalmazás belépéskor a GitHub API segítségével ellenőrzi, hogy elérhető-e új verzió, és kérésre innen is tölti le a telepítőt.
|
|
||||||
|
|
||||||
Ha az adataiddal kapcsolatban bármilyen kérdésed van (törlés, módosítás, adathordozás), keress minket a filcnaplo@filcnaplo.hu címen.
|
• Amikor az alkalmazás hibába ütközik, lehetőség van hibajelentés küldésére. Ez személyes- és/vagy tanulmányi adatokat nem tartalmaz, viszont részletes információval szolgál a hibáról, annak okáról és eszközödről. A küldés előtt megjelenő képernyőn a te felelősséged átnézni a továbbításra kerülő adatsort. A hibajelentéseket a reFilc fejlesztői felületén és egy privát Discord szobában tároljuk, ezekhez csak az app fejlesztői férnek hozzá.
|
||||||
|
|
||||||
Az alkalmazás használatával jelzed, hogy ezt a tájékoztatót tudomásul vetted.
|
• Az alkalmazás (az alábbi platformokon: Android, Linux, Windows) minden egyes indításakor a reFilc API, valamint a Github API segítségével ellenőrzi, hogy elérhető-e új verzió, és kérésre innen letölti és telepíti a frissítést.
|
||||||
|
|
||||||
Utolsó módosítás: 2023. 05. 28.
|
• Amennyiben az adataiddal kapcsolatban bármilyen kérdésed van (megtekintés, törlés, módosítás, adathordozás), keress minket a social@refilc.hu e-mail címen, vagy Discord szerverünkön!
|
||||||
""",
|
|
||||||
|
• A kliensalkalmazás bármely eszközön és platformon történő használatával tudomásul vetted és elfogadod a jelen adatkezelési tájékoztatót. A reFilc csapata fenntartja a jogot a tájékoztató módosítására és a módosításokról nem köteles értesíteni a felhasználóit!
|
||||||
|
""",
|
||||||
onOpen: (link) => launch(link.url,
|
onOpen: (link) => launch(link.url,
|
||||||
customTabsOption: CustomTabsOption(
|
customTabsOption: CustomTabsOption(
|
||||||
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
|
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
@ -15,6 +15,7 @@ import 'package:filcnaplo/models/user.dart';
|
|||||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||||
import 'package:filcnaplo_kreta_api/client/client.dart';
|
import 'package:filcnaplo_kreta_api/client/client.dart';
|
||||||
import 'package:filcnaplo_mobile_ui/common/action_button.dart';
|
import 'package:filcnaplo_mobile_ui/common/action_button.dart';
|
||||||
|
import 'package:filcnaplo_mobile_ui/common/beta_chip.dart';
|
||||||
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu.dart';
|
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu.dart';
|
||||||
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_item.dart';
|
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_item.dart';
|
||||||
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
|
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
|
||||||
@ -41,6 +42,7 @@ import 'package:filcnaplo_premium/ui/mobile/settings/nickname.dart';
|
|||||||
import 'package:filcnaplo_premium/ui/mobile/settings/profile_pic.dart';
|
import 'package:filcnaplo_premium/ui/mobile/settings/profile_pic.dart';
|
||||||
import 'package:filcnaplo_premium/ui/mobile/settings/icon_pack.dart';
|
import 'package:filcnaplo_premium/ui/mobile/settings/icon_pack.dart';
|
||||||
import 'package:filcnaplo_premium/ui/mobile/settings/modify_subject_names.dart';
|
import 'package:filcnaplo_premium/ui/mobile/settings/modify_subject_names.dart';
|
||||||
|
import 'package:filcnaplo_premium/ui/mobile/settings/modify_teacher_names.dart';
|
||||||
|
|
||||||
class SettingsScreen extends StatefulWidget {
|
class SettingsScreen extends StatefulWidget {
|
||||||
const SettingsScreen({Key? key}) : super(key: key);
|
const SettingsScreen({Key? key}) : super(key: key);
|
||||||
@ -156,7 +158,8 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
Future.delayed(Duration.zero, () {
|
Future.delayed(Duration.zero, () {
|
||||||
futureRelease = Provider.of<UpdateProvider>(context, listen: false).installedVersion();
|
futureRelease = Provider.of<UpdateProvider>(context, listen: false)
|
||||||
|
.installedVersion();
|
||||||
});
|
});
|
||||||
_hideContainersController = AnimationController(
|
_hideContainersController = AnimationController(
|
||||||
vsync: this, duration: const Duration(milliseconds: 200));
|
vsync: this, duration: const Duration(milliseconds: 200));
|
||||||
@ -383,7 +386,10 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
},
|
},
|
||||||
title: Text("language".i18n),
|
title: Text("language".i18n),
|
||||||
leading: const Icon(FeatherIcons.globe),
|
leading: const Icon(FeatherIcons.globe),
|
||||||
trailing: Text(languageText),
|
trailing: Text(
|
||||||
|
languageText,
|
||||||
|
style: const TextStyle(fontSize: 14.0),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PanelButton(
|
PanelButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -392,7 +398,10 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
},
|
},
|
||||||
title: Text("startpage".i18n),
|
title: Text("startpage".i18n),
|
||||||
leading: const Icon(FeatherIcons.play),
|
leading: const Icon(FeatherIcons.play),
|
||||||
trailing: Text(startPageTitle.capital()),
|
trailing: Text(
|
||||||
|
startPageTitle.capital(),
|
||||||
|
style: const TextStyle(fontSize: 14.0),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PanelButton(
|
PanelButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -401,8 +410,10 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
},
|
},
|
||||||
title: Text("rounding".i18n),
|
title: Text("rounding".i18n),
|
||||||
leading: const Icon(FeatherIcons.gitCommit),
|
leading: const Icon(FeatherIcons.gitCommit),
|
||||||
trailing:
|
trailing: Text(
|
||||||
Text((settings.rounding / 10).toStringAsFixed(1)),
|
(settings.rounding / 10).toStringAsFixed(1),
|
||||||
|
style: const TextStyle(fontSize: 14.0),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PanelButton(
|
PanelButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -411,7 +422,10 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
},
|
},
|
||||||
title: Text("vibrate".i18n),
|
title: Text("vibrate".i18n),
|
||||||
leading: const Icon(FeatherIcons.radio),
|
leading: const Icon(FeatherIcons.radio),
|
||||||
trailing: Text(vibrateTitle),
|
trailing: Text(
|
||||||
|
vibrateTitle,
|
||||||
|
style: const TextStyle(fontSize: 14.0),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PanelButton(
|
PanelButton(
|
||||||
padding: const EdgeInsets.only(left: 14.0),
|
padding: const EdgeInsets.only(left: 14.0),
|
||||||
@ -445,9 +459,14 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
contentPadding: const EdgeInsets.only(left: 12.0),
|
contentPadding: const EdgeInsets.only(left: 12.0),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12.0)),
|
borderRadius: BorderRadius.circular(12.0)),
|
||||||
title: Row(children: [
|
title: Row(
|
||||||
|
children: [
|
||||||
Icon(FeatherIcons.messageSquare,
|
Icon(FeatherIcons.messageSquare,
|
||||||
color: settings.notificationsEnabled ? Theme.of(context).colorScheme.secondary : AppColors.of(context).text.withOpacity(.25)),
|
color: settings.notificationsEnabled
|
||||||
|
? Theme.of(context).colorScheme.secondary
|
||||||
|
: AppColors.of(context)
|
||||||
|
.text
|
||||||
|
.withOpacity(.25)),
|
||||||
const SizedBox(width: 14.0),
|
const SizedBox(width: 14.0),
|
||||||
Text(
|
Text(
|
||||||
"notifications".i18n,
|
"notifications".i18n,
|
||||||
@ -461,34 +480,11 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 5,
|
width: 5,
|
||||||
),
|
),
|
||||||
SizedBox(
|
BetaChip(
|
||||||
height: 30,
|
disabled: !settings.notificationsEnabled,
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
child: Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.only(left: 10, right: 10),
|
|
||||||
child: Center(
|
|
||||||
child: Text("BETA",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 9.1,
|
|
||||||
color: AppColors.of(context)
|
|
||||||
.text
|
|
||||||
.withOpacity(
|
|
||||||
settings.notificationsEnabled
|
|
||||||
? 1.0
|
|
||||||
: .5),
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
overflow: TextOverflow.ellipsis))),
|
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
],
|
||||||
color: settings.notificationsEnabled
|
|
||||||
? Theme.of(context).colorScheme.secondary
|
|
||||||
: AppColors.of(context).text.withOpacity(.25),
|
|
||||||
borderRadius: BorderRadius.circular(40)),
|
|
||||||
),
|
),
|
||||||
)
|
|
||||||
]),
|
|
||||||
onChanged: (value) =>
|
onChanged: (value) =>
|
||||||
settings.update(notificationsEnabled: value),
|
settings.update(notificationsEnabled: value),
|
||||||
),
|
),
|
||||||
@ -636,7 +632,10 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
},
|
},
|
||||||
title: Text("theme".i18n),
|
title: Text("theme".i18n),
|
||||||
leading: const Icon(FeatherIcons.sun),
|
leading: const Icon(FeatherIcons.sun),
|
||||||
trailing: Text(themeModeText),
|
trailing: Text(
|
||||||
|
themeModeText,
|
||||||
|
style: const TextStyle(fontSize: 14.0),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PanelButton(
|
PanelButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@ -805,6 +804,9 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
MenuRenamedSubjects(
|
MenuRenamedSubjects(
|
||||||
settings: settings,
|
settings: settings,
|
||||||
),
|
),
|
||||||
|
MenuRenamedTeachers(
|
||||||
|
settings: settings,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -817,6 +819,19 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
child: Panel(
|
child: Panel(
|
||||||
title: Text("about".i18n),
|
title: Text("about".i18n),
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
|
PanelButton(
|
||||||
|
leading: const Icon(FeatherIcons.mail),
|
||||||
|
title: Text("news".i18n),
|
||||||
|
onPressed: () => _openNews(context),
|
||||||
|
),
|
||||||
|
PanelButton(
|
||||||
|
leading: const Icon(FeatherIcons.lock),
|
||||||
|
title: Text("privacy".i18n),
|
||||||
|
// onPressed: () => launchUrl(
|
||||||
|
// Uri.parse("https://refilc.hu/privacy-policy"),
|
||||||
|
// mode: LaunchMode.inAppWebView),
|
||||||
|
onPressed: () => _openPrivacy(context),
|
||||||
|
),
|
||||||
PanelButton(
|
PanelButton(
|
||||||
leading: const Icon(FeatherIcons.atSign),
|
leading: const Icon(FeatherIcons.atSign),
|
||||||
title: const Text("Discord"),
|
title: const Text("Discord"),
|
||||||
@ -838,16 +853,6 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
Uri.parse("https://github.com/refilc"),
|
Uri.parse("https://github.com/refilc"),
|
||||||
mode: LaunchMode.externalApplication),
|
mode: LaunchMode.externalApplication),
|
||||||
),
|
),
|
||||||
PanelButton(
|
|
||||||
leading: const Icon(FeatherIcons.mail),
|
|
||||||
title: Text("news".i18n),
|
|
||||||
onPressed: () => _openNews(context),
|
|
||||||
),
|
|
||||||
PanelButton(
|
|
||||||
leading: const Icon(FeatherIcons.lock),
|
|
||||||
title: Text("privacy".i18n),
|
|
||||||
onPressed: () => _openPrivacy(context),
|
|
||||||
),
|
|
||||||
PanelButton(
|
PanelButton(
|
||||||
leading: const Icon(FeatherIcons.award),
|
leading: const Icon(FeatherIcons.award),
|
||||||
title: Text("licenses".i18n),
|
title: Text("licenses".i18n),
|
||||||
@ -923,7 +928,8 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12.0)),
|
borderRadius: BorderRadius.circular(12.0)),
|
||||||
title: Text("devmode".i18n,
|
title: Text("devmode".i18n,
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500)),
|
style:
|
||||||
|
const TextStyle(fontWeight: FontWeight.w500)),
|
||||||
onChanged: (v) =>
|
onChanged: (v) =>
|
||||||
settings.update(developerMode: false),
|
settings.update(developerMode: false),
|
||||||
value: settings.developerMode,
|
value: settings.developerMode,
|
||||||
@ -998,8 +1004,8 @@ class _SettingsScreenState extends State<SettingsScreen>
|
|||||||
if (devmodeCountdown > 0) {
|
if (devmodeCountdown > 0) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
content: Text(
|
content:
|
||||||
"devmoretaps".i18n.fill([devmodeCountdown])),
|
Text("devmoretaps".i18n.fill([devmodeCountdown])),
|
||||||
));
|
));
|
||||||
|
|
||||||
setState(() => devmodeCountdown--);
|
setState(() => devmodeCountdown--);
|
||||||
|
@ -163,7 +163,7 @@ extension SettingsLocalization on String {
|
|||||||
"theme": "Thema",
|
"theme": "Thema",
|
||||||
"color": "Farbe",
|
"color": "Farbe",
|
||||||
"grade_colors": "Grad Farben",
|
"grade_colors": "Grad Farben",
|
||||||
"notifications": "Benachrichtigungen",
|
"notifications": "Mitteilung",
|
||||||
"news": "Nachrichten",
|
"news": "Nachrichten",
|
||||||
"extras": "Extras",
|
"extras": "Extras",
|
||||||
"about": "Informationen",
|
"about": "Informationen",
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
name: filcnaplo_mobile_ui
|
|
||||||
publish_to: "none"
|
|
||||||
|
|
||||||
environment:
|
|
||||||
sdk: ">=2.17.0 <3.0.0"
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
cupertino_icons: ^1.0.2
|
|
||||||
|
|
||||||
# Filcnaplo main dep
|
|
||||||
filcnaplo:
|
|
||||||
path: ../filcnaplo/
|
|
||||||
filcnaplo_kreta_api:
|
|
||||||
path: ../filcnaplo_kreta_api/
|
|
||||||
filcnaplo_premium:
|
|
||||||
path: ../filcnaplo_premium/
|
|
||||||
|
|
||||||
flutter_feather_icons: ^2.0.0+1
|
|
||||||
provider: ^5.0.0
|
|
||||||
fl_chart: ^0.45.1
|
|
||||||
url_launcher: ^6.0.9
|
|
||||||
flutter_material_color_picker: ^1.1.0+2
|
|
||||||
photo_view: ^0.14.0
|
|
||||||
flutter_linkify: ^5.0.2
|
|
||||||
flutter_custom_tabs: ^1.0.3
|
|
||||||
flutter_markdown: ^0.6.5
|
|
||||||
animations: ^2.0.1
|
|
||||||
animated_list_plus: ^0.5.0
|
|
||||||
confetti: ^0.6.0
|
|
||||||
live_activities: ^1.0.0
|
|
||||||
animated_flip_counter: ^0.2.5
|
|
||||||
lottie: ^1.4.3
|
|
||||||
rive: ^0.9.1
|
|
||||||
animated_background: ^2.0.0
|
|
||||||
home_widget: ^0.1.6
|
|
||||||
dropdown_button2: ^1.8.9
|
|
||||||
flutter_svg: ^1.1.6
|
|
||||||
background_fetch: ^1.1.5
|
|
||||||
wtf_sliding_sheet: ^1.0.0
|
|
||||||
package_info_plus: ^4.0.2
|
|
||||||
dotted_border: ^2.0.0+3
|
|
||||||
screenshot: ^2.1.0
|
|
||||||
image_gallery_saver: ^2.0.2
|
|
||||||
rounded_expansion_tile: ^0.0.13
|
|
||||||
|
|
||||||
dev_dependencies:
|
|
||||||
flutter_lints: ^1.0.0
|
|
||||||
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
@ -18,6 +18,7 @@ class PremiumScopes {
|
|||||||
|
|
||||||
/// Modify subject names
|
/// Modify subject names
|
||||||
static const renameSubjects = "filc.premium.RENAME_SUBJECTS";
|
static const renameSubjects = "filc.premium.RENAME_SUBJECTS";
|
||||||
|
static const renameTeachers = "filc.premium.RENAME_TEACHERS";
|
||||||
|
|
||||||
/// Tinta
|
/// Tinta
|
||||||
|
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
|
import 'package:filcnaplo/models/settings.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class GoalInput extends StatelessWidget {
|
class GoalInput extends StatelessWidget {
|
||||||
const GoalInput({Key? key, required this.currentAverage, required this.value, required this.onChanged}) : super(key: key);
|
const GoalInput(
|
||||||
|
{Key? key,
|
||||||
|
required this.currentAverage,
|
||||||
|
required this.value,
|
||||||
|
required this.onChanged})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
final double currentAverage;
|
final double currentAverage;
|
||||||
final double value;
|
final double value;
|
||||||
@ -24,6 +31,8 @@ class GoalInput extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
SettingsProvider settings = Provider.of<SettingsProvider>(context);
|
||||||
|
|
||||||
List<int> presets = [2, 3, 4, 5];
|
List<int> presets = [2, 3, 4, 5];
|
||||||
presets = presets.where((e) => gradeToAvg(e) > currentAverage).toList();
|
presets = presets.where((e) => gradeToAvg(e) > currentAverage).toList();
|
||||||
|
|
||||||
@ -44,7 +53,8 @@ class GoalInput extends StatelessWidget {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(right: 20.0),
|
padding: const EdgeInsets.only(right: 20.0),
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
painter: GoalSliderPainter(value: (value - 1) / 4),
|
painter: GoalSliderPainter(
|
||||||
|
value: (value - 1) / 4, settings: settings),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -61,8 +71,9 @@ class GoalInput extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(99.0),
|
borderRadius: BorderRadius.circular(99.0),
|
||||||
color: gradeColor(e).withOpacity(selected ? 1.0 : 0.2),
|
color:
|
||||||
border: Border.all(color: gradeColor(e), width: 4),
|
gradeColor(e, settings).withOpacity(selected ? 1.0 : 0.2),
|
||||||
|
border: Border.all(color: gradeColor(e, settings), width: 4),
|
||||||
),
|
),
|
||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
@ -70,11 +81,13 @@ class GoalInput extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(99.0),
|
borderRadius: BorderRadius.circular(99.0),
|
||||||
onTap: () => setValue(gradeToAvg(e)),
|
onTap: () => setValue(gradeToAvg(e)),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 24.0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 2.0, horizontal: 24.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
e.toString(),
|
e.toString(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: selected ? Colors.white : gradeColor(e),
|
color:
|
||||||
|
selected ? Colors.white : gradeColor(e, settings),
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 24.0,
|
fontSize: 24.0,
|
||||||
),
|
),
|
||||||
@ -93,8 +106,9 @@ class GoalInput extends StatelessWidget {
|
|||||||
|
|
||||||
class GoalSliderPainter extends CustomPainter {
|
class GoalSliderPainter extends CustomPainter {
|
||||||
final double value;
|
final double value;
|
||||||
|
final SettingsProvider settings;
|
||||||
|
|
||||||
GoalSliderPainter({required this.value});
|
GoalSliderPainter({required this.value, required this.settings});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
@ -115,21 +129,24 @@ class GoalSliderPainter extends CustomPainter {
|
|||||||
const Radius.circular(99.0),
|
const Radius.circular(99.0),
|
||||||
),
|
),
|
||||||
Paint()
|
Paint()
|
||||||
..shader = const LinearGradient(colors: [
|
..shader = LinearGradient(colors: [
|
||||||
Color(0xffFF3B30),
|
settings.gradeColors[0],
|
||||||
Color(0xffFF9F0A),
|
settings.gradeColors[1],
|
||||||
Color(0xffFFD60A),
|
settings.gradeColors[2],
|
||||||
Color(0xff34C759),
|
settings.gradeColors[3],
|
||||||
Color(0xff247665),
|
settings.gradeColors[4],
|
||||||
]).createShader(rect),
|
]).createShader(rect),
|
||||||
);
|
);
|
||||||
canvas.drawOval(
|
canvas.drawOval(
|
||||||
Rect.fromCircle(center: Offset(size.width * value, size.height / 2), radius: radius - cpadding),
|
Rect.fromCircle(
|
||||||
|
center: Offset(size.width * value, size.height / 2),
|
||||||
|
radius: radius - cpadding),
|
||||||
Paint()..color = Colors.white,
|
Paint()..color = Colors.white,
|
||||||
);
|
);
|
||||||
for (int i = 1; i < 4; i++) {
|
for (int i = 1; i < 4; i++) {
|
||||||
canvas.drawOval(
|
canvas.drawOval(
|
||||||
Rect.fromCircle(center: Offset(size.width / 4 * i, size.height / 2), radius: 4),
|
Rect.fromCircle(
|
||||||
|
center: Offset(size.width / 4 * i, size.height / 2), radius: 4),
|
||||||
Paint()..color = Colors.white.withOpacity(.5),
|
Paint()..color = Colors.white.withOpacity(.5),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -145,12 +162,19 @@ double gradeToAvg(int grade) {
|
|||||||
return grade - 0.5;
|
return grade - 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color gradeColor(int grade) {
|
Color gradeColor(int grade, SettingsProvider settings) {
|
||||||
|
// return [
|
||||||
|
// const Color(0xffFF3B30),
|
||||||
|
// const Color(0xffFF9F0A),
|
||||||
|
// const Color(0xffFFD60A),
|
||||||
|
// const Color(0xff34C759),
|
||||||
|
// const Color(0xff247665),
|
||||||
|
// ].elementAt(grade.clamp(1, 5) - 1);
|
||||||
return [
|
return [
|
||||||
const Color(0xffFF3B30),
|
settings.gradeColors[0],
|
||||||
const Color(0xffFF9F0A),
|
settings.gradeColors[1],
|
||||||
const Color(0xffFFD60A),
|
settings.gradeColors[2],
|
||||||
const Color(0xff34C759),
|
settings.gradeColors[3],
|
||||||
const Color(0xff247665),
|
settings.gradeColors[4],
|
||||||
].elementAt(grade.clamp(1, 5) - 1);
|
].elementAt(grade.clamp(1, 5) - 1);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import 'dart:math';
|
|||||||
import 'package:filcnaplo_kreta_api/models/category.dart';
|
import 'package:filcnaplo_kreta_api/models/category.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/models/teacher.dart';
|
||||||
import 'package:flutter/foundation.dart' show listEquals;
|
import 'package:flutter/foundation.dart' show listEquals;
|
||||||
|
|
||||||
/// Generate list of grades that achieve the wanted goal.
|
/// Generate list of grades that achieve the wanted goal.
|
||||||
@ -46,7 +47,8 @@ class GoalPlanner {
|
|||||||
|
|
||||||
for (int i = g.max; i >= 0; i--) {
|
for (int i = g.max; i >= 0; i--) {
|
||||||
int newGradeToAdd = g.gradeToAdd - 1;
|
int newGradeToAdd = g.gradeToAdd - 1;
|
||||||
List<int> newPlan = GoalPlannerHelper._addToList<int>(g.plan, g.gradeToAdd, i);
|
List<int> newPlan =
|
||||||
|
GoalPlannerHelper._addToList<int>(g.plan, g.gradeToAdd, i);
|
||||||
|
|
||||||
Avg newAvg = GoalPlannerHelper._addToAvg(g.currentAvg, g.gradeToAdd, i);
|
Avg newAvg = GoalPlannerHelper._addToAvg(g.currentAvg, g.gradeToAdd, i);
|
||||||
int newN = GoalPlannerHelper.howManyNeeded(
|
int newN = GoalPlannerHelper.howManyNeeded(
|
||||||
@ -57,7 +59,7 @@ class GoalPlanner {
|
|||||||
id: '',
|
id: '',
|
||||||
date: DateTime(0),
|
date: DateTime(0),
|
||||||
value: GradeValue(e, '', '', 100),
|
value: GradeValue(e, '', '', 100),
|
||||||
teacher: '',
|
teacher: Teacher.fromString(''),
|
||||||
description: '',
|
description: '',
|
||||||
form: '',
|
form: '',
|
||||||
groupId: '',
|
groupId: '',
|
||||||
@ -83,7 +85,8 @@ class GoalPlanner {
|
|||||||
grades,
|
grades,
|
||||||
goal,
|
goal,
|
||||||
),
|
),
|
||||||
Avg(GoalPlannerHelper.averageEvals(grades), GoalPlannerHelper.weightSum(grades)),
|
Avg(GoalPlannerHelper.averageEvals(grades),
|
||||||
|
GoalPlannerHelper.weightSum(grades)),
|
||||||
[],
|
[],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -92,7 +95,9 @@ class GoalPlanner {
|
|||||||
for (var e in plans) {
|
for (var e in plans) {
|
||||||
e.sum = e.plan.fold(0, (int a, b) => a + b);
|
e.sum = e.plan.fold(0, (int a, b) => a + b);
|
||||||
e.avg = e.sum / e.plan.length;
|
e.avg = e.sum / e.plan.length;
|
||||||
e.sigma = sqrt(e.plan.map((i) => pow(i - e.avg, 2)).fold(0, (num a, b) => a + b) / e.plan.length);
|
e.sigma = sqrt(
|
||||||
|
e.plan.map((i) => pow(i - e.avg, 2)).fold(0, (num a, b) => a + b) /
|
||||||
|
e.plan.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter without aggression
|
// filter without aggression
|
||||||
@ -141,7 +146,8 @@ class Plan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GoalPlannerHelper {
|
class GoalPlannerHelper {
|
||||||
static Avg _addToAvg(Avg base, int grade, int n) => Avg((base.avg * base.n + grade * n) / (base.n + n), base.n + n);
|
static Avg _addToAvg(Avg base, int grade, int n) =>
|
||||||
|
Avg((base.avg * base.n + grade * n) / (base.n + n), base.n + n);
|
||||||
|
|
||||||
static List<T> _addToList<T>(List<T> l, T e, int n) {
|
static List<T> _addToList<T>(List<T> l, T e, int n) {
|
||||||
if (n == 0) return l;
|
if (n == 0) return l;
|
||||||
@ -158,15 +164,20 @@ class GoalPlannerHelper {
|
|||||||
if (avg >= goal) return 0;
|
if (avg >= goal) return 0;
|
||||||
if (grade * 1.0 == goal) return -1;
|
if (grade * 1.0 == goal) return -1;
|
||||||
int candidate = (wsum * (avg - goal) / (goal - grade)).floor();
|
int candidate = (wsum * (avg - goal) / (goal - grade)).floor();
|
||||||
return (candidate * grade + avg * wsum) / (candidate + wsum) < goal ? candidate + 1 : candidate;
|
return (candidate * grade + avg * wsum) / (candidate + wsum) < goal
|
||||||
|
? candidate + 1
|
||||||
|
: candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double averageEvals(List<Grade> grades, {bool finalAvg = false}) {
|
static double averageEvals(List<Grade> grades, {bool finalAvg = false}) {
|
||||||
double average =
|
double average = grades
|
||||||
grades.map((e) => e.value.value * e.value.weight / 100.0).fold(0.0, (double a, double b) => a + b) / weightSum(grades, finalAvg: finalAvg);
|
.map((e) => e.value.value * e.value.weight / 100.0)
|
||||||
|
.fold(0.0, (double a, double b) => a + b) /
|
||||||
|
weightSum(grades, finalAvg: finalAvg);
|
||||||
return average.isNaN ? 0.0 : average;
|
return average.isNaN ? 0.0 : average;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double weightSum(List<Grade> grades, {bool finalAvg = false}) =>
|
static double weightSum(List<Grade> grades, {bool finalAvg = false}) => grades
|
||||||
grades.map((e) => finalAvg ? 1 : e.value.weight / 100).fold(0, (a, b) => a + b);
|
.map((e) => finalAvg ? 1 : e.value.weight / 100)
|
||||||
|
.fold(0, (a, b) => a + b);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
import 'package:i18n_extension/i18n_extension.dart';
|
||||||
|
|
||||||
|
extension Localization on String {
|
||||||
|
static final _t = Translations.byLocale("hu_hu") +
|
||||||
|
{
|
||||||
|
"en_en": {
|
||||||
|
"goal_planner_title": "Goal Planning",
|
||||||
|
"set_a_goal": "Your Goal",
|
||||||
|
"select_subject": "Subject",
|
||||||
|
"pick_route": "Pick a Route",
|
||||||
|
"track_it": "Track it!",
|
||||||
|
"recommended": "Recommended",
|
||||||
|
"fastest": "Fastest",
|
||||||
|
},
|
||||||
|
"hu_hu": {
|
||||||
|
"goal_planner_title": "Cél követés",
|
||||||
|
"set_a_goal": "Kitűzött cél",
|
||||||
|
"select_subject": "Tantárgy",
|
||||||
|
"pick_route": "Válassz egy utat",
|
||||||
|
"track_it": "Követés!",
|
||||||
|
"recommended": "Ajánlott",
|
||||||
|
"fastest": "Leggyorsabb",
|
||||||
|
},
|
||||||
|
"de_de": {
|
||||||
|
"goal_planner_title": "Zielplanung",
|
||||||
|
"set_a_goal": "Dein Ziel",
|
||||||
|
"select_subject": "Thema",
|
||||||
|
"pick_route": "Wähle einen Weg",
|
||||||
|
"track_it": "Verfolge es!",
|
||||||
|
"recommended": "Empfohlen",
|
||||||
|
"fastest": "Am schnellsten",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
|
import 'package:filcnaplo/models/settings.dart';
|
||||||
import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_input.dart';
|
import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_input.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class GradeDisplay extends StatelessWidget {
|
class GradeDisplay extends StatelessWidget {
|
||||||
const GradeDisplay({Key? key, required this.grade}) : super(key: key);
|
const GradeDisplay({Key? key, required this.grade}) : super(key: key);
|
||||||
@ -8,12 +10,14 @@ class GradeDisplay extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
SettingsProvider settings = Provider.of<SettingsProvider>(context);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: 36,
|
width: 36,
|
||||||
height: 36,
|
height: 36,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: gradeColor(grade).withOpacity(.3),
|
color: gradeColor(grade, settings).withOpacity(.3),
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -21,7 +25,7 @@ class GradeDisplay extends StatelessWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 22.0,
|
fontSize: 22.0,
|
||||||
color: gradeColor(grade),
|
color: gradeColor(grade, settings),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
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_planner_screen.i18n.dart';
|
||||||
import 'package:filcnaplo_premium/ui/mobile/goal_planner/grade_display.dart';
|
import 'package:filcnaplo_premium/ui/mobile/goal_planner/grade_display.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
enum RouteMark { recommended, fastest }
|
enum RouteMark { recommended, fastest }
|
||||||
|
|
||||||
class RouteOption extends StatelessWidget {
|
class RouteOption extends StatelessWidget {
|
||||||
const RouteOption({Key? key, required this.plan, this.mark, this.selected = false, required this.onSelected}) : super(key: key);
|
const RouteOption(
|
||||||
|
{Key? key,
|
||||||
|
required this.plan,
|
||||||
|
this.mark,
|
||||||
|
this.selected = false,
|
||||||
|
required this.onSelected})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
final Plan plan;
|
final Plan plan;
|
||||||
final RouteMark? mark;
|
final RouteMark? mark;
|
||||||
@ -17,20 +24,20 @@ class RouteOption extends StatelessWidget {
|
|||||||
|
|
||||||
switch (mark!) {
|
switch (mark!) {
|
||||||
case RouteMark.recommended:
|
case RouteMark.recommended:
|
||||||
return const Text("Recommended", style: style);
|
return Text("recommended".i18n, style: style);
|
||||||
case RouteMark.fastest:
|
case RouteMark.fastest:
|
||||||
return const Text("Fastest", style: style);
|
return Text("fastest".i18n, style: style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Color markColor() {
|
Color markColor(BuildContext context) {
|
||||||
switch (mark) {
|
switch (mark) {
|
||||||
case RouteMark.recommended:
|
case RouteMark.recommended:
|
||||||
return const Color(0xff6a63d4);
|
return const Color.fromARGB(255, 104, 93, 255);
|
||||||
case RouteMark.fastest:
|
case RouteMark.fastest:
|
||||||
return const Color(0xffe9d524);
|
return const Color.fromARGB(255, 255, 91, 146);
|
||||||
default:
|
default:
|
||||||
return Colors.teal;
|
return Theme.of(context).colorScheme.primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,14 +65,16 @@ class RouteOption extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
gradeWidgets.addAll(List.generate(count, (_) => GradeDisplay(grade: i)));
|
gradeWidgets
|
||||||
|
.addAll(List.generate(count, (_) => GradeDisplay(grade: i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
gradeWidgets.add(SizedBox(
|
gradeWidgets.add(SizedBox(
|
||||||
height: 36.0,
|
height: 36.0,
|
||||||
width: 32.0,
|
width: 32.0,
|
||||||
child: Center(child: Icon(Icons.add, color: Colors.black.withOpacity(.5))),
|
child: Center(
|
||||||
|
child: Icon(Icons.add, color: Colors.black.withOpacity(.5))),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,19 +86,23 @@ class RouteOption extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Card(
|
child: Card(
|
||||||
surfaceTintColor: selected ? markColor().withOpacity(.2) : Colors.white,
|
surfaceTintColor:
|
||||||
|
selected ? markColor(context).withOpacity(.2) : Colors.white,
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
elevation: 5,
|
elevation: 5,
|
||||||
shadowColor: Colors.transparent,
|
shadowColor: Colors.transparent,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(16.0),
|
borderRadius: BorderRadius.circular(16.0),
|
||||||
side: selected ? BorderSide(color: markColor(), width: 4.0) : BorderSide.none,
|
side: selected
|
||||||
|
? BorderSide(color: markColor(context), width: 4.0)
|
||||||
|
: BorderSide.none,
|
||||||
),
|
),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(16.0),
|
borderRadius: BorderRadius.circular(16.0),
|
||||||
onTap: onSelected,
|
onTap: onSelected,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(top: 16.0, bottom: 16.0, left: 20.0, right: 12.0),
|
padding: const EdgeInsets.only(
|
||||||
|
top: 16.0, bottom: 16.0, left: 20.0, right: 12.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -98,12 +111,14 @@ class RouteOption extends StatelessWidget {
|
|||||||
Chip(
|
Chip(
|
||||||
label: markLabel(),
|
label: markLabel(),
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
backgroundColor: selected ? markColor() : Colors.transparent,
|
backgroundColor:
|
||||||
|
selected ? markColor(context) : Colors.transparent,
|
||||||
labelPadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
labelPadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
labelStyle: TextStyle(color: selected ? Colors.white : null),
|
labelStyle:
|
||||||
|
TextStyle(color: selected ? Colors.white : null),
|
||||||
shape: StadiumBorder(
|
shape: StadiumBorder(
|
||||||
side: BorderSide(
|
side: BorderSide(
|
||||||
color: markColor(),
|
color: markColor(context),
|
||||||
width: 3.0,
|
width: 3.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
|
import 'package:filcnaplo/helpers/subject.dart';
|
||||||
|
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/providers/grade_provider.dart';
|
||||||
|
import 'package:filcnaplo_mobile_ui/pages/grades/calculator/grade_calculator_provider.dart';
|
||||||
import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_input.dart';
|
import 'package:filcnaplo_premium/ui/mobile/goal_planner/goal_input.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_planner_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';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:filcnaplo_mobile_ui/common/beta_chip.dart';
|
||||||
|
|
||||||
enum PlanResult {
|
enum PlanResult {
|
||||||
available, // There are possible solutions
|
available, // There are possible solutions
|
||||||
@ -12,13 +20,25 @@ enum PlanResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GoalPlannerTest extends StatefulWidget {
|
class GoalPlannerTest extends StatefulWidget {
|
||||||
const GoalPlannerTest({Key? key}) : super(key: key);
|
final Subject subject;
|
||||||
|
|
||||||
|
const GoalPlannerTest({Key? key, required this.subject}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GoalPlannerTest> createState() => _GoalPlannerTestState();
|
State<GoalPlannerTest> createState() => _GoalPlannerTestState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GoalPlannerTestState extends State<GoalPlannerTest> {
|
class _GoalPlannerTestState extends State<GoalPlannerTest> {
|
||||||
|
late GradeProvider gradeProvider;
|
||||||
|
late GradeCalculatorProvider calculatorProvider;
|
||||||
|
late SettingsProvider settingsProvider;
|
||||||
|
|
||||||
|
bool gradeCalcMode = false;
|
||||||
|
|
||||||
|
List<Grade> getSubjectGrades(Subject subject) => !gradeCalcMode
|
||||||
|
? gradeProvider.grades.where((e) => e.subject == subject).toList()
|
||||||
|
: calculatorProvider.grades.where((e) => e.subject == subject).toList();
|
||||||
|
|
||||||
double goalValue = 4.0;
|
double goalValue = 4.0;
|
||||||
List<Grade> grades = [];
|
List<Grade> grades = [];
|
||||||
|
|
||||||
@ -39,11 +59,14 @@ class _GoalPlannerTestState extends State<GoalPlannerTest> {
|
|||||||
final planner = GoalPlanner(goalValue, grades);
|
final planner = GoalPlanner(goalValue, grades);
|
||||||
final plans = planner.solve();
|
final plans = planner.solve();
|
||||||
|
|
||||||
plans.sort((a, b) => (a.avg - (2 * goalValue + 5) / 3).abs().compareTo(b.avg - (2 * goalValue + 5) / 3));
|
plans.sort((a, b) => (a.avg - (2 * goalValue + 5) / 3)
|
||||||
|
.abs()
|
||||||
|
.compareTo(b.avg - (2 * goalValue + 5) / 3));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final singleSolution = plans.every((e) => e.sigma == 0);
|
final singleSolution = plans.every((e) => e.sigma == 0);
|
||||||
recommended = plans.where((e) => singleSolution ? true : e.sigma > 0).first;
|
recommended =
|
||||||
|
plans.where((e) => singleSolution ? true : e.sigma > 0).first;
|
||||||
plans.removeWhere((e) => e == recommended);
|
plans.removeWhere((e) => e == recommended);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
@ -78,8 +101,18 @@ class _GoalPlannerTestState extends State<GoalPlannerTest> {
|
|||||||
return PlanResult.available;
|
return PlanResult.available;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void getGrades() {
|
||||||
|
grades = getSubjectGrades(widget.subject).toList();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
gradeProvider = Provider.of<GradeProvider>(context);
|
||||||
|
calculatorProvider = Provider.of<GradeCalculatorProvider>(context);
|
||||||
|
settingsProvider = Provider.of<SettingsProvider>(context);
|
||||||
|
|
||||||
|
getGrades();
|
||||||
|
|
||||||
final currentAvg = GoalPlannerHelper.averageEvals(grades);
|
final currentAvg = GoalPlannerHelper.averageEvals(grades);
|
||||||
|
|
||||||
final result = getResult();
|
final result = getResult();
|
||||||
@ -87,16 +120,42 @@ class _GoalPlannerTestState extends State<GoalPlannerTest> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.only(
|
||||||
|
left: 22.0, right: 22.0, top: 5.0, bottom: 220.0),
|
||||||
children: [
|
children: [
|
||||||
const Align(
|
Row(
|
||||||
alignment: Alignment.topLeft,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
child: BackButton(),
|
children: [
|
||||||
|
const BackButton(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 15.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'goal_planner_title'.i18n,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w500, fontSize: 18.0),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 5,
|
||||||
|
),
|
||||||
|
const BetaChip(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12.0),
|
const SizedBox(height: 12.0),
|
||||||
const Text(
|
Row(
|
||||||
"Set a goal",
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
style: TextStyle(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"set_a_goal".i18n,
|
||||||
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 20.0,
|
fontSize: 20.0,
|
||||||
),
|
),
|
||||||
@ -107,13 +166,51 @@ class _GoalPlannerTestState extends State<GoalPlannerTest> {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w900,
|
fontWeight: FontWeight.w900,
|
||||||
fontSize: 42.0,
|
fontSize: 42.0,
|
||||||
color: gradeColor(goalValue.round()),
|
color: gradeColor(goalValue.round(), settingsProvider),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"select_subject".i18n,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 20.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4.0),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
SubjectIcon.resolveVariant(
|
||||||
|
context: context,
|
||||||
|
subject: widget.subject,
|
||||||
|
),
|
||||||
|
size: 48.0,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
(widget.subject.isRenamed
|
||||||
|
? widget.subject.renamedTo
|
||||||
|
: widget.subject.name) ??
|
||||||
|
'',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 17.0,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
const SizedBox(height: 24.0),
|
const SizedBox(height: 24.0),
|
||||||
const Text(
|
Text(
|
||||||
"Pick a route",
|
"pick_route".i18n,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 20.0,
|
fontSize: 20.0,
|
||||||
),
|
),
|
||||||
@ -157,8 +254,9 @@ class _GoalPlannerTestState extends State<GoalPlannerTest> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.only(top: 24.0),
|
padding: const EdgeInsets.only(top: 24.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color.fromARGB(255, 215, 255, 242),
|
color: Colors.white,
|
||||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(24.0)),
|
borderRadius:
|
||||||
|
const BorderRadius.vertical(top: Radius.circular(24.0)),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(.1),
|
color: Colors.black.withOpacity(.1),
|
||||||
@ -183,13 +281,16 @@ class _GoalPlannerTestState extends State<GoalPlannerTest> {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: RawMaterialButton(
|
child: RawMaterialButton(
|
||||||
onPressed: () {},
|
onPressed: () {
|
||||||
fillColor: const Color(0xff01342D),
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text("Hamarosan...")));
|
||||||
|
},
|
||||||
|
fillColor: Theme.of(context).colorScheme.primary,
|
||||||
shape: const StadiumBorder(),
|
shape: const StadiumBorder(),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
child: const Text(
|
child: Text(
|
||||||
"Track it!",
|
"track_it".i18n,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 20.0,
|
fontSize: 20.0,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
@ -8,6 +8,7 @@ enum PremiumFeature {
|
|||||||
profile,
|
profile,
|
||||||
iconpack,
|
iconpack,
|
||||||
subjectrename,
|
subjectrename,
|
||||||
|
teacherrename,
|
||||||
weeklytimetable,
|
weeklytimetable,
|
||||||
goalplanner,
|
goalplanner,
|
||||||
widget,
|
widget,
|
||||||
|
@ -19,8 +19,10 @@ class PremiumIconPackSelector extends StatelessWidget {
|
|||||||
|
|
||||||
return PanelButton(
|
return PanelButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (!Provider.of<PremiumProvider>(context, listen: false).hasScope(PremiumScopes.customIcons)) {
|
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||||
PremiumLockedFeatureUpsell.show(context: context, feature: PremiumFeature.iconpack);
|
.hasScope(PremiumScopes.customIcons)) {
|
||||||
|
PremiumLockedFeatureUpsell.show(
|
||||||
|
context: context, feature: PremiumFeature.iconpack);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +30,10 @@ class PremiumIconPackSelector extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
title: Text("icon_pack".i18n),
|
title: Text("icon_pack".i18n),
|
||||||
leading: const Icon(FeatherIcons.grid),
|
leading: const Icon(FeatherIcons.grid),
|
||||||
trailing: Text(settings.iconPack.name.capital()),
|
trailing: Text(
|
||||||
|
settings.iconPack.name.capital(),
|
||||||
|
style: const TextStyle(fontSize: 14.0),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ extension SettingsLocalization on String {
|
|||||||
static final _t = Translations.byLocale("hu_hu") +
|
static final _t = Translations.byLocale("hu_hu") +
|
||||||
{
|
{
|
||||||
"en_en": {
|
"en_en": {
|
||||||
|
// subject rename
|
||||||
"renamed_subjects": "Renamed Subjects",
|
"renamed_subjects": "Renamed Subjects",
|
||||||
"rename_subjects": "Rename Subjects",
|
"rename_subjects": "Rename Subjects",
|
||||||
"rename_subject": "Rename Subject",
|
"rename_subject": "Rename Subject",
|
||||||
@ -13,21 +14,37 @@ extension SettingsLocalization on String {
|
|||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"done": "Done",
|
"done": "Done",
|
||||||
"rename_new_subject": "Rename New Subject",
|
"rename_new_subject": "Rename New Subject",
|
||||||
"italics_toggle": "Toggle Italics",
|
"italics_toggle": "Italic Font",
|
||||||
|
// teacher rename
|
||||||
|
"renamed_teachers": "Renamed Teachers",
|
||||||
|
"rename_teachers": "Rename Teachers",
|
||||||
|
"rename_teacher": "Rename Teacher",
|
||||||
|
"select_teacher": "Select Teacher",
|
||||||
|
"modify_teachers": "Modify Teachers",
|
||||||
|
"rename_new_teacher": "Rename New Teacher",
|
||||||
},
|
},
|
||||||
"hu_hu": {
|
"hu_hu": {
|
||||||
|
// subject rename
|
||||||
"renamed_subjects": "Átnevezett Tantárgyaid",
|
"renamed_subjects": "Átnevezett Tantárgyaid",
|
||||||
"rename_subjects": "Tantárgyak átnevezése",
|
"rename_subjects": "Tantárgyak átnevezése",
|
||||||
"rename_subject": "Tantárgy átnevezése",
|
"rename_subject": "Tantárgy átnevezése",
|
||||||
"select_subject": "Válassz tantárgyat",
|
"select_subject": "Válassz tantárgyat",
|
||||||
"modified_name": "Módosított név",
|
"modified_name": "Módosított név",
|
||||||
"modify_subjects": "Tantárgyak átnevezése",
|
"modify_subjects": "Tantárgyak módosítása",
|
||||||
"cancel": "Mégse",
|
"cancel": "Mégse",
|
||||||
"done": "Kész",
|
"done": "Kész",
|
||||||
"rename_new_subject": "Új Tantárgy átnevezése",
|
"rename_new_subject": "Új tantárgy átnevezése",
|
||||||
"italics_toggle": "Dőlt betűs megjelenítés",
|
"italics_toggle": "Dőlt betűs megjelenítés",
|
||||||
|
// teacher rename
|
||||||
|
"renamed_teachers": "Átnevezett tanáraid",
|
||||||
|
"rename_teachers": "Tanárok átnevezése",
|
||||||
|
"rename_teacher": "Tanár átnevezése",
|
||||||
|
"select_teacher": "Válassz tanárt",
|
||||||
|
"modify_teachers": "Tanárok módosítása",
|
||||||
|
"rename_new_teacher": "Új tanár átnevezése",
|
||||||
},
|
},
|
||||||
"de_de": {
|
"de_de": {
|
||||||
|
// subject rename
|
||||||
"renamed_subjects": "Umbenannte Fächer",
|
"renamed_subjects": "Umbenannte Fächer",
|
||||||
"rename_subjects": "Fächer umbenennen",
|
"rename_subjects": "Fächer umbenennen",
|
||||||
"rename_subject": "Fach umbenennen",
|
"rename_subject": "Fach umbenennen",
|
||||||
@ -38,6 +55,13 @@ extension SettingsLocalization on String {
|
|||||||
"done": "Erledigt",
|
"done": "Erledigt",
|
||||||
"rename_new_subject": "Neues Fach umbenennen",
|
"rename_new_subject": "Neues Fach umbenennen",
|
||||||
"italics_toggle": "Kursivschrift umschalten",
|
"italics_toggle": "Kursivschrift umschalten",
|
||||||
|
// teacher rename
|
||||||
|
"renamed_teachers": "Renamed Teachers",
|
||||||
|
"rename_teachers": "Rename Teachers",
|
||||||
|
"rename_teacher": "Rename Teacher",
|
||||||
|
"select_teacher": "Select Teacher",
|
||||||
|
"modify_teachers": "Modify Teachers",
|
||||||
|
"rename_new_teacher": "Rename New Teacher",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -19,7 +19,7 @@ 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 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'modify_subject_names.i18n.dart';
|
import 'modify_names.i18n.dart';
|
||||||
|
|
||||||
class MenuRenamedSubjects extends StatelessWidget {
|
class MenuRenamedSubjects extends StatelessWidget {
|
||||||
const MenuRenamedSubjects({Key? key, required this.settings})
|
const MenuRenamedSubjects({Key? key, required this.settings})
|
||||||
|
@ -0,0 +1,437 @@
|
|||||||
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
|
import 'package:filcnaplo/api/providers/database_provider.dart';
|
||||||
|
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||||
|
import 'package:filcnaplo/models/settings.dart';
|
||||||
|
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||||
|
import 'package:filcnaplo/utils/format.dart';
|
||||||
|
import 'package:filcnaplo_kreta_api/models/teacher.dart';
|
||||||
|
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
|
||||||
|
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
|
||||||
|
import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
|
||||||
|
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
|
||||||
|
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
|
||||||
|
import 'package:filcnaplo_premium/models/premium_scopes.dart';
|
||||||
|
import 'package:filcnaplo_premium/providers/premium_provider.dart';
|
||||||
|
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import 'modify_names.i18n.dart';
|
||||||
|
|
||||||
|
class MenuRenamedTeachers extends StatelessWidget {
|
||||||
|
const MenuRenamedTeachers({Key? key, required this.settings})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final SettingsProvider settings;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return PanelButton(
|
||||||
|
padding: const EdgeInsets.only(left: 14.0),
|
||||||
|
onPressed: () {
|
||||||
|
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||||
|
.hasScope(PremiumScopes.renameTeachers)) {
|
||||||
|
PremiumLockedFeatureUpsell.show(
|
||||||
|
context: context, feature: PremiumFeature.teacherrename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Navigator.of(context, rootNavigator: true).push(
|
||||||
|
CupertinoPageRoute(builder: (context) => const ModifyTeacherNames()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
title: Text(
|
||||||
|
"rename_teachers".i18n,
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.of(context)
|
||||||
|
.text
|
||||||
|
.withOpacity(settings.renamedTeachersEnabled ? 1.0 : .5)),
|
||||||
|
),
|
||||||
|
leading: settings.renamedTeachersEnabled
|
||||||
|
? const Icon(FeatherIcons.users)
|
||||||
|
: Icon(FeatherIcons.users,
|
||||||
|
color: AppColors.of(context).text.withOpacity(.25)),
|
||||||
|
trailingDivider: true,
|
||||||
|
trailing: Switch(
|
||||||
|
onChanged: (v) async {
|
||||||
|
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||||
|
.hasScope(PremiumScopes.renameTeachers)) {
|
||||||
|
PremiumLockedFeatureUpsell.show(
|
||||||
|
context: context, feature: PremiumFeature.teacherrename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.update(renamedTeachersEnabled: v);
|
||||||
|
await Provider.of<GradeProvider>(context, listen: false)
|
||||||
|
.convertBySettings();
|
||||||
|
await Provider.of<TimetableProvider>(context, listen: false)
|
||||||
|
.convertBySettings();
|
||||||
|
await Provider.of<AbsenceProvider>(context, listen: false)
|
||||||
|
.convertBySettings();
|
||||||
|
},
|
||||||
|
value: settings.renamedTeachersEnabled,
|
||||||
|
activeColor: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModifyTeacherNames extends StatefulWidget {
|
||||||
|
const ModifyTeacherNames({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ModifyTeacherNames> createState() => _ModifyTeacherNamesState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ModifyTeacherNamesState extends State<ModifyTeacherNames> {
|
||||||
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
final _teacherName = TextEditingController();
|
||||||
|
String? selectedTeacherId;
|
||||||
|
|
||||||
|
late List<Teacher> teachers;
|
||||||
|
late UserProvider user;
|
||||||
|
late DatabaseProvider dbProvider;
|
||||||
|
late SettingsProvider settings;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
teachers = (Provider.of<GradeProvider>(context, listen: false)
|
||||||
|
.grades
|
||||||
|
.map((e) => e.teacher)
|
||||||
|
.toSet()
|
||||||
|
.toList()
|
||||||
|
..sort((a, b) => a.name.compareTo(b.name)));
|
||||||
|
print(teachers.map((e) => e.name));
|
||||||
|
user = Provider.of<UserProvider>(context, listen: false);
|
||||||
|
dbProvider = Provider.of<DatabaseProvider>(context, listen: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, String>> fetchRenamedTeachers() async {
|
||||||
|
return await dbProvider.userQuery.renamedTeachers(userId: user.id!);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showRenameDialog() {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => StatefulBuilder(builder: (context, setS) {
|
||||||
|
return AlertDialog(
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(14.0))),
|
||||||
|
title: Text("rename_teacher".i18n),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
DropdownButton2(
|
||||||
|
items: teachers
|
||||||
|
.map((item) => DropdownMenuItem<String>(
|
||||||
|
value: item.id,
|
||||||
|
child: Text(
|
||||||
|
item.name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColors.of(context).text,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
onChanged: (String? v) async {
|
||||||
|
final renamedSubs = await fetchRenamedTeachers();
|
||||||
|
|
||||||
|
setS(() {
|
||||||
|
selectedTeacherId = v;
|
||||||
|
|
||||||
|
if (renamedSubs.containsKey(selectedTeacherId)) {
|
||||||
|
_teacherName.text = renamedSubs[selectedTeacherId]!;
|
||||||
|
} else {
|
||||||
|
_teacherName.text = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
iconSize: 14,
|
||||||
|
iconEnabledColor: AppColors.of(context).text,
|
||||||
|
iconDisabledColor: AppColors.of(context).text,
|
||||||
|
underline: const SizedBox(),
|
||||||
|
itemHeight: 40,
|
||||||
|
itemPadding: const EdgeInsets.only(left: 14, right: 14),
|
||||||
|
buttonWidth: 50,
|
||||||
|
dropdownWidth: 300,
|
||||||
|
dropdownPadding: null,
|
||||||
|
buttonDecoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
dropdownDecoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(14),
|
||||||
|
),
|
||||||
|
dropdownElevation: 8,
|
||||||
|
scrollbarRadius: const Radius.circular(40),
|
||||||
|
scrollbarThickness: 6,
|
||||||
|
scrollbarAlwaysShow: true,
|
||||||
|
offset: const Offset(-10, -10),
|
||||||
|
buttonSplashColor: Colors.transparent,
|
||||||
|
customButton: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.grey, width: 2),
|
||||||
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 12.0, horizontal: 8.0),
|
||||||
|
child: Text(
|
||||||
|
selectedTeacherId == null
|
||||||
|
? "select_teacher".i18n
|
||||||
|
: teachers
|
||||||
|
.firstWhere(
|
||||||
|
(element) => element.id == selectedTeacherId)
|
||||||
|
.name,
|
||||||
|
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: AppColors.of(context).text.withOpacity(0.75)),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 2,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Icon(FeatherIcons.arrowDown, size: 32),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: _teacherName,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide:
|
||||||
|
const BorderSide(color: Colors.grey, width: 1.5),
|
||||||
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide:
|
||||||
|
const BorderSide(color: Colors.grey, width: 1.5),
|
||||||
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
hintText: "modified_name".i18n,
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
FeatherIcons.x,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_teacherName.text = "";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text(
|
||||||
|
"cancel".i18n,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).maybePop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: Text(
|
||||||
|
"done".i18n,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
if (selectedTeacherId != null) {
|
||||||
|
final renamedSubs = await fetchRenamedTeachers();
|
||||||
|
|
||||||
|
renamedSubs[selectedTeacherId!] = _teacherName.text;
|
||||||
|
await dbProvider.userStore
|
||||||
|
.storeRenamedTeachers(renamedSubs, userId: user.id!);
|
||||||
|
await Provider.of<GradeProvider>(context, listen: false)
|
||||||
|
.convertBySettings();
|
||||||
|
await Provider.of<TimetableProvider>(context, listen: false)
|
||||||
|
.convertBySettings();
|
||||||
|
await Provider.of<AbsenceProvider>(context, listen: false)
|
||||||
|
.convertBySettings();
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
).then((val) {
|
||||||
|
_teacherName.text = "";
|
||||||
|
selectedTeacherId = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
settings = Provider.of<SettingsProvider>(context);
|
||||||
|
return Scaffold(
|
||||||
|
key: _scaffoldKey,
|
||||||
|
appBar: AppBar(
|
||||||
|
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
leading: BackButton(color: AppColors.of(context).text),
|
||||||
|
title: Text(
|
||||||
|
"modify_teachers".i18n,
|
||||||
|
style: TextStyle(color: AppColors.of(context).text),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Panel(
|
||||||
|
child: SwitchListTile(
|
||||||
|
title: Text("italics_toggle".i18n),
|
||||||
|
onChanged: (value) =>
|
||||||
|
settings.update(renamedTeachersItalics: value),
|
||||||
|
value: settings.renamedTeachersItalics,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: showRenameDialog,
|
||||||
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.grey, width: 2),
|
||||||
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 18.0, horizontal: 12.0),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
"rename_new_teacher".i18n,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 18,
|
||||||
|
color: AppColors.of(context).text.withOpacity(.85),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 30,
|
||||||
|
),
|
||||||
|
FutureBuilder<Map<String, String>>(
|
||||||
|
future: fetchRenamedTeachers(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Panel(
|
||||||
|
title: Text("renamed_teachers".i18n),
|
||||||
|
child: Column(
|
||||||
|
children: snapshot.data!.keys.map(
|
||||||
|
(key) {
|
||||||
|
Teacher? teacher = teachers
|
||||||
|
.firstWhere((element) => key == element.id);
|
||||||
|
String renameTo = snapshot.data![key]!;
|
||||||
|
return RenamedTeacherItem(
|
||||||
|
teacher: teacher,
|
||||||
|
renamedTo: renameTo,
|
||||||
|
modifyCallback: () {
|
||||||
|
setState(() {
|
||||||
|
selectedTeacherId = teacher.id;
|
||||||
|
_teacherName.text = renameTo;
|
||||||
|
});
|
||||||
|
showRenameDialog();
|
||||||
|
},
|
||||||
|
removeCallback: () {
|
||||||
|
setState(() {
|
||||||
|
Map<String, String> subs =
|
||||||
|
Map.from(snapshot.data!);
|
||||||
|
subs.remove(key);
|
||||||
|
dbProvider.userStore.storeRenamedTeachers(
|
||||||
|
subs,
|
||||||
|
userId: user.id!);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RenamedTeacherItem extends StatelessWidget {
|
||||||
|
const RenamedTeacherItem({
|
||||||
|
Key? key,
|
||||||
|
required this.teacher,
|
||||||
|
required this.renamedTo,
|
||||||
|
required this.modifyCallback,
|
||||||
|
required this.removeCallback,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final Teacher teacher;
|
||||||
|
final String renamedTo;
|
||||||
|
final void Function() modifyCallback;
|
||||||
|
final void Function() removeCallback;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListTile(
|
||||||
|
minLeadingWidth: 32.0,
|
||||||
|
dense: true,
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
onTap: () {},
|
||||||
|
leading: Icon(FeatherIcons.user,
|
||||||
|
color: AppColors.of(context).text.withOpacity(.75)),
|
||||||
|
title: InkWell(
|
||||||
|
onTap: modifyCallback,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
teacher.name.capital(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 14,
|
||||||
|
color: AppColors.of(context).text.withOpacity(.75)),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
renamedTo,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: InkWell(
|
||||||
|
onTap: removeCallback,
|
||||||
|
child: Icon(FeatherIcons.trash,
|
||||||
|
color: AppColors.of(context).red.withOpacity(.75)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user