common filter

This commit is contained in:
55nknown 2022-09-11 14:46:58 +02:00
parent fd7793a20d
commit 8a39086ca6
20 changed files with 848 additions and 2 deletions

View File

@ -0,0 +1,8 @@
import 'package:flutter/widgets.dart';
class DateWidget {
final DateTime date;
final Widget widget;
final String? key;
const DateWidget({required this.date, required this.widget, this.key});
}

View File

@ -0,0 +1,159 @@
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo/ui/filter/widgets.dart';
import 'package:filcnaplo/ui/widgets/message/message_tile.dart';
import 'package:filcnaplo_kreta_api/models/message.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_viewable.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_tile.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/timetable/changed_lesson_tile.dart';
import 'package:flutter/material.dart';
import 'package:implicitly_animated_reorderable_list/implicitly_animated_reorderable_list.dart';
import 'package:filcnaplo/utils/format.dart';
// difference.inDays is not reliable
bool _sameDate(DateTime a, DateTime b) => (a.year == b.year && a.month == b.month && a.day == b.day);
List<Widget> sortDateWidgets(
BuildContext context, {
required List<DateWidget> dateWidgets,
bool showTitle = true,
bool showDivider = false,
bool hasShadow = false,
EdgeInsetsGeometry? padding,
}) {
dateWidgets.sort((a, b) => -a.date.compareTo(b.date));
List<Conversation> conversations = [];
List<DateWidget> convMessages = [];
// Group messages into conversations
for (var w in dateWidgets) {
if (w.widget.runtimeType == MessageTile) {
Message message = (w.widget as MessageTile).message;
if (message.conversationId != null) {
convMessages.add(w);
Conversation conv = conversations.firstWhere((e) => e.id == message.conversationId, orElse: () => Conversation(id: message.conversationId!));
conv.add(message);
if (conv.messages.length == 1) conversations.add(conv);
}
if (conversations.any((c) => c.id == message.messageId)) {
Conversation conv = conversations.firstWhere((e) => e.id == message.messageId);
convMessages.add(w);
conv.add(message);
}
}
}
// remove individual messages
for (var e in convMessages) {
dateWidgets.remove(e);
}
// Add conversations
for (var conv in conversations) {
conv.sort();
dateWidgets.add(DateWidget(
key: "${conv.newest.date.millisecondsSinceEpoch}-msg",
date: conv.newest.date,
widget: MessageTile(
conv.newest,
),
));
}
dateWidgets.sort((a, b) => -a.date.compareTo(b.date));
List<List<DateWidget>> groupedDateWidgets = [[]];
for (var element in dateWidgets) {
if (groupedDateWidgets.last.isNotEmpty) {
if (!_sameDate(element.date, groupedDateWidgets.last.last.date)) {
groupedDateWidgets.add([element]);
continue;
}
}
groupedDateWidgets.last.add(element);
}
List<DateWidget> items = [];
if (groupedDateWidgets.first.isNotEmpty) {
for (var elements in groupedDateWidgets) {
bool cst = showTitle;
// Group Absence Tiles
List<DateWidget> absenceTileWidgets = elements.where((element) {
return element.widget is AbsenceViewable && (element.widget as AbsenceViewable).absence.delay == 0;
}).toList();
List<AbsenceViewable> absenceTiles = absenceTileWidgets.map((e) => e.widget as AbsenceViewable).toList();
if (absenceTiles.length > 1) {
elements.removeWhere((element) => element.widget.runtimeType == AbsenceViewable && (element.widget as AbsenceViewable).absence.delay == 0);
if (elements.isEmpty) {
cst = false;
}
elements.add(DateWidget(
widget: AbsenceGroupTile(absenceTiles, showDate: !cst),
date: absenceTileWidgets.first.date,
key: "${absenceTileWidgets.first.date.millisecondsSinceEpoch}-absence-group"));
}
// Bring Lesson Tiles to front & sort by index asc
List<DateWidget> lessonTiles = elements.where((element) {
return element.widget.runtimeType == ChangedLessonTile;
}).toList();
lessonTiles.sort((a, b) => (a.widget as ChangedLessonTile).lesson.lessonIndex.compareTo((b.widget as ChangedLessonTile).lesson.lessonIndex));
elements.removeWhere((element) => element.widget.runtimeType == ChangedLessonTile);
elements.insertAll(0, lessonTiles);
final date = (elements + absenceTileWidgets).first.date;
items.add(DateWidget(
date: date,
widget: Panel(
key: ValueKey(date),
padding: padding ?? const EdgeInsets.symmetric(vertical: 6.0),
title: cst ? Text(date.format(context, forceToday: true)) : null,
hasShadow: hasShadow,
child: ImplicitlyAnimatedList<DateWidget>(
areItemsTheSame: (a, b) => a.key == b.key,
spawnIsolate: false,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, animation, item, index) => filterItemBuilder(context, animation, item.widget, index),
items: elements,
),
),
));
}
}
final nh = DateTime.now();
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))) {
items.add(
DateWidget(
date: now,
widget: Center(
child: Container(
margin: const EdgeInsets.symmetric(vertical: 12.0),
height: 3.0,
width: 150.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: AppColors.of(context).text.withOpacity(.25),
),
),
),
),
);
}
// 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));
return items.map((e) => e.widget).toList();
}

