pfp color fix

This commit is contained in:
Kima 2023-05-27 12:11:50 +02:00
parent 5c7b33e6f2
commit 85f7fb71cb
9 changed files with 463 additions and 238 deletions

View File

@ -478,7 +478,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 195; CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_TEAM = JWGEQSC9U7; DEVELOPMENT_TEAM = 48XS7JAZB7;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = reFilc; INFOPLIST_KEY_CFBundleDisplayName = reFilc;
@ -488,7 +488,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 3.6.0; MARKETING_VERSION = 3.6.0;
PRODUCT_BUNDLE_IDENTIFIER = hu.refilc.naplo; PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -510,7 +510,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JWGEQSC9U7; DEVELOPMENT_TEAM = 48XS7JAZB7;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = livecard/Info.plist; INFOPLIST_FILE = livecard/Info.plist;
@ -526,7 +526,7 @@
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = hu.refilc.naplo.livecardpro; PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -552,7 +552,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JWGEQSC9U7; DEVELOPMENT_TEAM = 48XS7JAZB7;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = livecard/Info.plist; INFOPLIST_FILE = livecard/Info.plist;
@ -567,7 +567,7 @@
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = hu.refilc.naplo.livecardpro; PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -592,7 +592,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JWGEQSC9U7; DEVELOPMENT_TEAM = 48XS7JAZB7;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = livecard/Info.plist; INFOPLIST_FILE = livecard/Info.plist;
@ -607,7 +607,7 @@
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = hu.refilc.naplo.livecardpro; PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo.livecardpro;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -736,7 +736,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 195; CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_TEAM = JWGEQSC9U7; DEVELOPMENT_TEAM = 48XS7JAZB7;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = reFilc; INFOPLIST_KEY_CFBundleDisplayName = reFilc;
@ -746,7 +746,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 3.6.0; MARKETING_VERSION = 3.6.0;
PRODUCT_BUNDLE_IDENTIFIER = hu.refilc.naplo; PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -764,7 +764,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 195; CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_TEAM = JWGEQSC9U7; DEVELOPMENT_TEAM = 48XS7JAZB7;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = reFilc; INFOPLIST_KEY_CFBundleDisplayName = reFilc;
@ -774,7 +774,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 3.6.0; MARKETING_VERSION = 3.6.0;
PRODUCT_BUNDLE_IDENTIFIER = hu.refilc.naplo; PRODUCT_BUNDLE_IDENTIFIER = com.refilc.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>BGTaskSchedulerPermittedIdentifiers</key> <key>BGTaskSchedulerPermittedIdentifiers</key>
<array> <array>
<string>com.transistorsoft.fetch</string> <string>com.transistorsoft.fetch</string>
@ -67,5 +67,5 @@
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
</dict> </dict>
</plist> </plist>

View File

@ -10,5 +10,6 @@ class ColorUtils {
return HSLColor.fromAHSL(1, hash % 360, .8, .75).toColor(); return HSLColor.fromAHSL(1, hash % 360, .8, .75).toColor();
} }
static Color foregroundColor(Color color) => color.computeLuminance() >= .5 ? Colors.black : Colors.white; static Color foregroundColor(Color color) =>
color.computeLuminance() >= .5 ? Colors.black : Colors.white;
} }

View File