View File

@ -0,0 +1,160 @@
import 'package:filcnaplo/api/providers/update_provider.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo/ui/filter/widgets/grades.dart' as grade_filter;
import 'package:filcnaplo/ui/filter/widgets/certifications.dart' as certification_filter;
import 'package:filcnaplo/ui/filter/widgets/messages.dart' as message_filter;
import 'package:filcnaplo/ui/filter/widgets/absences.dart' as absence_filter;
import 'package:filcnaplo/ui/filter/widgets/homework.dart' as homework_filter;
import 'package:filcnaplo/ui/filter/widgets/exams.dart' as exam_filter;
import 'package:filcnaplo/ui/filter/widgets/notes.dart' as note_filter;
import 'package:filcnaplo/ui/filter/widgets/events.dart' as event_filter;
import 'package:filcnaplo/ui/filter/widgets/lessons.dart' as lesson_filter;
import 'package:filcnaplo/ui/filter/widgets/update.dart' as update_filter;
import 'package:filcnaplo/ui/filter/widgets/missed_exams.dart' as missed_exam_filter;
import 'package:filcnaplo_kreta_api/providers/absence_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/grade_provider.dart';
import 'package:filcnaplo_kreta_api/providers/homework_provider.dart';
import 'package:filcnaplo_kreta_api/providers/message_provider.dart';
import 'package:filcnaplo_kreta_api/providers/note_provider.dart';
import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
import 'package:flutter/material.dart';
import 'package:implicitly_animated_reorderable_list/transitions.dart';
import 'package:provider/provider.dart';
const List<FilterType> homeFilters = [FilterType.all, FilterType.grades, FilterType.messages, FilterType.absences];
enum FilterType { all, grades, messages, absences, homework, exams, notes, events, lessons, updates, certifications, missedExams }
Future<List<DateWidget>> getFilterWidgets(FilterType activeData, {bool absencesNoExcused = false, required BuildContext context}) async {
final gradeProvider = Provider.of<GradeProvider>(context);
final timetableProvider = Provider.of<TimetableProvider>(context);
final messageProvider = Provider.of<MessageProvider>(context);
final absenceProvider = Provider.of<AbsenceProvider>(context);
final homeworkProvider = Provider.of<HomeworkProvider>(context);
final examProvider = Provider.of<ExamProvider>(context);
final noteProvider = Provider.of<NoteProvider>(context);
final eventProvider = Provider.of<EventProvider>(context);
final updateProvider = Provider.of<UpdateProvider>(context);
List<DateWidget> items = [];
switch (activeData) {
// All
case FilterType.all:
final all = await Future.wait<List<DateWidget>>([
getFilterWidgets(FilterType.grades, context: context),
getFilterWidgets(FilterType.lessons, context: context),
getFilterWidgets(FilterType.messages, context: context),
getFilterWidgets(FilterType.absences, context: context, absencesNoExcused: true),
getFilterWidgets(FilterType.homework, context: context),
getFilterWidgets(FilterType.exams, context: context),
getFilterWidgets(FilterType.updates, context: context),
getFilterWidgets(FilterType.certifications, context: context),
getFilterWidgets(FilterType.missedExams, context: context),
]);
items = all.expand((x) => x).toList();
break;
// Grades
case FilterType.grades:
items = grade_filter.getWidgets(gradeProvider.grades);
break;
// Certifications
case FilterType.certifications:
items = certification_filter.getWidgets(gradeProvider.grades);
break;
// Messages
case FilterType.messages:
items = message_filter.getWidgets(
messageProvider.messages,
noteProvider.notes,
eventProvider.events,
);
break;
// Absences
case FilterType.absences:
items = absence_filter.getWidgets(absenceProvider.absences, noExcused: absencesNoExcused);
break;
// Homework
case FilterType.homework:
items = homework_filter.getWidgets(homeworkProvider.homework);
break;
// Exams
case FilterType.exams:
items = exam_filter.getWidgets(examProvider.exams);
break;
// Notes
case FilterType.notes:
items = note_filter.getWidgets(noteProvider.notes);
break;
// Events
case FilterType.events:
items = event_filter.getWidgets(eventProvider.events);
break;
// Changed Lessons
case FilterType.lessons:
items = lesson_filter.getWidgets(timetableProvider.lessons);
break;
// Updates
case FilterType.updates:
if (updateProvider.releases.isNotEmpty) items = [update_filter.getWidget(updateProvider.releases.first)];
break;
// Missed Exams
case FilterType.missedExams:
items = missed_exam_filter.getWidgets(timetableProvider.lessons);
break;
}
return items;
}
Widget filterItemBuilder(BuildContext context, Animation<double> animation, Widget item, int index) {
final wrappedItem = SizeFadeTransition(
curve: Curves.easeInOutCubic,
animation: animation,
child: item,
);
return item is Panel
// Re-add & animate shadow
? AnimatedBuilder(
animation: animation,
child: wrappedItem,
builder: (context, child) {
return Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: DecoratedBox(
decoration: BoxDecoration(
boxShadow: [
if (Theme.of(context).brightness == Brightness.light)
BoxShadow(
offset: const Offset(0, 21),
blurRadius: 23.0,
color: AppColors.of(context).shadow.withOpacity(
CurvedAnimation(
parent: CurvedAnimation(parent: animation, curve: Curves.easeInOutCubic),
curve: const Interval(2 / 3, 1.0),
).value,
),
),
],
),
child: child,
),
);
})
: wrappedItem;
}

View File

@ -0,0 +1,15 @@
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo_kreta_api/models/absence.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_viewable.dart' as mobile;
List<DateWidget> getWidgets(List<Absence> providerAbsences, {bool noExcused = false}) {
List<DateWidget> items = [];
providerAbsences.where((a) => !noExcused || a.state != Justification.excused).forEach((absence) {
items.add(DateWidget(
key: absence.id,
date: absence.date,
widget: mobile.AbsenceViewable(absence),
));
});
return items;
}

View File

@ -0,0 +1,24 @@
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/cretification/certification_card.dart' as mobile;
List<DateWidget> getWidgets(List<Grade> providerGrades) {
List<DateWidget> items = [];
for (var gradeType in GradeType.values) {
if ([GradeType.midYear, GradeType.unknown, GradeType.levelExam].contains(gradeType)) continue;
List<Grade> grades = providerGrades.where((grade) => grade.type == gradeType).toList();
if (grades.isNotEmpty) {
grades.sort((a, b) => -a.date.compareTo(b.date));
items.add(DateWidget(
date: grades.first.date,
widget: mobile.CertificationCard(
grades,
gradeType: gradeType,
),
));
}
}
return items;
}

View File

@ -0,0 +1,15 @@
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo_kreta_api/models/event.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/event/event_viewable.dart' as mobile;
List<DateWidget> getWidgets(List<Event> providerEvents) {
List<DateWidget> items = [];
for (var event in providerEvents) {
items.add(DateWidget(
key: event.id,
date: event.start,
widget: mobile.EventViewable(event),
));
}
return items;
}

View File

@ -0,0 +1,15 @@
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo_kreta_api/models/exam.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/exam/exam_viewable.dart' as mobile;
List<DateWidget> getWidgets(List<Exam> providerExams) {
List<DateWidget> items = [];
for (var exam in providerExams) {
items.add(DateWidget(
key: exam.id,
date: exam.writeDate.year != 0 ? exam.writeDate : exam.date,
widget: mobile.ExamViewable(exam),
));
}
return items;
}

View File