@ -38,7 +38,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 {
@ -48,7 +49,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;
@ -68,7 +70,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) {
@ -93,25 +97,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();
@ -135,7 +144,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,
@ -151,7 +161,9 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
child: ProfileImage( child: ProfileImage(
heroTag: "profile", heroTag: "profile",
name: firstName, name: firstName,
backgroundColor: ColorUtils.stringToColor(user.displayName ?? "?"), backgroundColor: Theme.of(context)
.colorScheme
.primary, //ColorUtils.stringToColor(user.displayName ?? "?"),
badge: updateProvider.available, badge: updateProvider.available,
role: user.role, role: user.role,
profilePictureString: user.picture, profilePictureString: user.picture,
@ -165,7 +177,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: [
@ -178,7 +193,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))),
), ),
), ),
); );
@ -194,10 +210,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),
), ),
)); ));
} }
@ -214,7 +237,8 @@ class _AbsencesPageState extends State<AbsencesPage> with TickerProviderStateMix
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),
@ -252,10 +276,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())
],
), ),
); );
}, },
@ -282,7 +311,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(),
), ),
), ),
), ),
@ -304,7 +336,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 = "";
@ -312,18 +345,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;
@ -332,7 +373,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(
@ -368,7 +410,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 {

View File

@ -47,13 +47,21 @@ class _GradesPageState extends State<GradesPage> {
int avgDropValue = 0; int avgDropValue = 0;
List<Grade> getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider.grades List<Grade> getSubjectGrades(Subject subject, {int days = 0}) => gradeProvider
.where( .grades
(e) => e.subject == subject && e.type == GradeType.midYear && (days == 0 || e.date.isBefore(DateTime.now().subtract(Duration(days: days))))) .where((e) =>
e.subject == subject &&
e.type == GradeType.midYear &&
(days == 0 ||
e.date.isBefore(DateTime.now().subtract(Duration(days: days)))))
.toList(); .toList();
void generateTiles() { void generateTiles() {
List<Subject> subjects = gradeProvider.grades.map((e) => e.subject).toSet().toList()..sort((a, b) => a.name.compareTo(b.name)); List<Subject> subjects = gradeProvider.grades
.map((e) => e.subject)
.toSet()
.toList()
..sort((a, b) => a.name.compareTo(b.name));
List<Widget> tiles = []; List<Widget> tiles = [];
Map<Subject, double> subjectAvgs = {}; Map<Subject, double> subjectAvgs = {};
@ -65,11 +73,15 @@ class _GradesPageState extends State<GradesPage> {
double averageBefore = 0.0; double averageBefore = 0.0;
if (avgDropValue != 0) { if (avgDropValue != 0) {
List<Grade> gradesBefore = getSubjectGrades(subject, days: avgDropValue); List<Grade> gradesBefore =
averageBefore = avgDropValue == 0 ? 0.0 : AverageHelper.averageEvals(gradesBefore); getSubjectGrades(subject, days: avgDropValue);
averageBefore =
avgDropValue == 0 ? 0.0 : AverageHelper.averageEvals(gradesBefore);
} }
var nullavg = GroupAverage(average: 0.0, subject: subject, uid: "0"); var nullavg = GroupAverage(average: 0.0, subject: subject, uid: "0");
double groupAverage = gradeProvider.groupAverages.firstWhere((e) => e.subject == subject, orElse: () => nullavg).average; double groupAverage = gradeProvider.groupAverages
.firstWhere((e) => e.subject == subject, orElse: () => nullavg)
.average;
if (avg != 0) subjectAvgs[subject] = avg; if (avg != 0) subjectAvgs[subject] = avg;
@ -79,7 +91,8 @@ class _GradesPageState extends State<GradesPage> {
average: avg, average: avg,
groupAverage: avgDropValue == 0 ? groupAverage : 0.0, groupAverage: avgDropValue == 0 ? groupAverage : 0.0,
onTap: () { onTap: () {
GradeSubjectView(subject, groupAverage: groupAverage).push(context, root: true); GradeSubjectView(subject, groupAverage: groupAverage)
.push(context, root: true);
}, },
); );
})); }));
@ -88,7 +101,12 @@ class _GradesPageState extends State<GradesPage> {
tiles.insert(0, yearlyGraph); tiles.insert(0, yearlyGraph);
tiles.insert(1, gradesCount); tiles.insert(1, gradesCount);
tiles.insert(2, FailWarning(subjectAvgs: subjectAvgs)); tiles.insert(2, FailWarning(subjectAvgs: subjectAvgs));
tiles.insert(3, PanelTitle(title: Text(avgDropValue == 0 ? "Subjects".i18n : "Subjects_changes".i18n))); tiles.insert(
3,
PanelTitle(
title: Text(avgDropValue == 0
? "Subjects".i18n
: "Subjects_changes".i18n)));
tiles.insert(4, const PanelHeader(padding: EdgeInsets.only(top: 12.0))); tiles.insert(4, const PanelHeader(padding: EdgeInsets.only(top: 12.0)));
tiles.add(const PanelFooter(padding: EdgeInsets.only(bottom: 12.0))); tiles.add(const PanelFooter(padding: EdgeInsets.only(bottom: 12.0)));
tiles.add(const Padding(padding: EdgeInsets.only(bottom: 24.0))); tiles.add(const Padding(padding: EdgeInsets.only(bottom: 24.0)));
@ -102,9 +120,15 @@ class _GradesPageState extends State<GradesPage> {
); );
} }
double subjectAvg = subjectAvgs.isNotEmpty ? subjectAvgs.values.fold(0.0, (double a, double b) => a + b) / subjectAvgs.length : 0.0; double subjectAvg = subjectAvgs.isNotEmpty
? subjectAvgs.values.fold(0.0, (double a, double b) => a + b) /
subjectAvgs.length
: 0.0;
final double classAvg = gradeProvider.groupAverages.isNotEmpty final double classAvg = gradeProvider.groupAverages.isNotEmpty
? gradeProvider.groupAverages.map((e) => e.average).fold(0.0, (double a, double b) => a + b) / gradeProvider.groupAverages.length ? gradeProvider.groupAverages
.map((e) => e.average)
.fold(0.0, (double a, double b) => a + b) /
gradeProvider.groupAverages.length
: 0.0; : 0.0;
if (subjectAvg > 0) { if (subjectAvg > 0) {
@ -169,18 +193,31 @@ class _GradesPageState extends State<GradesPage> {
final double totalClassAvg = gradeProvider.groupAverages.isEmpty final double totalClassAvg = gradeProvider.groupAverages.isEmpty
? 0.0 ? 0.0
: gradeProvider.groupAverages.map((e) => e.average).fold(0.0, (double a, double b) => a + b) / gradeProvider.groupAverages.length; : gradeProvider.groupAverages
.map((e) => e.average)
.fold(0.0, (double a, double b) => a + b) /
gradeProvider.groupAverages.length;
final now = gradeProvider.grades.isNotEmpty ? gradeProvider.grades.reduce((v, e) => e.date.isAfter(v.date) ? e : v).date : DateTime.now(); final now = gradeProvider.grades.isNotEmpty
? gradeProvider.grades
.reduce((v, e) => e.date.isAfter(v.date) ? e : v)
.date
: DateTime.now();
final currentStudentAvg = AverageHelper.averageEvals(gradeProvider.grades.where((e) => e.type == GradeType.midYear).toList()); final currentStudentAvg = AverageHelper.averageEvals(gradeProvider.grades
.where((e) => e.type == GradeType.midYear)
.toList());
final prevStudentAvg = AverageHelper.averageEvals(gradeProvider.grades final prevStudentAvg = AverageHelper.averageEvals(gradeProvider.grades
.where((e) => e.type == GradeType.midYear) .where((e) => e.type == GradeType.midYear)
.where((e) => e.date.isBefore(now.subtract(const Duration(days: 30)))) .where((e) => e.date.isBefore(now.subtract(const Duration(days: 30))))
.toList()); .toList());
List<Grade> graphGrades = gradeProvider.grades List<Grade> graphGrades = gradeProvider.grades
.where((e) => e.type == GradeType.midYear && (avgDropValue == 0 || e.date.isAfter(DateTime.now().subtract(Duration(days: avgDropValue))))) .where((e) =>
e.type == GradeType.midYear &&
(avgDropValue == 0 ||
e.date.isAfter(
DateTime.now().subtract(Duration(days: avgDropValue)))))
.toList(); .toList();
yearlyGraph = Padding( yearlyGraph = Padding(
@ -201,15 +238,20 @@ class _GradesPageState extends State<GradesPage> {
children: [ children: [
// if (totalClassAvg >= 1.0) AverageDisplay(average: totalClassAvg, border: true), // if (totalClassAvg >= 1.0) AverageDisplay(average: totalClassAvg, border: true),
// const SizedBox(width: 4.0), // const SizedBox(width: 4.0),
TrendDisplay(previous: prevStudentAvg, current: currentStudentAvg), TrendDisplay(
if (gradeProvider.grades.where((e) => e.type == GradeType.midYear).isNotEmpty) AverageDisplay(average: currentStudentAvg), previous: prevStudentAvg, current: currentStudentAvg),
if (gradeProvider.grades
.where((e) => e.type == GradeType.midYear)
.isNotEmpty)
AverageDisplay(average: currentStudentAvg),
], ],
) )
], ],
), ),
child: Container( child: Container(
padding: const EdgeInsets.only(top: 12.0, right: 12.0), padding: const EdgeInsets.only(top: 12.0, right: 12.0),
child: GradeGraph(graphGrades, dayThreshold: 2, classAvg: totalClassAvg), child:
GradeGraph(graphGrades, dayThreshold: 2, classAvg: totalClassAvg),
), ),
), ),
); );
@ -225,7 +267,8 @@ class _GradesPageState extends State<GradesPage> {
body: Padding( body: Padding(
padding: const EdgeInsets.only(top: 9.0), padding: const EdgeInsets.only(top: 9.0),
child: NestedScrollView( child: NestedScrollView(
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
headerSliverBuilder: (context, _) => [ headerSliverBuilder: (context, _) => [
SliverAppBar( SliverAppBar(
centerTitle: false, centerTitle: false,
@ -241,7 +284,9 @@ class _GradesPageState extends State<GradesPage> {
child: ProfileImage( child: ProfileImage(
heroTag: "profile", heroTag: "profile",
name: firstName, name: firstName,
backgroundColor: ColorUtils.stringToColor(user.displayName ?? "?"), backgroundColor: Theme.of(context)
.colorScheme
.primary, //ColorUtils.stringToColor(user.displayName ?? "?"),
badge: updateProvider.available, badge: updateProvider.available,
role: user.role, role: user.role,
profilePictureString: user.picture, profilePictureString: user.picture,
@ -254,7 +299,10 @@ class _GradesPageState extends State<GradesPage> {
padding: const EdgeInsets.only(left: 8.0), padding: const EdgeInsets.only(left: 8.0),
child: Text( child: Text(
"Grades".i18n, "Grades".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),
), ),
), ),
shadowColor: Theme.of(context).shadowColor, shadowColor: Theme.of(context).shadowColor,
@ -269,7 +317,8 @@ class _GradesPageState extends State<GradesPage> {
itemCount: max(subjectTiles.length, 1), itemCount: max(subjectTiles.length, 1),
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (subjectTiles.isNotEmpty) { if (subjectTiles.isNotEmpty) {
EdgeInsetsGeometry panelPadding = const EdgeInsets.symmetric(horizontal: 24.0); EdgeInsetsGeometry panelPadding =
const EdgeInsets.symmetric(horizontal: 24.0);
if (subjectTiles[index].runtimeType == GradeSubjectTile) { if (subjectTiles[index].runtimeType == GradeSubjectTile) {
return Padding( return Padding(
@ -279,7 +328,8 @@ class _GradesPageState extends State<GradesPage> {
padding: const EdgeInsets.symmetric(horizontal: 8.0), padding: const EdgeInsets.symmetric(horizontal: 8.0),
)); ));
} else { } else {
return Padding(padding: panelPadding, child: subjectTiles[index]); return Padding(
padding: panelPadding, child: subjectTiles[index]);
} }
} else { } else {
return Container(); return Container();

View File

@ -73,9 +73,11 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
_pageController = PageController(); _pageController = PageController();
user = Provider.of<UserProvider>(context, listen: false); user = Provider.of<UserProvider>(context, listen: false);
_liveCard = Provider.of<LiveCardProvider>(context, listen: false); _liveCard = Provider.of<LiveCardProvider>(context, listen: false);
_liveCardAnimation = AnimationController(vsync: this, duration: const Duration(milliseconds: 500)); _liveCardAnimation = AnimationController(
vsync: this, duration: const Duration(milliseconds: 500));
_liveCardAnimation.animateTo(_liveCard.show ? 1.0 : 0.0, duration: Duration.zero); _liveCardAnimation.animateTo(_liveCard.show ? 1.0 : 0.0,
duration: Duration.zero);
listOrder = List.generate(pageCount, (index) => "$index"); listOrder = List.generate(pageCount, (index) => "$index");
} }
@ -93,21 +95,29 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
void setGreeting() { void setGreeting() {
DateTime now = DateTime.now(); DateTime now = DateTime.now();
if (now.isBefore(DateTime(now.year, DateTime.august, 31)) && now.isAfter(DateTime(now.year, DateTime.june, 14))) { if (now.isBefore(DateTime(now.year, DateTime.august, 31)) &&
now.isAfter(DateTime(now.year, DateTime.june, 14))) {
greeting = "goodrest"; greeting = "goodrest";
if (NavigationScreen.of(context)?.init("confetti") ?? false) { if (NavigationScreen.of(context)?.init("confetti") ?? false) {
_confettiController = ConfettiController(duration: const Duration(seconds: 1)); _confettiController =
Future.delayed(const Duration(seconds: 1)).then((value) => mounted ? _confettiController?.play() : null); ConfettiController(duration: const Duration(seconds: 1));
Future.delayed(const Duration(seconds: 1))
.then((value) => mounted ? _confettiController?.play() : null);
} }
} else if (now.month == user.student?.birth.month && now.day == user.student?.birth.day) { } else if (now.month == user.student?.birth.month &&
now.day == user.student?.birth.day) {
greeting = "happybirthday"; greeting = "happybirthday";
if (NavigationScreen.of(context)?.init("confetti") ?? false) { if (NavigationScreen.of(context)?.init("confetti") ?? false) {
_confettiController = ConfettiController(duration: const Duration(seconds: 3)); _confettiController =
Future.delayed(const Duration(seconds: 1)).then((value) => mounted ? _confettiController?.play() : null); ConfettiController(duration: const Duration(seconds: 3));
Future.delayed(const Duration(seconds: 1))
.then((value) => mounted ? _confettiController?.play() : null);
} }
} else if (now.month == DateTime.december && now.day >= 24 && now.day <= 26) { } else if (now.month == DateTime.december &&
now.day >= 24 &&
now.day <= 26) {
greeting = "merryxmas"; greeting = "merryxmas";
} else if (now.month == DateTime.january && now.day == 1) { } else if (now.month == DateTime.january && now.day == 1) {
greeting = "happynewyear"; greeting = "happynewyear";
@ -149,14 +159,16 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
Padding( 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, _) => [
AnimatedBuilder( AnimatedBuilder(
animation: _liveCardAnimation, animation: _liveCardAnimation,
builder: (context, child) { builder: (context, child) {
return SliverAppBar( return SliverAppBar(
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor, surfaceTintColor:
Theme.of(context).scaffoldBackgroundColor,
centerTitle: false, centerTitle: false,
titleSpacing: 0.0, titleSpacing: 0.0,
// Welcome text // Welcome text
@ -168,7 +180,10 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 18.0, fontSize: 18.0,
color: Theme.of(context).textTheme.bodyMedium?.color, color: Theme.of(context)
.textTheme
.bodyMedium
?.color,
), ),
), ),
), ),
@ -180,9 +195,11 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
child: ProfileImage( child: ProfileImage(
heroTag: "profile", heroTag: "profile",
name: firstName, name: firstName,
backgroundColor: !settings.presentationMode backgroundColor: Theme.of(context)
? ColorUtils.stringToColor(user.displayName ?? "?") .colorScheme
: Theme.of(context).colorScheme.secondary, .primary, //!settings.presentationMode
//? ColorUtils.stringToColor(user.displayName ?? "?")
//: Theme.of(context).colorScheme.secondary,
badge: updateProvider.available, badge: updateProvider.available,
role: user.role, role: user.role,
profilePictureString: user.picture, profilePictureString: user.picture,
@ -199,7 +216,8 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: 24.0, left: 24.0,
right: 24.0, right: 24.0,
top: 58.0 + MediaQuery.of(context).padding.top, top:
58.0 + MediaQuery.of(context).padding.top,
bottom: 52.0, bottom: 52.0,
), ),
child: Transform.scale( child: Transform.scale(
@ -223,11 +241,15 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
], ],
controller: _tabController, controller: _tabController,
onTap: (i) async { onTap: (i) async {
int selectedPage = _pageController.page!.round(); int selectedPage =
_pageController.page!.round();
if (i == selectedPage) return; if (i == selectedPage) return;
if (_pageController.page?.roundToDouble() != _pageController.page) { if (_pageController.page?.roundToDouble() !=
_pageController.animateToPage(i, curve: Curves.easeIn, duration: kTabScrollDuration); _pageController.page) {
_pageController.animateToPage(i,
curve: Curves.easeIn,
duration: kTabScrollDuration);
return; return;
} }
@ -252,14 +274,22 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
child: NotificationListener<ScrollNotification>( child: NotificationListener<ScrollNotification>(
onNotification: (notification) { onNotification: (notification) {
// from flutter source // from flutter source
if (notification is ScrollUpdateNotification && !_tabController.indexIsChanging) { if (notification is ScrollUpdateNotification &&
if ((_pageController.page! - _tabController.index).abs() > 1.0) { !_tabController.indexIsChanging) {
if ((_pageController.page! - _tabController.index)
.abs() >
1.0) {
_tabController.index = _pageController.page!.floor(); _tabController.index = _pageController.page!.floor();
} }
_tabController.offset = (_pageController.page! - _tabController.index).clamp(-1.0, 1.0); _tabController.offset =
(_pageController.page! - _tabController.index)
.clamp(-1.0, 1.0);
} else if (notification is ScrollEndNotification) { } else if (notification is ScrollEndNotification) {
_tabController.index = _pageController.page!.round(); _tabController.index = _pageController.page!.round();
if (!_tabController.indexIsChanging) _tabController.offset = (_pageController.page! - _tabController.index).clamp(-1.0, 1.0); if (!_tabController.indexIsChanging)
_tabController.offset =
(_pageController.page! - _tabController.index)
.clamp(-1.0, 1.0);
} }
return false; return false;
}, },
@ -269,33 +299,44 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
(BuildContext context, int index) { (BuildContext context, int index) {
return FutureBuilder<List<DateWidget>>( return FutureBuilder<List<DateWidget>>(
key: ValueKey<String>(listOrder[index]), key: ValueKey<String>(listOrder[index]),
future: getFilterWidgets(homeFilters[index], context: context), future: getFilterWidgets(homeFilters[index],
builder: (context, dateWidgets) => dateWidgets.data != null context: context),
builder: (context, dateWidgets) => dateWidgets
.data !=
null
? RefreshIndicator( ? RefreshIndicator(
color: Theme.of(context).colorScheme.secondary, color:
Theme.of(context).colorScheme.secondary,
onRefresh: () => syncAll(context), onRefresh: () => syncAll(context),
child: ImplicitlyAnimatedList<Widget>( child: ImplicitlyAnimatedList<Widget>(
items: [ items: [
if (index == 0) const SizedBox(key: Key("\$premium")), if (index == 0)
...sortDateWidgets(context, dateWidgets: dateWidgets.data!), const SizedBox(key: Key("\$premium")),
...sortDateWidgets(context,
dateWidgets: dateWidgets.data!),
], ],
itemBuilder: filterItemBuilder, itemBuilder: filterItemBuilder,
spawnIsolate: false, spawnIsolate: false,
areItemsTheSame: (a, b) => a.key == b.key, areItemsTheSame: (a, b) => a.key == b.key,
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), physics: const BouncingScrollPhysics(
padding: const EdgeInsets.symmetric(horizontal: 24.0), parent:
AlwaysScrollableScrollPhysics()),
padding: const EdgeInsets.symmetric(
horizontal: 24.0),
)) ))
: Container(), : Container(),
); );
}, },
childCount: 4, childCount: 4,
findChildIndexCallback: (Key key) { findChildIndexCallback: (Key key) {
final ValueKey<String> valueKey = key as ValueKey<String>; final ValueKey<String> valueKey =
key as ValueKey<String>;
final String data = valueKey.value; final String data = valueKey.value;
return listOrder.indexOf(data); return listOrder.indexOf(data);
}, },
), ),
physics: const PageScrollPhysics().applyTo(const BouncingScrollPhysics()), physics: const PageScrollPhysics()
.applyTo(const BouncingScrollPhysics()),
), ),
), ),
)), )),

View File

@ -24,7 +24,8 @@ class MessagesPage extends StatefulWidget {
_MessagesPageState createState() => _MessagesPageState(); _MessagesPageState createState() => _MessagesPageState();
} }
class _MessagesPageState extends State<MessagesPage> with TickerProviderStateMixin { class _MessagesPageState extends State<MessagesPage>
with TickerProviderStateMixin {
late UserProvider user; late UserProvider user;
late MessageProvider messageProvider; late MessageProvider messageProvider;
late UpdateProvider updateProvider; late UpdateProvider updateProvider;
@ -51,7 +52,8 @@ class _MessagesPageState extends State<MessagesPage> 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,
@ -67,7 +69,9 @@ class _MessagesPageState extends State<MessagesPage> with TickerProviderStateMix
child: ProfileImage( child: ProfileImage(
heroTag: "profile", heroTag: "profile",
name: firstName, name: firstName,
backgroundColor: ColorUtils.stringToColor(user.displayName ?? "?"), backgroundColor: Theme.of(context)
.colorScheme
.primary, //ColorUtils.stringToColor(user.displayName ?? "?"),
badge: updateProvider.available, badge: updateProvider.available,
role: user.role, role: user.role,
profilePictureString: user.picture, profilePictureString: user.picture,
@ -81,7 +85,10 @@ class _MessagesPageState extends State<MessagesPage> with TickerProviderStateMix
padding: const EdgeInsets.only(left: 8.0), padding: const EdgeInsets.only(left: 8.0),
child: Text( child: Text(
"Messages".i18n, "Messages".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: [
@ -95,7 +102,8 @@ class _MessagesPageState extends State<MessagesPage> with TickerProviderStateMix
body: TabBarView( body: TabBarView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
controller: tabController, controller: tabController,
children: List.generate(4, (index) => filterViewBuilder(context, index))), children: List.generate(
4, (index) => filterViewBuilder(context, index))),
), ),
), ),
); );
@ -149,7 +157,9 @@ class _MessagesPageState extends State<MessagesPage> with TickerProviderStateMix
} }
Widget filterViewBuilder(context, int activeData) { Widget filterViewBuilder(context, int activeData) {
List<Widget> filterWidgets = sortDateWidgets(context, dateWidgets: getFilterWidgets(MessageType.values[activeData]), hasShadow: true); List<Widget> filterWidgets = sortDateWidgets(context,
dateWidgets: getFilterWidgets(MessageType.values[activeData]),
hasShadow: true);
return Padding( return Padding(
padding: const EdgeInsets.only(top: 12.0), padding: const EdgeInsets.only(top: 12.0),
@ -167,7 +177,8 @@ class _MessagesPageState extends State<MessagesPage> with TickerProviderStateMix
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) => filterWidgets.isNotEmpty itemBuilder: (context, index) => filterWidgets.isNotEmpty
? Padding( ? Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 6.0), padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 6.0),
child: filterWidgets[index], child: filterWidgets[index],
) )
: Empty(subtitle: "empty".i18n), : Empty(subtitle: "empty".i18n),

View File

@ -31,14 +31,17 @@ import 'timetable_page.i18n.dart';
// todo: "fix" overflow (priority: -1) // todo: "fix" overflow (priority: -1)
class TimetablePage extends StatefulWidget { class TimetablePage extends StatefulWidget {
const TimetablePage({Key? key, this.initialDay, this.initialWeek}) : super(key: key); const TimetablePage({Key? key, this.initialDay, this.initialWeek})
: super(key: key);
final DateTime? initialDay; final DateTime? initialDay;
final Week? initialWeek; final Week? initialWeek;
static void jump(BuildContext context, {Week? week, DateTime? day, Lesson? lesson}) { static void jump(BuildContext context,
{Week? week, DateTime? day, Lesson? lesson}) {
// Go to timetable page with arguments // Go to timetable page with arguments
NavigationScreen.of(context)?.customRoute(navigationPageRoute((context) => TimetablePage( NavigationScreen.of(context)
?.customRoute(navigationPageRoute((context) => TimetablePage(
initialDay: lesson?.date ?? day, initialDay: lesson?.date ?? day,
initialWeek: lesson?.date != null initialWeek: lesson?.date != null
? Week.fromDate(lesson!.date) ? Week.fromDate(lesson!.date)
@ -57,7 +60,8 @@ class TimetablePage extends StatefulWidget {
_TimetablePageState createState() => _TimetablePageState(); _TimetablePageState createState() => _TimetablePageState();
} }
class _TimetablePageState extends State<TimetablePage> with TickerProviderStateMixin, WidgetsBindingObserver { class _TimetablePageState extends State<TimetablePage>
with TickerProviderStateMixin, WidgetsBindingObserver {
late UserProvider user; late UserProvider user;
late TimetableProvider timetableProvider; late TimetableProvider timetableProvider;
late UpdateProvider updateProvider; late UpdateProvider updateProvider;
@ -68,7 +72,8 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
int _getDayIndex(DateTime date) { int _getDayIndex(DateTime date) {
int index = 0; int index = 0;
if (_controller.days == null || (_controller.days?.isEmpty ?? true)) return index; if (_controller.days == null || (_controller.days?.isEmpty ?? true))
return index;
// find the first day with upcoming lessons // find the first day with upcoming lessons
index = _controller.days!.indexWhere((day) => day.last.end.isAfter(date)); index = _controller.days!.indexWhere((day) => day.last.end.isAfter(date));
@ -110,11 +115,14 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
_tabController = TabController( _tabController = TabController(
length: _controller.days!.length, length: _controller.days!.length,
vsync: this, vsync: this,
initialIndex: min(_tabController.index, max(_controller.days!.length - 1, 0)), initialIndex:
min(_tabController.index, max(_controller.days!.length - 1, 0)),
); );
if (initial || _controller.previousWeekId != _controller.currentWeekId) { if (initial ||
_tabController.animateTo(_getDayIndex(widget.initialDay ?? DateTime.now())); _controller.previousWeekId != _controller.currentWeekId) {
_tabController
.animateTo(_getDayIndex(widget.initialDay ?? DateTime.now()));
} }
initial = false; initial = false;
@ -127,7 +135,8 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
if (widget.initialWeek != null) { if (widget.initialWeek != null) {
_controller.jump(widget.initialWeek!, context: context, initial: true); _controller.jump(widget.initialWeek!, context: context, initial: true);
} else { } else {
_controller.jump(_controller.currentWeek, context: context, initial: true, skip: true); _controller.jump(_controller.currentWeek,
context: context, initial: true, skip: true);
} }
} }
@ -152,7 +161,8 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
// Sometimes when changing weeks really fast, // Sometimes when changing weeks really fast,
// controller.days might be null or won't include index // controller.days might be null or won't include index
try { try {
return DateFormat("EEEE", I18n.of(context).locale.languageCode).format(_controller.days![index].first.date); return DateFormat("EEEE", I18n.of(context).locale.languageCode)
.format(_controller.days![index].first.date);
} catch (e) { } catch (e) {
return "timetable".i18n; return "timetable".i18n;
} }
@ -172,11 +182,15 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
body: Padding( body: Padding(
padding: const EdgeInsets.only(top: 9.0), padding: const EdgeInsets.only(top: 9.0),
child: RefreshIndicator( child: RefreshIndicator(
onRefresh: () => mounted ? _controller.jump(_controller.currentWeek, context: context, loader: false) : Future.value(null), onRefresh: () => mounted
? _controller.jump(_controller.currentWeek,
context: context, loader: false)
: Future.value(null),
color: Theme.of(context).colorScheme.secondary, color: Theme.of(context).colorScheme.secondary,
edgeOffset: 132.0, edgeOffset: 132.0,
child: NestedScrollView( child: NestedScrollView(
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
headerSliverBuilder: (context, _) => [ headerSliverBuilder: (context, _) => [
SliverAppBar( SliverAppBar(
centerTitle: false, centerTitle: false,
@ -194,7 +208,9 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
child: ProfileImage( child: ProfileImage(
heroTag: "profile", heroTag: "profile",
name: firstName, name: firstName,
backgroundColor: ColorUtils.stringToColor(user.displayName ?? "?"), backgroundColor: Theme.of(context)
.colorScheme
.primary, //ColorUtils.stringToColor(user.displayName ?? "?"),
badge: updateProvider.available, badge: updateProvider.available,
role: user.role, role: user.role,
profilePictureString: user.picture, profilePictureString: user.picture,
@ -205,7 +221,8 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
// Current day text // Current day text
title: PageTransitionSwitcher( title: PageTransitionSwitcher(
reverse: _controller.currentWeekId < _controller.previousWeekId, reverse:
_controller.currentWeekId < _controller.previousWeekId,
transitionBuilder: ( transitionBuilder: (
Widget child, Widget child,
Animation<double> primaryAnimation, Animation<double> primaryAnimation,
@ -229,8 +246,9 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
child: Row( child: Row(
children: [ children: [
() { () {
final show = final show = _controller.days == null ||
_controller.days == null || (_controller.loadType != LoadType.offline && _controller.loadType != LoadType.online); (_controller.loadType != LoadType.offline &&
_controller.loadType != LoadType.online);
const duration = Duration(milliseconds: 150); const duration = Duration(milliseconds: 150);
return AnimatedOpacity( return AnimatedOpacity(
opacity: show ? 1.0 : 0.0, opacity: show ? 1.0 : 0.0,
@ -249,7 +267,8 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
}(), }(),
() { () {
if ((_controller.days?.length ?? 0) > 0) { if ((_controller.days?.length ?? 0) > 0) {
return DayTitle(controller: _tabController, dayTitle: dayTitle); return DayTitle(
controller: _tabController, dayTitle: dayTitle);
} else { } else {
return Text( return Text(
"timetable".i18n, "timetable".i18n,
@ -292,10 +311,12 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
_controller.jump( _controller.jump(
_controller.currentWeek, _controller.currentWeek,
context: context, context: context,
loader: _controller.currentWeekId != _controller.previousWeekId, loader: _controller.currentWeekId !=
_controller.previousWeekId,
); );
} }
_tabController.animateTo(_getDayIndex(DateTime.now())); _tabController
.animateTo(_getDayIndex(DateTime.now()));
}), }),
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
@ -304,12 +325,22 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
"week".i18n + "week".i18n +
" (" + " (" +
// Week start // Week start
DateFormat((_controller.currentWeek.start.year != DateTime.now().year ? "yy. " : "") + "MMM d.", DateFormat(
(_controller.currentWeek.start.year !=
DateTime.now().year
? "yy. "
: "") +
"MMM d.",
I18n.of(context).locale.languageCode) I18n.of(context).locale.languageCode)
.format(_controller.currentWeek.start) + .format(_controller.currentWeek.start) +
" - " + " - " +
// Week end // Week end
DateFormat((_controller.currentWeek.start.year != DateTime.now().year ? "yy. " : "") + "MMM d.", DateFormat(
(_controller.currentWeek.start.year !=
DateTime.now().year
? "yy. "
: "") +
"MMM d.",
I18n.of(context).locale.languageCode) I18n.of(context).locale.languageCode)
.format(_controller.currentWeek.end) + .format(_controller.currentWeek.end) +
")", ")",
@ -365,41 +396,70 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
children: List.generate( children: List.generate(
_controller.days!.length, _controller.days!.length,
(tab) => RefreshIndicator( (tab) => RefreshIndicator(
onRefresh: () => onRefresh: () => mounted
mounted ? _controller.jump(_controller.currentWeek, context: context, loader: false) : Future.value(null), ? _controller.jump(
color: Theme.of(context).colorScheme.secondary, _controller.currentWeek,
context: context,
loader: false)
: Future.value(null),
color: Theme.of(context)
.colorScheme
.secondary,
child: ListView.builder( child: ListView.builder(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
itemCount: _controller.days![tab].length + 2, itemCount:
_controller.days![tab].length + 2,
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (_controller.days == null) return Container(); if (_controller.days == null)
return Container();
// Header // Header
if (index == 0) { if (index == 0) {
return const Padding( return const Padding(
padding: EdgeInsets.only(top: 8.0, left: 24.0, right: 24.0), padding: EdgeInsets.only(
child: PanelHeader(padding: EdgeInsets.only(top: 12.0)), top: 8.0,
left: 24.0,
right: 24.0),
child: PanelHeader(
padding: EdgeInsets.only(
top: 12.0)),
); );
} }
// Footer // Footer
if (index == _controller.days![tab].length + 1) { if (index ==
_controller.days![tab].length +
1) {
return const Padding( return const Padding(
padding: EdgeInsets.only(bottom: 8.0, left: 24.0, right: 24.0), padding: EdgeInsets.only(
child: PanelFooter(padding: EdgeInsets.only(top: 12.0)), bottom: 8.0,
left: 24.0,
right: 24.0),
child: PanelFooter(
padding: EdgeInsets.only(
top: 12.0)),
); );
} }
// Body // Body
final Lesson lesson = _controller.days![tab][index - 1]; final Lesson lesson =
final bool swapDescDay = _controller.days![tab].map((l) => l.swapDesc ? 1 : 0).reduce((a, b) => a + b) >= _controller.days![tab][index - 1];
_controller.days![tab].length * .5; final bool swapDescDay = _controller
.days![tab]
.map(
(l) => l.swapDesc ? 1 : 0)
.reduce((a, b) => a + b) >=
_controller.days![tab].length *
.5;
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0), padding: const EdgeInsets.symmetric(
horizontal: 24.0),
child: PanelBody( child: PanelBody(
padding: const EdgeInsets.symmetric(horizontal: 10.0), padding:
const EdgeInsets.symmetric(
horizontal: 10.0),
child: LessonViewable( child: LessonViewable(
lesson, lesson,
swapDesc: swapDescDay, swapDesc: swapDescDay,
@ -425,32 +485,48 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
// Label // Label
labelPadding: EdgeInsets.zero, labelPadding: EdgeInsets.zero,
labelColor: Theme.of(context).colorScheme.secondary, labelColor: Theme.of(context).colorScheme.secondary,
unselectedLabelColor: AppColors.of(context).text.withOpacity(0.9), unselectedLabelColor:
AppColors.of(context).text.withOpacity(0.9),
// Indicator // Indicator
indicatorSize: TabBarIndicatorSize.tab, indicatorSize: TabBarIndicatorSize.tab,
indicatorPadding: EdgeInsets.zero, indicatorPadding: EdgeInsets.zero,
indicator: BoxDecoration( indicator: BoxDecoration(
color: Theme.of(context).colorScheme.secondary.withOpacity(0.25), color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(0.25),
borderRadius: BorderRadius.circular(45.0), borderRadius: BorderRadius.circular(45.0),
), ),
overlayColor: MaterialStateProperty.all(const Color(0x00000000)), overlayColor: MaterialStateProperty.all(
const Color(0x00000000)),
// Tabs // Tabs
padding: const EdgeInsets.symmetric(vertical: 6.0, horizontal: 8.0), padding: const EdgeInsets.symmetric(
vertical: 6.0, horizontal: 8.0),
tabs: List.generate(_tabController.length, (index) { tabs: List.generate(_tabController.length, (index) {
String label = DateFormat("E", I18n.of(context).locale.languageCode).format(_controller.days![index].first.date); String label = DateFormat(
"E", I18n.of(context).locale.languageCode)
.format(_controller.days![index].first.date);
return Tab( return Tab(
height: 46.0, height: 46.0,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
if (_sameDate(_controller.days![index].first.date, DateTime.now())) if (_sameDate(
_controller.days![index].first.date,
DateTime.now()))
Padding( Padding(
padding: const EdgeInsets.only(top: 4.0), padding: const EdgeInsets.only(top: 4.0),
child: Dot(size: 4.0, color: Theme.of(context).colorScheme.secondary), child: Dot(
size: 4.0,
color: Theme.of(context)
.colorScheme
.secondary),
), ),
Text( Text(
label.substring(0, min(2, label.length)), label.substring(0, min(2, label.length)),
style: const TextStyle(fontSize: 26.0, fontWeight: FontWeight.w600), style: const TextStyle(
fontSize: 26.0,
fontWeight: FontWeight.w600),
), ),
], ],
), ),
@ -469,4 +545,5 @@ class _TimetablePageState extends State<TimetablePage> with TickerProviderStateM
} }
// 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);

View File

@ -96,15 +96,17 @@ class _SettingsScreenState extends State<SettingsScreen>
} }
accountTiles.add(AccountTile( accountTiles.add(AccountTile(
name: Text(!settings.presentationMode ? account.name : "Béla", name: Text(!settings.presentationMode ? account.name : "János",
style: const TextStyle(fontWeight: FontWeight.w500)), style: const TextStyle(fontWeight: FontWeight.w500)),
username: username:
Text(!settings.presentationMode ? account.username : "72469696969"), Text(!settings.presentationMode ? account.username : "01234567890"),
profileImage: ProfileImage( profileImage: ProfileImage(
name: _firstName, name: _firstName,
backgroundColor: !settings.presentationMode backgroundColor: Theme.of(context)
? ColorUtils.stringToColor(account.name) .colorScheme
: Theme.of(context).colorScheme.secondary, .primary, //!settings.presentationMode
//? ColorUtils.stringToColor(account.name)
//: Theme.of(context).colorScheme.secondary,
role: account.role, role: account.role,
), ),
onTap: () { onTap: () {
@ -169,7 +171,7 @@ class _SettingsScreenState extends State<SettingsScreen>
if (!settings.presentationMode) { if (!settings.presentationMode) {
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0]; firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
} else { } else {
firstName = "Béla"; firstName = "János";
} }
String startPageTitle = String startPageTitle =
@ -349,18 +351,18 @@ class _SettingsScreenState extends State<SettingsScreen>
// padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0), // padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
// child: PremiumBannerButton(), // child: PremiumBannerButton(),
// ), // ),
if (!context.watch<PremiumProvider>().hasPremium) // if (!context.watch<PremiumProvider>().hasPremium)
const ClipRect( // const ClipRect(
child: Padding( // child: Padding(
padding: EdgeInsets.symmetric(vertical: 12.0), // padding: EdgeInsets.symmetric(vertical: 12.0),
child: PremiumButton(), // child: PremiumButton(),
), // ),
) // )
else // else
const Padding( // const Padding(
padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0), // padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: ActiveSponsorCard(), // child: ActiveSponsorCard(),
), // ),
// General Settings // General Settings
Padding( Padding(