@ -0,0 +1,19 @@
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo/utils/platform.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/grade/grade_viewable.dart' as mobile;
import 'package:filcnaplo_desktop_ui/common/widgets/grade/grade_viewable.dart' as desktop;
List<DateWidget> getWidgets(List<Grade> providerGrades) {
List<DateWidget> items = [];
for (var grade in providerGrades) {
if (grade.type == GradeType.midYear) {
items.add(DateWidget(
key: grade.id,
date: grade.date,
widget: PlatformUtils.isMobile ? mobile.GradeViewable(grade) : desktop.GradeViewable(grade),
));
}
}
return items;
}

View File

@ -0,0 +1,18 @@
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo_kreta_api/models/homework.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_viewable.dart' as mobile;
List<DateWidget> getWidgets(List<Homework> providerHomework) {
List<DateWidget> items = [];
final now = DateTime.now();
providerHomework.where((h) => h.deadline.hour == 0 ? _sameDate(h.deadline, now) : h.deadline.isAfter(now)).forEach((homework) {
items.add(DateWidget(
key: homework.id,
date: homework.deadline.year != 0 ? homework.deadline : homework.date,
widget: mobile.HomeworkViewable(homework),
));
});
return items;
}
bool _sameDate(DateTime a, DateTime b) => (a.year == b.year && a.month == b.month && a.day == b.day);

View File

@ -0,0 +1,15 @@
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo_kreta_api/models/lesson.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/timetable/changed_lesson_viewable.dart' as mobile;
List<DateWidget> getWidgets(List<Lesson> providerLessons) {
List<DateWidget> items = [];
providerLessons.where((l) => l.isChanged && l.start.isAfter(DateTime.now())).forEach((lesson) {
items.add(DateWidget(
key: lesson.id,
date: lesson.date,
widget: mobile.ChangedLessonViewable(lesson),
));
});
return items;
}

View File

@ -0,0 +1,23 @@
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo/ui/filter/widgets/notes.dart' as note_filter;
import 'package:filcnaplo/ui/filter/widgets/events.dart' as event_filter;
import 'package:filcnaplo_kreta_api/models/event.dart';
import 'package:filcnaplo_kreta_api/models/message.dart';
import 'package:filcnaplo_kreta_api/models/note.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/message/message_viewable.dart' as mobile;
List<DateWidget> getWidgets(List<Message> providerMessages, List<Note> providerNotes, List<Event> providerEvents) {
List<DateWidget> items = [];
for (var message in providerMessages) {
if (message.type == MessageType.inbox) {
items.add(DateWidget(
key: "${message.id}",
date: message.date,
widget: mobile.MessageViewable(message),
));
}
}
items.addAll(note_filter.getWidgets(providerNotes));
items.addAll(event_filter.getWidgets(providerEvents));
return items;
}

View File

@ -0,0 +1,35 @@
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo_kreta_api/models/lesson.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/missed_exam/missed_exam_viewable.dart';
List<DateWidget> getWidgets(List<Lesson> providerLessons) {
List<DateWidget> items = [];
List<Lesson> missedExams = [];
for (var lesson in providerLessons) {
final desc = lesson.description.toLowerCase().specialChars();
// Check if lesson description includes hints for an exam written during the lesson
if (!lesson.studentPresence &&
(lesson.exam != "" ||
desc.contains("dolgozat") ||
desc.contains("feleles") ||
desc.contains("temazaro") ||
desc.contains("szamonkeres") ||
desc == "tz") &&
!(desc.contains("felkeszules") || desc.contains("gyakorlas"))) {
missedExams.add(lesson);
}
}
if (missedExams.isNotEmpty) {
missedExams.sort((a, b) => -a.date.compareTo(b.date));
items.add(DateWidget(
date: missedExams.first.date,
widget: MissedExamViewable(missedExams),
));
}
return items;
}

View File

@ -0,0 +1,15 @@
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo_kreta_api/models/note.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/note/note_viewable.dart' as mobile;
List<DateWidget> getWidgets(List<Note> providerNotes) {
List<DateWidget> items = [];
for (var note in providerNotes) {
items.add(DateWidget(
key: note.id,
date: note.date,
widget: mobile.NoteViewable(note),
));
}
return items;
}

View File

@ -0,0 +1,10 @@
import 'package:filcnaplo/models/release.dart';
import 'package:filcnaplo/ui/date_widget.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/update/update_viewable.dart' as mobile;
DateWidget getWidget(Release providerRelease) {
return DateWidget(
date: DateTime.now(),
widget: mobile.UpdateViewable(providerRelease),
);
}

View File

@ -0,0 +1,229 @@
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo/helpers/subject_icon.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_mobile_ui/pages/grades/calculator/grade_calculator_provider.dart';
import 'package:filcnaplo_mobile_ui/pages/grades/subject_grades_container.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
class GradeTile extends StatelessWidget {
const GradeTile(this.grade, {Key? key, this.onTap, this.padding}) : super(key: key);
final Grade grade;
final void Function()? onTap;
final EdgeInsetsGeometry? padding;
@override
Widget build(BuildContext context) {
String title;
String subtitle;
EdgeInsets leadingPadding = EdgeInsets.zero;
bool isSubjectView = SubjectGradesContainer.of(context) != null;
String subjectName = grade.subject.name.capital();
String modeDescription = grade.mode.description.capital();
String description = grade.description.capital();
GradeCalculatorProvider calculatorProvider = Provider.of<GradeCalculatorProvider>(context, listen: false);
// Test order:
// description
// mode
// value name
if (grade.type == GradeType.midYear || grade.type == GradeType.ghost) {
if (grade.description != "") {
title = description;
} else {
title = modeDescription != "" ? modeDescription : grade.value.valueName.split("(")[0];
}
} else {
title = subjectName;
}
// Test order:
// subject name
// mode + weight != 100
if (grade.type == GradeType.midYear) {
subtitle = isSubjectView
? description != ""
? modeDescription
: ""
: subjectName;
} else {
subtitle = grade.value.valueName.split("(")[0];
}
if (subtitle != "") leadingPadding = const EdgeInsets.only(top: 2.0);
return Material(
type: MaterialType.transparency,
child: Padding(
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
child: ListTile(
visualDensity: VisualDensity.compact,
contentPadding: isSubjectView
? grade.type != GradeType.ghost
? const EdgeInsets.symmetric(horizontal: 12.0)
: const EdgeInsets.only(left: 12.0, right: 4.0)
: const EdgeInsets.only(left: 8.0, right: 12.0),
onTap: onTap,
// onLongPress: kDebugMode ? () => log(jsonEncode(grade.json)) : null,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
leading: isSubjectView
? GradeValueWidget(grade.value)
: SizedBox(
width: 44,
height: 44,
child: Center(
child: Padding(
padding: leadingPadding,
child: Icon(SubjectIcon.lookup(subject: grade.subject), size: 28.0, color: AppColors.of(context).text.withOpacity(.75)),
),
),
),
title: Text(
title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600),
),
subtitle: subtitle != ""
? Text(
subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w500),
)
: null,
trailing: isSubjectView
? grade.type != GradeType.ghost
? Text(grade.date.format(context), style: const TextStyle(fontWeight: FontWeight.w500))
: IconButton(
splashRadius: 24.0,
icon: Icon(FeatherIcons.trash2, color: AppColors.of(context).red),
onPressed: () {
calculatorProvider.removeGrade(grade);
},
)
: GradeValueWidget(grade.value),
minLeadingWidth: isSubjectView ? 32.0 : 0,
),
),
);
}
}
class GradeValueWidget extends StatelessWidget {
const GradeValueWidget(this.value, {Key? key, this.size = 38.0, this.fill = false, this.complemented = false}) : super(key: key);
final GradeValue value;
final double size;
final bool fill;
final bool complemented;
@override
Widget build(BuildContext context) {
bool isSubjectView = SubjectGradesContainer.of(context) != null;
Color color = gradeColor(context: context, value: value.value);
Widget valueText;
final percentage = value.percentage;
if (percentage) {
double multiplier = 1.0;
if (isSubjectView) multiplier = 0.75;
valueText = Text.rich(
TextSpan(
text: value.value.toString(),
children: [
TextSpan(
text: "\n%",
style: TextStyle(fontWeight: FontWeight.w700, fontSize: size / 2.5 * multiplier, height: 0.7),
),
],
style: TextStyle(fontWeight: FontWeight.w700, fontSize: size / 1 * multiplier, height: 1),
),
textAlign: TextAlign.center,
);
} else if (value.value != 0) {
valueText = Stack(alignment: Alignment.topRight, children: [
Transform.translate(
offset: (value.weight >= 200) ? const Offset(2, 1.5) : Offset.zero,
child: Text(
value.value.toString(),
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: value.weight == 50 ? FontWeight.w600 : FontWeight.bold,
fontSize: size,
color: color,
shadows: [
if (value.weight >= 200)
Shadow(
color: color.withOpacity(.4),
offset: const Offset(-4, -3),
)
],
),
),
),
if (complemented)
Transform.translate(
offset: const Offset(9, 1),
child: Text(
"*",
style: TextStyle(fontSize: size / 1.6, fontWeight: FontWeight.bold),
),
),
]);
} else if (value.valueName.toLowerCase().specialChars() == 'nem irt') {
valueText = const Icon(FeatherIcons.slash);
} else {
valueText = const Icon(FeatherIcons.type);
}
return fill
? Container(
width: size * 1.4,
height: size * 1.4,
decoration: BoxDecoration(
color: color.withOpacity(.25),
shape: BoxShape.circle,
),
child: Center(child: valueText),
)
: valueText;
}
}
Color gradeColor({required BuildContext context, required num value}) {
int valueInt = 0;
var settings = Provider.of<SettingsProvider>(context, listen: false);
try {
if (value > value.floor() + settings.rounding / 10) {
valueInt = value.ceil();
} else {
valueInt = value.floor();
}
} catch (_) {}
switch (valueInt) {
case 5:
return settings.gradeColors[4];
case 4:
return settings.gradeColors[3];
case 3:
return settings.gradeColors[2];
case 2:
return settings.gradeColors[1];
case 1:
return settings.gradeColors[0];
default:
return AppColors.of(context).text;
}
}

View File

@ -0,0 +1,79 @@
import 'package:animations/animations.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/utils/color.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_kreta_api/models/message.dart';
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
class MessageTile extends StatelessWidget {
const MessageTile(
this.message, {
Key? key,
this.messages,
this.padding,
this.onTap,
}) : super(key: key);
final Message message;
final List<Message>? messages;
final EdgeInsetsGeometry? padding;
final Function()? onTap;
@override
Widget build(BuildContext context) {
return Material(
type: MaterialType.transparency,
child: Padding(
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
child: ListTile(
onTap: onTap,
visualDensity: VisualDensity.compact,
contentPadding: const EdgeInsets.only(left: 8.0, right: 4.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
leading: !Provider.of<SettingsProvider>(context, listen: false).presentationMode
? ProfileImage(
name: message.author,
radius: 22.0,
backgroundColor: ColorUtils.stringToColor(message.author),
)
: ProfileImage(
name: "Béla",
radius: 22.0,
backgroundColor: Theme.of(context).colorScheme.secondary,
),
title: Row(
children: [
Expanded(
child: Text(
!Provider.of<SettingsProvider>(context, listen: false).presentationMode ? message.author : "Béla",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 15.5),
),
),
if (message.attachments.isNotEmpty) const Icon(FeatherIcons.paperclip, size: 16.0)
],
),
subtitle: Text(
message.subject,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 14.0),
),
trailing: Text(
message.date.format(context),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14.0,
color: AppColors.of(context).text.withOpacity(.75),
),
),
),
),
);
}
}

View File

@ -0,0 +1,6 @@
import 'dart:io';
class PlatformUtils {
static bool get isDesktop => Platform.isWindows || Platform.isMacOS || Platform.isLinux;
static bool get isMobile => !isDesktop;
}

View File

@ -45,6 +45,7 @@ dependencies:
material_color_utilities: ^0.1.3 material_color_utilities: ^0.1.3
crypto: ^3.0.2 crypto: ^3.0.2
elegant_notification: ^1.6.1 elegant_notification: ^1.6.1
flutter_feather_icons: ^2.0.0+1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

@ -1 +1 @@
Subproject commit 351c241efb3aad1ba4f7384ee63475ab74399f5d Subproject commit 98ae9b4139ca795ac9b39bcbb1d9ef2713d98a63

@ -1 +1 @@
Subproject commit 2ed17a62b3d463c95452a9fa5bd406d87856668f Subproject commit 5600330a166e274c06cbbc5678ca2a40db919eb1