igen
This commit is contained in:
@@ -1,50 +1,50 @@
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
class AbsenceDisplay extends StatelessWidget {
|
||||
const AbsenceDisplay(this.excused, this.unexcused, this.pending, {Key? key}) : super(key: key);
|
||||
|
||||
final int excused;
|
||||
final int unexcused;
|
||||
final int pending;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(top: 5.0),
|
||||
// padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 6.0),
|
||||
// decoration: BoxDecoration(
|
||||
// color: Theme.of(context).scaffoldBackgroundColor.withOpacity(.2),
|
||||
// borderRadius: BorderRadius.circular(12.0),
|
||||
// ),
|
||||
child: Row(children: [
|
||||
if (excused > 0)
|
||||
Icon(
|
||||
FeatherIcons.check,
|
||||
size: 16.0,
|
||||
color: AppColors.of(context).green,
|
||||
),
|
||||
if (excused > 0) const SizedBox(width: 2.0),
|
||||
if (excused > 0) Text(excused.toString(), style: const TextStyle(fontFamily: "monospace", fontSize: 14.0)),
|
||||
if (excused > 0 && pending > 0) const SizedBox(width: 6.0),
|
||||
if (pending > 0)
|
||||
Icon(
|
||||
FeatherIcons.slash,
|
||||
size: 14.0,
|
||||
color: AppColors.of(context).orange,
|
||||
),
|
||||
if (pending > 0) const SizedBox(width: 3.0),
|
||||
if (pending > 0) Text(pending.toString(), style: const TextStyle(fontFamily: "monospace", fontSize: 14.0)),
|
||||
if (unexcused > 0 && pending > 0) const SizedBox(width: 3.0),
|
||||
if (unexcused > 0)
|
||||
Icon(
|
||||
FeatherIcons.x,
|
||||
size: 18.0,
|
||||
color: AppColors.of(context).red,
|
||||
),
|
||||
if (unexcused > 0) Text(unexcused.toString(), style: const TextStyle(fontFamily: "monospace", fontSize: 14.0)),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
class AbsenceDisplay extends StatelessWidget {
|
||||
const AbsenceDisplay(this.excused, this.unexcused, this.pending, {Key? key}) : super(key: key);
|
||||
|
||||
final int excused;
|
||||
final int unexcused;
|
||||
final int pending;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(top: 5.0),
|
||||
// padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 6.0),
|
||||
// decoration: BoxDecoration(
|
||||
// color: Theme.of(context).scaffoldBackgroundColor.withOpacity(.2),
|
||||
// borderRadius: BorderRadius.circular(12.0),
|
||||
// ),
|
||||
child: Row(children: [
|
||||
if (excused > 0)
|
||||
Icon(
|
||||
FeatherIcons.check,
|
||||
size: 16.0,
|
||||
color: AppColors.of(context).green,
|
||||
),
|
||||
if (excused > 0) const SizedBox(width: 2.0),
|
||||
if (excused > 0) Text(excused.toString(), style: const TextStyle(fontFamily: "monospace", fontSize: 14.0)),
|
||||
if (excused > 0 && pending > 0) const SizedBox(width: 6.0),
|
||||
if (pending > 0)
|
||||
Icon(
|
||||
FeatherIcons.slash,
|
||||
size: 14.0,
|
||||
color: AppColors.of(context).orange,
|
||||
),
|
||||
if (pending > 0) const SizedBox(width: 3.0),
|
||||
if (pending > 0) Text(pending.toString(), style: const TextStyle(fontFamily: "monospace", fontSize: 14.0)),
|
||||
if (unexcused > 0 && pending > 0) const SizedBox(width: 3.0),
|
||||
if (unexcused > 0)
|
||||
Icon(
|
||||
FeatherIcons.x,
|
||||
size: 18.0,
|
||||
color: AppColors.of(context).red,
|
||||
),
|
||||
if (unexcused > 0) Text(unexcused.toString(), style: const TextStyle(fontFamily: "monospace", fontSize: 14.0)),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +1,80 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/subject.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_display.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AbsenceSubjectTile extends StatelessWidget {
|
||||
const AbsenceSubjectTile(this.subject, {Key? key, this.percentage = 0.0, this.excused = 0, this.unexcused = 0, this.pending = 0, this.onTap})
|
||||
: super(key: key);
|
||||
|
||||
final Subject subject;
|
||||
final void Function()? onTap;
|
||||
final double percentage;
|
||||
|
||||
final int excused;
|
||||
final int unexcused;
|
||||
final int pending;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: ListTile(
|
||||
// minLeadingWidth: 32.0,
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 6.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onTap: onTap,
|
||||
leading: Icon(SubjectIcon.resolveVariant(subject: subject, context: context), size: 32.0),
|
||||
title: Text(
|
||||
subject.renamedTo ?? subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15.0, fontStyle: subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: AbsenceDisplay(excused, unexcused, pending),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(width: 8.0),
|
||||
if (percentage >= 0)
|
||||
Stack(
|
||||
alignment: Alignment.centerRight,
|
||||
children: [
|
||||
const Opacity(child: Text("100%", style: TextStyle(fontFamily: "monospace")), opacity: 0),
|
||||
Text(
|
||||
percentage.round().toString() + "%",
|
||||
style: TextStyle(
|
||||
// fontFamily: "monospace",
|
||||
color: getColorByPercentage(percentage, context: context),
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 24.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Color getColorByPercentage(double percentage, {required BuildContext context}) {
|
||||
Color color = AppColors.of(context).text;
|
||||
|
||||
percentage = percentage.round().toDouble();
|
||||
|
||||
if (percentage > 35) {
|
||||
color = AppColors.of(context).red;
|
||||
} else if (percentage > 25) {
|
||||
color = AppColors.of(context).orange;
|
||||
} else if (percentage > 15) {
|
||||
color = AppColors.of(context).yellow;
|
||||
}
|
||||
|
||||
return color.withOpacity(.8);
|
||||
}
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/subject.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_display.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AbsenceSubjectTile extends StatelessWidget {
|
||||
const AbsenceSubjectTile(this.subject, {Key? key, this.percentage = 0.0, this.excused = 0, this.unexcused = 0, this.pending = 0, this.onTap})
|
||||
: super(key: key);
|
||||
|
||||
final Subject subject;
|
||||
final void Function()? onTap;
|
||||
final double percentage;
|
||||
|
||||
final int excused;
|
||||
final int unexcused;
|
||||
final int pending;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: ListTile(
|
||||
// minLeadingWidth: 32.0,
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 6.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onTap: onTap,
|
||||
leading: Icon(SubjectIcon.resolveVariant(subject: subject, context: context), size: 32.0),
|
||||
title: Text(
|
||||
subject.renamedTo ?? subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15.0, fontStyle: subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: AbsenceDisplay(excused, unexcused, pending),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(width: 8.0),
|
||||
if (percentage >= 0)
|
||||
Stack(
|
||||
alignment: Alignment.centerRight,
|
||||
children: [
|
||||
const Opacity(child: Text("100%", style: TextStyle(fontFamily: "monospace")), opacity: 0),
|
||||
Text(
|
||||
percentage.round().toString() + "%",
|
||||
style: TextStyle(
|
||||
// fontFamily: "monospace",
|
||||
color: getColorByPercentage(percentage, context: context),
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 24.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Color getColorByPercentage(double percentage, {required BuildContext context}) {
|
||||
Color color = AppColors.of(context).text;
|
||||
|
||||
percentage = percentage.round().toDouble();
|
||||
|
||||
if (percentage > 35) {
|
||||
color = AppColors.of(context).red;
|
||||
} else if (percentage > 25) {
|
||||
color = AppColors.of(context).orange;
|
||||
} else if (percentage > 15) {
|
||||
color = AppColors.of(context).yellow;
|
||||
}
|
||||
|
||||
return color.withOpacity(.8);
|
||||
}
|
||||
|
||||
@@ -1,118 +1,118 @@
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_container.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'absence_tile.i18n.dart';
|
||||
|
||||
class AbsenceTile extends StatelessWidget {
|
||||
const AbsenceTile(this.absence, {Key? key, this.onTap, this.elevation = 0.0, this.padding}) : super(key: key);
|
||||
|
||||
final Absence absence;
|
||||
final void Function()? onTap;
|
||||
final double elevation;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color color = justificationColor(absence.state, context: context);
|
||||
bool group = AbsenceGroupContainer.of(context) != null;
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [
|
||||
if (elevation > 0)
|
||||
BoxShadow(
|
||||
offset: Offset(0, 21 * elevation),
|
||||
blurRadius: 23.0 * elevation,
|
||||
color: Theme.of(context).shadowColor,
|
||||
)
|
||||
],
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: padding ?? (group ? EdgeInsets.zero : const EdgeInsets.symmetric(horizontal: 8.0)),
|
||||
child: ListTile(
|
||||
onTap: onTap,
|
||||
visualDensity: VisualDensity.compact,
|
||||
dense: group,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(!group ? 14.0 : 12.0)),
|
||||
leading: Container(
|
||||
width: 44.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: !group ? color.withOpacity(.25) : null,
|
||||
),
|
||||
child: Center(child: Icon(justificationIcon(absence.state), color: color)),
|
||||
),
|
||||
title: !group
|
||||
? Text.rich(TextSpan(
|
||||
text: "${absence.delay == 0 ? "" : absence.delay}",
|
||||
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 15.5),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: absence.delay == 0
|
||||
? justificationName(absence.state).fill(["absence".i18n]).capital()
|
||||
: 'minute'.plural(absence.delay) + justificationName(absence.state).fill(["delay".i18n]),
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
))
|
||||
: Text(
|
||||
(absence.lessonIndex != null ? "${absence.lessonIndex}. " : "") + (absence.subject.renamedTo ?? absence.subject.name.capital()),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 14.0, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: !group
|
||||
? Text(
|
||||
absence.subject.renamedTo ?? absence.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
// DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date),
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static String justificationName(Justification state) {
|
||||
switch (state) {
|
||||
case Justification.excused:
|
||||
return "excused".i18n;
|
||||
case Justification.pending:
|
||||
return "pending".i18n;
|
||||
case Justification.unexcused:
|
||||
return "unexcused".i18n;
|
||||
}
|
||||
}
|
||||
|
||||
static Color justificationColor(Justification state, {required BuildContext context}) {
|
||||
switch (state) {
|
||||
case Justification.excused:
|
||||
return AppColors.of(context).green;
|
||||
case Justification.pending:
|
||||
return AppColors.of(context).orange;
|
||||
case Justification.unexcused:
|
||||
return AppColors.of(context).red;
|
||||
}
|
||||
}
|
||||
|
||||
static IconData justificationIcon(Justification state) {
|
||||
switch (state) {
|
||||
case Justification.excused:
|
||||
return FeatherIcons.check;
|
||||
case Justification.pending:
|
||||
return FeatherIcons.slash;
|
||||
case Justification.unexcused:
|
||||
return FeatherIcons.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_container.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'absence_tile.i18n.dart';
|
||||
|
||||
class AbsenceTile extends StatelessWidget {
|
||||
const AbsenceTile(this.absence, {Key? key, this.onTap, this.elevation = 0.0, this.padding}) : super(key: key);
|
||||
|
||||
final Absence absence;
|
||||
final void Function()? onTap;
|
||||
final double elevation;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color color = justificationColor(absence.state, context: context);
|
||||
bool group = AbsenceGroupContainer.of(context) != null;
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [
|
||||
if (elevation > 0)
|
||||
BoxShadow(
|
||||
offset: Offset(0, 21 * elevation),
|
||||
blurRadius: 23.0 * elevation,
|
||||
color: Theme.of(context).shadowColor,
|
||||
)
|
||||
],
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: padding ?? (group ? EdgeInsets.zero : const EdgeInsets.symmetric(horizontal: 8.0)),
|
||||
child: ListTile(
|
||||
onTap: onTap,
|
||||
visualDensity: VisualDensity.compact,
|
||||
dense: group,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(!group ? 14.0 : 12.0)),
|
||||
leading: Container(
|
||||
width: 44.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: !group ? color.withOpacity(.25) : null,
|
||||
),
|
||||
child: Center(child: Icon(justificationIcon(absence.state), color: color)),
|
||||
),
|
||||
title: !group
|
||||
? Text.rich(TextSpan(
|
||||
text: "${absence.delay == 0 ? "" : absence.delay}",
|
||||
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 15.5),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: absence.delay == 0
|
||||
? justificationName(absence.state).fill(["absence".i18n]).capital()
|
||||
: 'minute'.plural(absence.delay) + justificationName(absence.state).fill(["delay".i18n]),
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
))
|
||||
: Text(
|
||||
(absence.lessonIndex != null ? "${absence.lessonIndex}. " : "") + (absence.subject.renamedTo ?? absence.subject.name.capital()),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 14.0, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: !group
|
||||
? Text(
|
||||
absence.subject.renamedTo ?? absence.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
// DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date),
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static String justificationName(Justification state) {
|
||||
switch (state) {
|
||||
case Justification.excused:
|
||||
return "excused".i18n;
|
||||
case Justification.pending:
|
||||
return "pending".i18n;
|
||||
case Justification.unexcused:
|
||||
return "unexcused".i18n;
|
||||
}
|
||||
}
|
||||
|
||||
static Color justificationColor(Justification state, {required BuildContext context}) {
|
||||
switch (state) {
|
||||
case Justification.excused:
|
||||
return AppColors.of(context).green;
|
||||
case Justification.pending:
|
||||
return AppColors.of(context).orange;
|
||||
case Justification.unexcused:
|
||||
return AppColors.of(context).red;
|
||||
}
|
||||
}
|
||||
|
||||
static IconData justificationIcon(Justification state) {
|
||||
switch (state) {
|
||||
case Justification.excused:
|
||||
return FeatherIcons.check;
|
||||
case Justification.pending:
|
||||
return FeatherIcons.slash;
|
||||
case Justification.unexcused:
|
||||
return FeatherIcons.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"excused": "excused %s",
|
||||
"pending": "%s to be excused",
|
||||
"unexcused": "unexcused %s",
|
||||
"absence": "absence",
|
||||
"delay": "delay",
|
||||
"minute": " minutes of ".one(" minute of "),
|
||||
},
|
||||
"hu_hu": {
|
||||
"excused": "igazolt %s",
|
||||
"pending": "igazolandó %s",
|
||||
"unexcused": "igazolatlan %s",
|
||||
"absence": "hiányzás",
|
||||
"delay": "késés",
|
||||
"minute": " perc ",
|
||||
},
|
||||
"de_de": {
|
||||
"excused": "anerkannt %s",
|
||||
"pending": "%s zu anerkennen",
|
||||
"unexcused": "unanerkannt %s",
|
||||
"absence": "Abwesenheit",
|
||||
"delay": "Verspätung",
|
||||
"minute": " Minuten ".one(" Minute "),
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"excused": "excused %s",
|
||||
"pending": "%s to be excused",
|
||||
"unexcused": "unexcused %s",
|
||||
"absence": "absence",
|
||||
"delay": "delay",
|
||||
"minute": " minutes of ".one(" minute of "),
|
||||
},
|
||||
"hu_hu": {
|
||||
"excused": "igazolt %s",
|
||||
"pending": "igazolandó %s",
|
||||
"unexcused": "igazolatlan %s",
|
||||
"absence": "hiányzás",
|
||||
"delay": "késés",
|
||||
"minute": " perc ",
|
||||
},
|
||||
"de_de": {
|
||||
"excused": "anerkannt %s",
|
||||
"pending": "%s zu anerkennen",
|
||||
"unexcused": "unanerkannt %s",
|
||||
"absence": "Abwesenheit",
|
||||
"delay": "Verspätung",
|
||||
"minute": " Minuten ".one(" Minute "),
|
||||
}
|
||||
};
|
||||
|
||||
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,128 +1,128 @@
|
||||
// ignore_for_file: empty_catches
|
||||
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel_action_button.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_tile.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:filcnaplo/utils/reverse_search.dart';
|
||||
import 'absence_view.i18n.dart';
|
||||
|
||||
class AbsenceView extends StatelessWidget {
|
||||
const AbsenceView(this.absence, {Key? key, this.outsideContext, this.viewable = false}) : super(key: key);
|
||||
|
||||
final Absence absence;
|
||||
final BuildContext? outsideContext;
|
||||
final bool viewable;
|
||||
|
||||
static show(Absence absence, {required BuildContext context}) {
|
||||
showBottomCard(context: context, child: AbsenceView(absence, outsideContext: context));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color color = AbsenceTile.justificationColor(absence.state, context: context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 16.0, right: 12.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
leading: Container(
|
||||
width: 44.0,
|
||||
height: 44.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: color.withOpacity(.25),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
AbsenceTile.justificationIcon(absence.state),
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
absence.subject.renamedTo ?? absence.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w700, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
absence.teacher,
|
||||
// DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
absence.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Absence Details
|
||||
if (absence.delay > 0)
|
||||
Detail(
|
||||
title: "delay".i18n,
|
||||
description: absence.delay.toString() + " " + "minutes".i18n.plural(absence.delay),
|
||||
),
|
||||
if (absence.lessonIndex != null)
|
||||
Detail(
|
||||
title: "Lesson".i18n,
|
||||
description: "${absence.lessonIndex}. (${absence.lessonStart.format(context, timeOnly: true)}"
|
||||
" - "
|
||||
"${absence.lessonEnd.format(context, timeOnly: true)})",
|
||||
),
|
||||
if (absence.justification != null)
|
||||
Detail(
|
||||
title: "Excuse".i18n,
|
||||
description: absence.justification?.description ?? "",
|
||||
),
|
||||
if (absence.mode != null) Detail(title: "Mode".i18n, description: absence.mode?.description ?? ""),
|
||||
Detail(title: "Submit date".i18n, description: absence.submitDate.format(context)),
|
||||
|
||||
// Show in timetable
|
||||
if (!viewable)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 6.0, top: 12.0),
|
||||
child: PanelActionButton(
|
||||
leading: const Icon(FeatherIcons.calendar),
|
||||
title: Text(
|
||||
"show in timetable".i18n,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
if (outsideContext != null) {
|
||||
ReverseSearch.getLessonByAbsence(absence, context).then((lesson) {
|
||||
if (lesson != null) {
|
||||
TimetablePage.jump(outsideContext!, lesson: lesson);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
||||
content: Text("Cannot find lesson".i18n, style: const TextStyle(color: Colors.white)),
|
||||
backgroundColor: AppColors.of(context).red,
|
||||
context: context,
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// ignore_for_file: empty_catches
|
||||
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel_action_button.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_tile.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:filcnaplo/utils/reverse_search.dart';
|
||||
import 'absence_view.i18n.dart';
|
||||
|
||||
class AbsenceView extends StatelessWidget {
|
||||
const AbsenceView(this.absence, {Key? key, this.outsideContext, this.viewable = false}) : super(key: key);
|
||||
|
||||
final Absence absence;
|
||||
final BuildContext? outsideContext;
|
||||
final bool viewable;
|
||||
|
||||
static show(Absence absence, {required BuildContext context}) {
|
||||
showBottomCard(context: context, child: AbsenceView(absence, outsideContext: context));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color color = AbsenceTile.justificationColor(absence.state, context: context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 16.0, right: 12.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
leading: Container(
|
||||
width: 44.0,
|
||||
height: 44.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: color.withOpacity(.25),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
AbsenceTile.justificationIcon(absence.state),
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
absence.subject.renamedTo ?? absence.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w700, fontStyle: absence.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
absence.teacher,
|
||||
// DateFormat("MM. dd. (EEEEE)", I18n.of(context).locale.toString()).format(absence.date),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
absence.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Absence Details
|
||||
if (absence.delay > 0)
|
||||
Detail(
|
||||
title: "delay".i18n,
|
||||
description: absence.delay.toString() + " " + "minutes".i18n.plural(absence.delay),
|
||||
),
|
||||
if (absence.lessonIndex != null)
|
||||
Detail(
|
||||
title: "Lesson".i18n,
|
||||
description: "${absence.lessonIndex}. (${absence.lessonStart.format(context, timeOnly: true)}"
|
||||
" - "
|
||||
"${absence.lessonEnd.format(context, timeOnly: true)})",
|
||||
),
|
||||
if (absence.justification != null)
|
||||
Detail(
|
||||
title: "Excuse".i18n,
|
||||
description: absence.justification?.description ?? "",
|
||||
),
|
||||
if (absence.mode != null) Detail(title: "Mode".i18n, description: absence.mode?.description ?? ""),
|
||||
Detail(title: "Submit date".i18n, description: absence.submitDate.format(context)),
|
||||
|
||||
// Show in timetable
|
||||
if (!viewable)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 6.0, top: 12.0),
|
||||
child: PanelActionButton(
|
||||
leading: const Icon(FeatherIcons.calendar),
|
||||
title: Text(
|
||||
"show in timetable".i18n,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
if (outsideContext != null) {
|
||||
ReverseSearch.getLessonByAbsence(absence, context).then((lesson) {
|
||||
if (lesson != null) {
|
||||
TimetablePage.jump(outsideContext!, lesson: lesson);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
||||
content: Text("Cannot find lesson".i18n, style: const TextStyle(color: Colors.white)),
|
||||
backgroundColor: AppColors.of(context).red,
|
||||
context: context,
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"Lesson": "Lesson",
|
||||
"Excuse": "Excuse",
|
||||
"Mode": "Mode",
|
||||
"Submit date": "Submit Date",
|
||||
"show in timetable": "Show in timetable",
|
||||
"minutes": "minutes".one("minute"),
|
||||
"delay": "Delay",
|
||||
},
|
||||
"hu_hu": {
|
||||
"Lesson": "Óra",
|
||||
"Excuse": "Igazolás",
|
||||
"Mode": "Típus",
|
||||
"Submit date": "Rögzítés dátuma",
|
||||
"show in timetable": "Megtekintés az órarendben",
|
||||
"minutes": "perc",
|
||||
"delay": "Késés",
|
||||
},
|
||||
"de_de": {
|
||||
"Lesson": "Stunde",
|
||||
"Excuse": "Anerkannt",
|
||||
"Mode": "Typ",
|
||||
"Submit date": "Datum einreichen",
|
||||
"show in timetable": "im Stundenplan anzeigen",
|
||||
"minutes": "Minuten".one("Minute"),
|
||||
"delay": "Verspätung",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"Lesson": "Lesson",
|
||||
"Excuse": "Excuse",
|
||||
"Mode": "Mode",
|
||||
"Submit date": "Submit Date",
|
||||
"show in timetable": "Show in timetable",
|
||||
"minutes": "minutes".one("minute"),
|
||||
"delay": "Delay",
|
||||
},
|
||||
"hu_hu": {
|
||||
"Lesson": "Óra",
|
||||
"Excuse": "Igazolás",
|
||||
"Mode": "Típus",
|
||||
"Submit date": "Rögzítés dátuma",
|
||||
"show in timetable": "Megtekintés az órarendben",
|
||||
"minutes": "perc",
|
||||
"delay": "Késés",
|
||||
},
|
||||
"de_de": {
|
||||
"Lesson": "Stunde",
|
||||
"Excuse": "Anerkannt",
|
||||
"Mode": "Typ",
|
||||
"Submit date": "Datum einreichen",
|
||||
"show in timetable": "im Stundenplan anzeigen",
|
||||
"minutes": "Minuten".one("Minute"),
|
||||
"delay": "Verspätung",
|
||||
}
|
||||
};
|
||||
|
||||
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,68 +1,68 @@
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/viewable.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_view.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_container.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/card_handle.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/absences/absence_subject_view_container.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/reverse_search.dart';
|
||||
|
||||
import 'absence_view.i18n.dart';
|
||||
|
||||
class AbsenceViewable extends StatelessWidget {
|
||||
const AbsenceViewable(this.absence, {Key? key, this.padding}) : super(key: key);
|
||||
|
||||
final Absence absence;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final subject = AbsenceSubjectViewContainer.of(context) != null;
|
||||
final group = AbsenceGroupContainer.of(context) != null;
|
||||
final tile = AbsenceTile(absence, padding: padding);
|
||||
|
||||
return Viewable(
|
||||
tile: group ? AbsenceGroupContainer(child: tile) : tile,
|
||||
view: CardHandle(child: AbsenceView(absence, viewable: true)),
|
||||
actions: [
|
||||
PanelButton(
|
||||
background: true,
|
||||
title: Text(
|
||||
"show in timetable".i18n,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
|
||||
if (subject) {
|
||||
Future.delayed(const Duration(milliseconds: 250)).then((_) {
|
||||
Navigator.of(context, rootNavigator: true).pop(absence);
|
||||
});
|
||||
} else {
|
||||
Future.delayed(const Duration(milliseconds: 250)).then((_) {
|
||||
ReverseSearch.getLessonByAbsence(absence, context).then((lesson) {
|
||||
if (lesson != null) {
|
||||
TimetablePage.jump(context, lesson: lesson);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
||||
content: Text("Cannot find lesson".i18n, style: const TextStyle(color: Colors.white)),
|
||||
backgroundColor: AppColors.of(context).red,
|
||||
context: context,
|
||||
));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/viewable.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_view.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_container.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/card_handle.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/absences/absence_subject_view_container.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/reverse_search.dart';
|
||||
|
||||
import 'absence_view.i18n.dart';
|
||||
|
||||
class AbsenceViewable extends StatelessWidget {
|
||||
const AbsenceViewable(this.absence, {Key? key, this.padding}) : super(key: key);
|
||||
|
||||
final Absence absence;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final subject = AbsenceSubjectViewContainer.of(context) != null;
|
||||
final group = AbsenceGroupContainer.of(context) != null;
|
||||
final tile = AbsenceTile(absence, padding: padding);
|
||||
|
||||
return Viewable(
|
||||
tile: group ? AbsenceGroupContainer(child: tile) : tile,
|
||||
view: CardHandle(child: AbsenceView(absence, viewable: true)),
|
||||
actions: [
|
||||
PanelButton(
|
||||
background: true,
|
||||
title: Text(
|
||||
"show in timetable".i18n,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
|
||||
if (subject) {
|
||||
Future.delayed(const Duration(milliseconds: 250)).then((_) {
|
||||
Navigator.of(context, rootNavigator: true).pop(absence);
|
||||
});
|
||||
} else {
|
||||
Future.delayed(const Duration(milliseconds: 250)).then((_) {
|
||||
ReverseSearch.getLessonByAbsence(absence, context).then((lesson) {
|
||||
if (lesson != null) {
|
||||
TimetablePage.jump(context, lesson: lesson);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
||||
content: Text("Cannot find lesson".i18n, style: const TextStyle(color: Colors.white)),
|
||||
backgroundColor: AppColors.of(context).red,
|
||||
context: context,
|
||||
));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AbsenceGroupContainer extends InheritedWidget {
|
||||
const AbsenceGroupContainer({Key? key, required Widget child}) : super(key: key, child: child);
|
||||
|
||||
static AbsenceGroupContainer? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<AbsenceGroupContainer>();
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(AbsenceGroupContainer oldWidget) => false;
|
||||
}
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AbsenceGroupContainer extends InheritedWidget {
|
||||
const AbsenceGroupContainer({Key? key, required Widget child}) : super(key: key, child: child);
|
||||
|
||||
static AbsenceGroupContainer? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<AbsenceGroupContainer>();
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(AbsenceGroupContainer oldWidget) => false;
|
||||
}
|
||||
|
||||
@@ -1,80 +1,80 @@
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_viewable.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_container.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_tile.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'absence_group_tile.i18n.dart';
|
||||
|
||||
class AbsenceGroupTile extends StatelessWidget {
|
||||
const AbsenceGroupTile(this.absences, {Key? key, this.showDate = false, this.padding}) : super(key: key);
|
||||
|
||||
final List<AbsenceViewable> absences;
|
||||
final bool showDate;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Justification state = getState(absences.map((e) => e.absence.state).toList());
|
||||
Color color = AbsenceTile.justificationColor(state, context: context);
|
||||
|
||||
absences.sort((a, b) => a.absence.lessonIndex?.compareTo(b.absence.lessonIndex ?? 0) ?? -1);
|
||||
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: AbsenceGroupContainer(
|
||||
child: ExpansionTile(
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
backgroundColor: Colors.transparent,
|
||||
leading: Container(
|
||||
width: 44.0,
|
||||
height: 44.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: color.withOpacity(.25),
|
||||
),
|
||||
child: Center(child: Icon(AbsenceTile.justificationIcon(state), color: color)),
|
||||
),
|
||||
title: Text.rich(TextSpan(
|
||||
text: "${absences.where((a) => a.absence.state == state).length} ",
|
||||
style: TextStyle(fontWeight: FontWeight.w700, color: AppColors.of(context).text),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: AbsenceTile.justificationName(state).fill(["absence".i18n]),
|
||||
style: TextStyle(fontWeight: FontWeight.w600, color: AppColors.of(context).text),
|
||||
),
|
||||
],
|
||||
)),
|
||||
subtitle: showDate
|
||||
? Text(
|
||||
absences.first.absence.date.format(context, weekday: true),
|
||||
style: TextStyle(fontWeight: FontWeight.w500, color: AppColors.of(context).text.withOpacity(0.8)),
|
||||
)
|
||||
: null,
|
||||
children: absences,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Justification getState(List<Justification> states) {
|
||||
Justification state;
|
||||
|
||||
if (states.any((element) => element == Justification.unexcused)) {
|
||||
state = Justification.unexcused;
|
||||
} else if (states.any((element) => element == Justification.pending)) {
|
||||
state = Justification.pending;
|
||||
} else {
|
||||
state = Justification.excused;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/absence.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_viewable.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence_group/absence_group_container.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/absence/absence_tile.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'absence_group_tile.i18n.dart';
|
||||
|
||||
class AbsenceGroupTile extends StatelessWidget {
|
||||
const AbsenceGroupTile(this.absences, {Key? key, this.showDate = false, this.padding}) : super(key: key);
|
||||
|
||||
final List<AbsenceViewable> absences;
|
||||
final bool showDate;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Justification state = getState(absences.map((e) => e.absence.state).toList());
|
||||
Color color = AbsenceTile.justificationColor(state, context: context);
|
||||
|
||||
absences.sort((a, b) => a.absence.lessonIndex?.compareTo(b.absence.lessonIndex ?? 0) ?? -1);
|
||||
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: AbsenceGroupContainer(
|
||||
child: ExpansionTile(
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
backgroundColor: Colors.transparent,
|
||||
leading: Container(
|
||||
width: 44.0,
|
||||
height: 44.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: color.withOpacity(.25),
|
||||
),
|
||||
child: Center(child: Icon(AbsenceTile.justificationIcon(state), color: color)),
|
||||
),
|
||||
title: Text.rich(TextSpan(
|
||||
text: "${absences.where((a) => a.absence.state == state).length} ",
|
||||
style: TextStyle(fontWeight: FontWeight.w700, color: AppColors.of(context).text),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: AbsenceTile.justificationName(state).fill(["absence".i18n]),
|
||||
style: TextStyle(fontWeight: FontWeight.w600, color: AppColors.of(context).text),
|
||||
),
|
||||
],
|
||||
)),
|
||||
subtitle: showDate
|
||||
? Text(
|
||||
absences.first.absence.date.format(context, weekday: true),
|
||||
style: TextStyle(fontWeight: FontWeight.w500, color: AppColors.of(context).text.withOpacity(0.8)),
|
||||
)
|
||||
: null,
|
||||
children: absences,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Justification getState(List<Justification> states) {
|
||||
Justification state;
|
||||
|
||||
if (states.any((element) => element == Justification.unexcused)) {
|
||||
state = Justification.unexcused;
|
||||
} else if (states.any((element) => element == Justification.pending)) {
|
||||
state = Justification.pending;
|
||||
} else {
|
||||
state = Justification.excused;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"absence": "absences",
|
||||
},
|
||||
"hu_hu": {
|
||||
"absence": "hiányzás",
|
||||
},
|
||||
"de_de": {
|
||||
"absence": "Fehlen",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"absence": "absences",
|
||||
},
|
||||
"hu_hu": {
|
||||
"absence": "hiányzás",
|
||||
},
|
||||
"de_de": {
|
||||
"absence": "Fehlen",
|
||||
}
|
||||
};
|
||||
|
||||
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,27 +1,27 @@
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CardHandle extends StatelessWidget {
|
||||
const CardHandle({Key? key, this.child}) : super(key: key);
|
||||
|
||||
final Widget? child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 42.0,
|
||||
height: 4.0,
|
||||
margin: const EdgeInsets.only(top: 12.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(45.0),
|
||||
color: AppColors.of(context).text.withOpacity(0.10),
|
||||
),
|
||||
),
|
||||
if (child != null) child!,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CardHandle extends StatelessWidget {
|
||||
const CardHandle({Key? key, this.child}) : super(key: key);
|
||||
|
||||
final Widget? child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 42.0,
|
||||
height: 4.0,
|
||||
margin: const EdgeInsets.only(top: 12.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(45.0),
|
||||
color: AppColors.of(context).text.withOpacity(0.10),
|
||||
),
|
||||
),
|
||||
if (child != null) child!,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,108 +1,108 @@
|
||||
import 'package:filcnaplo/helpers/average_helper.dart';
|
||||
import 'package:i18n_extension/i18n_widget.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/cretification/certification_view.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'certification_card.i18n.dart';
|
||||
|
||||
class CertificationCard extends StatelessWidget {
|
||||
const CertificationCard(this.grades, {Key? key, required this.gradeType, this.padding}) : super(key: key);
|
||||
|
||||
final List<Grade> grades;
|
||||
final GradeType gradeType;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String title = getGradeTypeTitle(gradeType);
|
||||
double average = AverageHelper.averageEvals(grades, finalAvg: true);
|
||||
String averageText = average.toStringAsFixed(1);
|
||||
if (I18n.of(context).locale.languageCode != "en") averageText = averageText.replaceAll(".", ",");
|
||||
Color color = gradeColor(context: context, value: average);
|
||||
Color textColor;
|
||||
|
||||
if (color.computeLuminance() >= .5) {
|
||||
textColor = Colors.black;
|
||||
} else {
|
||||
textColor = Colors.white;
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(vertical: 2.0, horizontal: 8.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
gradient: LinearGradient(
|
||||
colors: [color, color.withOpacity(.75)],
|
||||
),
|
||||
),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 16.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
||||
leading: Text(
|
||||
averageText,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 24.0,
|
||||
),
|
||||
),
|
||||
title: Text.rich(
|
||||
TextSpan(
|
||||
text: title,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: " • ${grades.length}",
|
||||
style: TextStyle(
|
||||
color: textColor.withOpacity(.75),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18.0,
|
||||
),
|
||||
),
|
||||
trailing: Icon(FeatherIcons.arrowRight, color: textColor),
|
||||
onTap: () => CertificationView.show(grades, context: context, gradeType: gradeType),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String getGradeTypeTitle(GradeType gradeType) {
|
||||
String title;
|
||||
|
||||
switch (gradeType) {
|
||||
case GradeType.halfYear:
|
||||
title = "mid".i18n;
|
||||
break;
|
||||
case GradeType.firstQ:
|
||||
title = "1q".i18n;
|
||||
break;
|
||||
case GradeType.secondQ:
|
||||
title = "2q".i18n;
|
||||
break;
|
||||
case GradeType.thirdQ:
|
||||
title = "3q".i18n;
|
||||
break;
|
||||
case GradeType.fourthQ:
|
||||
title = "4q".i18n;
|
||||
break;
|
||||
default:
|
||||
title = "final".i18n;
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
import 'package:filcnaplo/helpers/average_helper.dart';
|
||||
import 'package:i18n_extension/i18n_widget.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/cretification/certification_view.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'certification_card.i18n.dart';
|
||||
|
||||
class CertificationCard extends StatelessWidget {
|
||||
const CertificationCard(this.grades, {Key? key, required this.gradeType, this.padding}) : super(key: key);
|
||||
|
||||
final List<Grade> grades;
|
||||
final GradeType gradeType;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String title = getGradeTypeTitle(gradeType);
|
||||
double average = AverageHelper.averageEvals(grades, finalAvg: true);
|
||||
String averageText = average.toStringAsFixed(1);
|
||||
if (I18n.of(context).locale.languageCode != "en") averageText = averageText.replaceAll(".", ",");
|
||||
Color color = gradeColor(context: context, value: average);
|
||||
Color textColor;
|
||||
|
||||
if (color.computeLuminance() >= .5) {
|
||||
textColor = Colors.black;
|
||||
} else {
|
||||
textColor = Colors.white;
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(vertical: 2.0, horizontal: 8.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
gradient: LinearGradient(
|
||||
colors: [color, color.withOpacity(.75)],
|
||||
),
|
||||
),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 16.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
||||
leading: Text(
|
||||
averageText,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 24.0,
|
||||
),
|
||||
),
|
||||
title: Text.rich(
|
||||
TextSpan(
|
||||
text: title,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: " • ${grades.length}",
|
||||
style: TextStyle(
|
||||
color: textColor.withOpacity(.75),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18.0,
|
||||
),
|
||||
),
|
||||
trailing: Icon(FeatherIcons.arrowRight, color: textColor),
|
||||
onTap: () => CertificationView.show(grades, context: context, gradeType: gradeType),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String getGradeTypeTitle(GradeType gradeType) {
|
||||
String title;
|
||||
|
||||
switch (gradeType) {
|
||||
case GradeType.halfYear:
|
||||
title = "mid".i18n;
|
||||
break;
|
||||
case GradeType.firstQ:
|
||||
title = "1q".i18n;
|
||||
break;
|
||||
case GradeType.secondQ:
|
||||
title = "2q".i18n;
|
||||
break;
|
||||
case GradeType.thirdQ:
|
||||
title = "3q".i18n;
|
||||
break;
|
||||
case GradeType.fourthQ:
|
||||
title = "4q".i18n;
|
||||
break;
|
||||
default:
|
||||
title = "final".i18n;
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"final": "Final grades",
|
||||
"mid": "Midterm grades",
|
||||
"1q": "1. Quarter grades",
|
||||
"2q": "2. Quarter grades",
|
||||
"3q": "3. Quarter grades",
|
||||
"4q": "4. Quarter grades",
|
||||
},
|
||||
"hu_hu": {
|
||||
"final": "Év végi jegyek",
|
||||
"mid": "Félévi jegyek",
|
||||
"1q": "1. Negyedéves jegyek",
|
||||
"2q": "2. Negyedéves jegyek",
|
||||
"3q": "3. Negyedéves jegyek",
|
||||
"4q": "4. Negyedéves jegyek",
|
||||
},
|
||||
"de_de": {
|
||||
"final": "Zeugnis Noten",
|
||||
"mid": "Halbjährlich Noten",
|
||||
"1q": "1. Quartal Noten",
|
||||
"2q": "2. Quartal Noten",
|
||||
"3q": "3. Quartal Noten",
|
||||
"4q": "4. Quartal Noten",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"final": "Final grades",
|
||||
"mid": "Midterm grades",
|
||||
"1q": "1. Quarter grades",
|
||||
"2q": "2. Quarter grades",
|
||||
"3q": "3. Quarter grades",
|
||||
"4q": "4. Quarter grades",
|
||||
},
|
||||
"hu_hu": {
|
||||
"final": "Év végi jegyek",
|
||||
"mid": "Félévi jegyek",
|
||||
"1q": "1. Negyedéves jegyek",
|
||||
"2q": "2. Negyedéves jegyek",
|
||||
"3q": "3. Negyedéves jegyek",
|
||||
"4q": "4. Negyedéves jegyek",
|
||||
},
|
||||
"de_de": {
|
||||
"final": "Zeugnis Noten",
|
||||
"mid": "Halbjährlich Noten",
|
||||
"1q": "1. Quartal Noten",
|
||||
"2q": "2. Quartal Noten",
|
||||
"3q": "3. Quartal Noten",
|
||||
"4q": "4. Quartal Noten",
|
||||
}
|
||||
};
|
||||
|
||||
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,87 +1,87 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.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:filcnaplo/utils/format.dart';
|
||||
import 'certification_tile.i18n.dart';
|
||||
|
||||
class CertificationTile extends StatelessWidget {
|
||||
const CertificationTile(this.grade, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Function()? onTap;
|
||||
final Grade grade;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isSubjectView = SubjectGradesContainer.of(context) != null;
|
||||
String certificationName;
|
||||
|
||||
switch (grade.type) {
|
||||
case GradeType.endYear:
|
||||
certificationName = "final".i18n;
|
||||
break;
|
||||
case GradeType.halfYear:
|
||||
certificationName = "mid".i18n;
|
||||
break;
|
||||
case GradeType.firstQ:
|
||||
certificationName = "1q".i18n;
|
||||
break;
|
||||
case GradeType.secondQ:
|
||||
certificationName = "2q".i18n;
|
||||
break;
|
||||
case GradeType.thirdQ:
|
||||
certificationName = "3q".i18n;
|
||||
break;
|
||||
case GradeType.fourthQ:
|
||||
certificationName = "4q".i18n;
|
||||
break;
|
||||
case GradeType.levelExam:
|
||||
certificationName = "equivalency".i18n;
|
||||
break;
|
||||
case GradeType.unknown:
|
||||
default:
|
||||
certificationName = "unknown".i18n;
|
||||
}
|
||||
|
||||
return Material(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding:
|
||||
isSubjectView ? const EdgeInsets.only(left: 12.0, right: 12.0, top: 2.0, bottom: 8.0) : const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
onTap: onTap,
|
||||
leading: isSubjectView
|
||||
? GradeValueWidget(
|
||||
grade.value,
|
||||
complemented: grade.description == 'Dicséret',
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(left: 2.0),
|
||||
child: Icon(SubjectIcon.resolveVariant(subject: grade.subject, context: context),
|
||||
size: 28.0, color: AppColors.of(context).text.withOpacity(.75)),
|
||||
),
|
||||
minLeadingWidth: isSubjectView ? 32.0 : 42.0,
|
||||
trailing: isSubjectView
|
||||
? const Icon(FeatherIcons.award)
|
||||
: GradeValueWidget(
|
||||
grade.value,
|
||||
complemented: grade.description == 'Dicséret',
|
||||
),
|
||||
title: Text(isSubjectView ? certificationName : grade.subject.renamedTo ?? grade.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 18.0, fontStyle: grade.subject.isRenamed ? FontStyle.italic : null)),
|
||||
subtitle: Text(grade.value.valueName, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16.0)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.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:filcnaplo/utils/format.dart';
|
||||
import 'certification_tile.i18n.dart';
|
||||
|
||||
class CertificationTile extends StatelessWidget {
|
||||
const CertificationTile(this.grade, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Function()? onTap;
|
||||
final Grade grade;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isSubjectView = SubjectGradesContainer.of(context) != null;
|
||||
String certificationName;
|
||||
|
||||
switch (grade.type) {
|
||||
case GradeType.endYear:
|
||||
certificationName = "final".i18n;
|
||||
break;
|
||||
case GradeType.halfYear:
|
||||
certificationName = "mid".i18n;
|
||||
break;
|
||||
case GradeType.firstQ:
|
||||
certificationName = "1q".i18n;
|
||||
break;
|
||||
case GradeType.secondQ:
|
||||
certificationName = "2q".i18n;
|
||||
break;
|
||||
case GradeType.thirdQ:
|
||||
certificationName = "3q".i18n;
|
||||
break;
|
||||
case GradeType.fourthQ:
|
||||
certificationName = "4q".i18n;
|
||||
break;
|
||||
case GradeType.levelExam:
|
||||
certificationName = "equivalency".i18n;
|
||||
break;
|
||||
case GradeType.unknown:
|
||||
default:
|
||||
certificationName = "unknown".i18n;
|
||||
}
|
||||
|
||||
return Material(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding:
|
||||
isSubjectView ? const EdgeInsets.only(left: 12.0, right: 12.0, top: 2.0, bottom: 8.0) : const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
onTap: onTap,
|
||||
leading: isSubjectView
|
||||
? GradeValueWidget(
|
||||
grade.value,
|
||||
complemented: grade.description == 'Dicséret',
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(left: 2.0),
|
||||
child: Icon(SubjectIcon.resolveVariant(subject: grade.subject, context: context),
|
||||
size: 28.0, color: AppColors.of(context).text.withOpacity(.75)),
|
||||
),
|
||||
minLeadingWidth: isSubjectView ? 32.0 : 42.0,
|
||||
trailing: isSubjectView
|
||||
? const Icon(FeatherIcons.award)
|
||||
: GradeValueWidget(
|
||||
grade.value,
|
||||
complemented: grade.description == 'Dicséret',
|
||||
),
|
||||
title: Text(isSubjectView ? certificationName : grade.subject.renamedTo ?? grade.subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 18.0, fontStyle: grade.subject.isRenamed ? FontStyle.italic : null)),
|
||||
subtitle: Text(grade.value.valueName, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16.0)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"final": "Final",
|
||||
"mid": "Mid year",
|
||||
"1q": "1. Quarter",
|
||||
"2q": "2. Quarter",
|
||||
"3q": "3. Quarter",
|
||||
"4q": "4. Quarter",
|
||||
"equivalency": "Equivalency test",
|
||||
"unknown": "Unknown",
|
||||
"classavg": "Class Average",
|
||||
},
|
||||
"hu_hu": {
|
||||
"final": "Év vége",
|
||||
"mid": "Félév",
|
||||
"1q": "1. Negyedév",
|
||||
"2q": "2. Negyedév",
|
||||
"3q": "3. Negyedév",
|
||||
"4q": "4. Negyedév",
|
||||
"equivalency": "Osztályozó",
|
||||
"unknown": "Ismeretlen",
|
||||
"classavg": "Osztályátlag",
|
||||
},
|
||||
"de_de": {
|
||||
"final": "Zeugnis",
|
||||
"mid": "Halbjährlich",
|
||||
"1q": "1. Quartal",
|
||||
"2q": "2. Quartal",
|
||||
"3q": "3. Quartal",
|
||||
"4q": "4. Quartal",
|
||||
"equivalency": "Zulassungsprüfung",
|
||||
"unknown": "Unbekannt",
|
||||
"classavg": "Klassendurchschnitt",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"final": "Final",
|
||||
"mid": "Mid year",
|
||||
"1q": "1. Quarter",
|
||||
"2q": "2. Quarter",
|
||||
"3q": "3. Quarter",
|
||||
"4q": "4. Quarter",
|
||||
"equivalency": "Equivalency test",
|
||||
"unknown": "Unknown",
|
||||
"classavg": "Class Average",
|
||||
},
|
||||
"hu_hu": {
|
||||
"final": "Év vége",
|
||||
"mid": "Félév",
|
||||
"1q": "1. Negyedév",
|
||||
"2q": "2. Negyedév",
|
||||
"3q": "3. Negyedév",
|
||||
"4q": "4. Negyedév",
|
||||
"equivalency": "Osztályozó",
|
||||
"unknown": "Ismeretlen",
|
||||
"classavg": "Osztályátlag",
|
||||
},
|
||||
"de_de": {
|
||||
"final": "Zeugnis",
|
||||
"mid": "Halbjährlich",
|
||||
"1q": "1. Quartal",
|
||||
"2q": "2. Quartal",
|
||||
"3q": "3. Quartal",
|
||||
"4q": "4. Quartal",
|
||||
"equivalency": "Zulassungsprüfung",
|
||||
"unknown": "Unbekannt",
|
||||
"classavg": "Klassendurchschnitt",
|
||||
}
|
||||
};
|
||||
|
||||
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,43 +1,43 @@
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/cretification/certification_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/cretification/certification_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/hero_scrollview.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
class CertificationView extends StatelessWidget {
|
||||
const CertificationView(this.grades, {Key? key, required this.gradeType}) : super(key: key);
|
||||
|
||||
final List<Grade> grades;
|
||||
final GradeType gradeType;
|
||||
|
||||
static show(List<Grade> grades, {required BuildContext context, required GradeType gradeType}) =>
|
||||
Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute(builder: (context) => CertificationView(grades, gradeType: gradeType)));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
grades.sort((a, b) => a.subject.name.compareTo(b.subject.name));
|
||||
List<Widget> tiles = grades.map((e) => CertificationTile(e)).toList();
|
||||
return Scaffold(
|
||||
body: HeroScrollView(
|
||||
title: getGradeTypeTitle(gradeType),
|
||||
icon: FeatherIcons.award,
|
||||
iconSize: 50,
|
||||
child: ListView(
|
||||
children: [
|
||||
SafeArea(
|
||||
child: Panel(
|
||||
child: Column(
|
||||
children: tiles,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/cretification/certification_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/cretification/certification_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/hero_scrollview.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
class CertificationView extends StatelessWidget {
|
||||
const CertificationView(this.grades, {Key? key, required this.gradeType}) : super(key: key);
|
||||
|
||||
final List<Grade> grades;
|
||||
final GradeType gradeType;
|
||||
|
||||
static show(List<Grade> grades, {required BuildContext context, required GradeType gradeType}) =>
|
||||
Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute(builder: (context) => CertificationView(grades, gradeType: gradeType)));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
grades.sort((a, b) => a.subject.name.compareTo(b.subject.name));
|
||||
List<Widget> tiles = grades.map((e) => CertificationTile(e)).toList();
|
||||
return Scaffold(
|
||||
body: HeroScrollView(
|
||||
title: getGradeTypeTitle(gradeType),
|
||||
icon: FeatherIcons.award,
|
||||
iconSize: 50,
|
||||
child: ListView(
|
||||
children: [
|
||||
SafeArea(
|
||||
child: Panel(
|
||||
child: Column(
|
||||
children: tiles,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomSwitch extends StatelessWidget {
|
||||
final ValueChanged<bool> onChanged;
|
||||
final bool value;
|
||||
|
||||
const CustomSwitch({
|
||||
Key? key,
|
||||
required this.onChanged,
|
||||
required this.value,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () => onChanged(!value),
|
||||
child: SizedBox(
|
||||
height: 25,
|
||||
width: 50,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
AnimatedContainer(
|
||||
height: 25,
|
||||
width: 50,
|
||||
curve: Curves.ease,
|
||||
duration: const Duration(milliseconds: 400),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(25.0),
|
||||
),
|
||||
color: value ? Theme.of(context).colorScheme.secondary : Theme.of(context).highlightColor,
|
||||
),
|
||||
),
|
||||
AnimatedAlign(
|
||||
curve: Curves.ease,
|
||||
duration: const Duration(milliseconds: 400),
|
||||
alignment: !value ? Alignment.centerLeft : Alignment.centerRight,
|
||||
child: Container(
|
||||
height: 20,
|
||||
width: 20,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 3),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black12.withOpacity(0.1),
|
||||
spreadRadius: 0.5,
|
||||
blurRadius: 1,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomSwitch extends StatelessWidget {
|
||||
final ValueChanged<bool> onChanged;
|
||||
final bool value;
|
||||
|
||||
const CustomSwitch({
|
||||
Key? key,
|
||||
required this.onChanged,
|
||||
required this.value,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () => onChanged(!value),
|
||||
child: SizedBox(
|
||||
height: 25,
|
||||
width: 50,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
AnimatedContainer(
|
||||
height: 25,
|
||||
width: 50,
|
||||
curve: Curves.ease,
|
||||
duration: const Duration(milliseconds: 400),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(25.0),
|
||||
),
|
||||
color: value ? Theme.of(context).colorScheme.secondary : Theme.of(context).highlightColor,
|
||||
),
|
||||
),
|
||||
AnimatedAlign(
|
||||
curve: Curves.ease,
|
||||
duration: const Duration(milliseconds: 400),
|
||||
alignment: !value ? Alignment.centerLeft : Alignment.centerRight,
|
||||
child: Container(
|
||||
height: 20,
|
||||
width: 20,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 3),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black12.withOpacity(0.1),
|
||||
spreadRadius: 0.5,
|
||||
blurRadius: 1,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
import 'package:filcnaplo_kreta_api/models/event.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class EventTile extends StatelessWidget {
|
||||
const EventTile(this.event, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Event event;
|
||||
final void Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: onTap,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
leading: const ProfileImage(
|
||||
name: "!",
|
||||
radius: 22.0,
|
||||
),
|
||||
title: Text(
|
||||
event.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
event.content.escapeHtml().replaceAll('\n', ' '),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/event.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class EventTile extends StatelessWidget {
|
||||
const EventTile(this.event, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Event event;
|
||||
final void Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: onTap,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
leading: const ProfileImage(
|
||||
name: "!",
|
||||
radius: 22.0,
|
||||
),
|
||||
title: Text(
|
||||
event.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
event.content.escapeHtml().replaceAll('\n', ' '),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
import 'package:filcnaplo_kreta_api/models/event.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/sliding_bottom_sheet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
|
||||
class EventView extends StatelessWidget {
|
||||
const EventView(this.event, {Key? key}) : super(key: key);
|
||||
|
||||
final Event event;
|
||||
|
||||
static void show(Event event, {required BuildContext context}) => showSlidingBottomSheet(context: context, child: EventView(event));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
ListTile(
|
||||
title: Text(
|
||||
event.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
trailing: Text(
|
||||
event.start.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Details
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: SelectableLinkify(
|
||||
text: event.content.escapeHtml(),
|
||||
options: const LinkifyOptions(looseUrl: true, removeWww: true),
|
||||
onOpen: (link) {
|
||||
launch(link.url,
|
||||
customTabsOption: CustomTabsOption(
|
||||
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
showPageTitle: true,
|
||||
));
|
||||
},
|
||||
style: const TextStyle(fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/event.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/sliding_bottom_sheet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
|
||||
class EventView extends StatelessWidget {
|
||||
const EventView(this.event, {Key? key}) : super(key: key);
|
||||
|
||||
final Event event;
|
||||
|
||||
static void show(Event event, {required BuildContext context}) => showSlidingBottomSheet(context: context, child: EventView(event));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
ListTile(
|
||||
title: Text(
|
||||
event.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
trailing: Text(
|
||||
event.start.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Details
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: SelectableLinkify(
|
||||
text: event.content.escapeHtml(),
|
||||
options: const LinkifyOptions(looseUrl: true, removeWww: true),
|
||||
onOpen: (link) {
|
||||
launch(link.url,
|
||||
customTabsOption: CustomTabsOption(
|
||||
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
showPageTitle: true,
|
||||
));
|
||||
},
|
||||
style: const TextStyle(fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import 'package:filcnaplo_kreta_api/models/event.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/event/event_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/event/event_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class EventViewable extends StatelessWidget {
|
||||
const EventViewable(this.event, {Key? key}) : super(key: key);
|
||||
|
||||
final Event event;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return EventTile(
|
||||
event,
|
||||
onTap: () => EventView.show(event, context: context),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/event.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/event/event_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/event/event_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class EventViewable extends StatelessWidget {
|
||||
const EventViewable(this.event, {Key? key}) : super(key: key);
|
||||
|
||||
final Event event;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return EventTile(
|
||||
event,
|
||||
onTap: () => EventView.show(event, context: context),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/exam.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
class ExamTile extends StatelessWidget {
|
||||
const ExamTile(this.exam, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Exam exam;
|
||||
final void Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: onTap,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
leading: SizedBox(
|
||||
width: 44,
|
||||
height: 44,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 2.0),
|
||||
child: Icon(
|
||||
SubjectIcon.resolveVariant(subjectName: exam.subjectName, context: context),
|
||||
size: 28.0,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
)),
|
||||
title: Text(
|
||||
exam.description != "" ? exam.description : (exam.mode?.description ?? "Számonkérés"),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
exam.subjectName.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Icon(
|
||||
FeatherIcons.edit,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/exam.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
class ExamTile extends StatelessWidget {
|
||||
const ExamTile(this.exam, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Exam exam;
|
||||
final void Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: onTap,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
leading: SizedBox(
|
||||
width: 44,
|
||||
height: 44,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 2.0),
|
||||
child: Icon(
|
||||
SubjectIcon.resolveVariant(subjectName: exam.subjectName, context: context),
|
||||
size: 28.0,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
)),
|
||||
title: Text(
|
||||
exam.description != "" ? exam.description : (exam.mode?.description ?? "Számonkérés"),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
exam.subjectName.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Icon(
|
||||
FeatherIcons.edit,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/exam.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'exam_view.i18n.dart';
|
||||
|
||||
class ExamView extends StatelessWidget {
|
||||
const ExamView(this.exam, {Key? key}) : super(key: key);
|
||||
|
||||
final Exam exam;
|
||||
|
||||
static show(Exam exam, {required BuildContext context}) => showBottomCard(context: context, child: ExamView(exam));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
ListTile(
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.only(left: 6.0),
|
||||
child: Icon(
|
||||
SubjectIcon.resolveVariant(subjectName: exam.subjectName, context: context),
|
||||
size: 36.0,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
exam.subjectName.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
exam.teacher,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
exam.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Details
|
||||
if (exam.writeDate.year != 0) Detail(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),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/exam.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'exam_view.i18n.dart';
|
||||
|
||||
class ExamView extends StatelessWidget {
|
||||
const ExamView(this.exam, {Key? key}) : super(key: key);
|
||||
|
||||
final Exam exam;
|
||||
|
||||
static show(Exam exam, {required BuildContext context}) => showBottomCard(context: context, child: ExamView(exam));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
ListTile(
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.only(left: 6.0),
|
||||
child: Icon(
|
||||
SubjectIcon.resolveVariant(subjectName: exam.subjectName, context: context),
|
||||
size: 36.0,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
exam.subjectName.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
exam.teacher,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
exam.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Details
|
||||
if (exam.writeDate.year != 0) Detail(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),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"date": "Date",
|
||||
"description": "Description",
|
||||
"mode": "Type",
|
||||
},
|
||||
"hu_hu": {
|
||||
"date": "Írás ideje",
|
||||
"description": "Leírás",
|
||||
"mode": "Típus",
|
||||
},
|
||||
"de_de": {
|
||||
"date": "Prüfungszeit",
|
||||
"description": "Bezeichnung",
|
||||
"mode": "Typ",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"date": "Date",
|
||||
"description": "Description",
|
||||
"mode": "Type",
|
||||
},
|
||||
"hu_hu": {
|
||||
"date": "Írás ideje",
|
||||
"description": "Leírás",
|
||||
"mode": "Típus",
|
||||
},
|
||||
"de_de": {
|
||||
"date": "Prüfungszeit",
|
||||
"description": "Bezeichnung",
|
||||
"mode": "Typ",
|
||||
}
|
||||
};
|
||||
|
||||
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,20 +1,20 @@
|
||||
import 'package:filcnaplo_kreta_api/models/exam.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/viewable.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/card_handle.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/exam/exam_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/exam/exam_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ExamViewable extends StatelessWidget {
|
||||
const ExamViewable(this.exam, {Key? key}) : super(key: key);
|
||||
|
||||
final Exam exam;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Viewable(
|
||||
tile: ExamTile(exam),
|
||||
view: CardHandle(child: ExamView(exam)),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/exam.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/viewable.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/card_handle.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/exam/exam_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/exam/exam_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ExamViewable extends StatelessWidget {
|
||||
const ExamViewable(this.exam, {Key? key}) : super(key: key);
|
||||
|
||||
final Exam exam;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Viewable(
|
||||
tile: ExamTile(exam),
|
||||
view: CardHandle(child: ExamView(exam)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +1,70 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/subject.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/average_display.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class GradeSubjectTile extends StatelessWidget {
|
||||
const GradeSubjectTile(this.subject, {Key? key, this.average = 0.0, this.groupAverage = 0.0, this.onTap, this.averageBefore = 0.0})
|
||||
: super(key: key);
|
||||
|
||||
final Subject subject;
|
||||
final void Function()? onTap;
|
||||
final double average;
|
||||
final double groupAverage;
|
||||
final double averageBefore;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color textColor = AppColors.of(context).text;
|
||||
|
||||
// Failing indicator
|
||||
if (average < 2.0 && average >= 1.0) {
|
||||
textColor = AppColors.of(context).red;
|
||||
}
|
||||
|
||||
final String changeIcon = average < averageBefore ? "▼" : "▲";
|
||||
final Color changeColor = average < averageBefore ? Colors.redAccent : Colors.lightGreenAccent.shade700;
|
||||
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: ListTile(
|
||||
minLeadingWidth: 32.0,
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 6.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onTap: onTap,
|
||||
leading: Icon(SubjectIcon.resolveVariant(subject: subject, context: context), color: textColor.withOpacity(.75)),
|
||||
title: Text(
|
||||
subject.renamedTo ?? subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 14.0, color: textColor, fontStyle: subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (groupAverage != 0 && averageBefore == 0.0) AverageDisplay(average: groupAverage, border: true),
|
||||
const SizedBox(width: 6.0),
|
||||
if (averageBefore != 0.0 && averageBefore != average) ...[
|
||||
AverageDisplay(average: averageBefore),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6.0, right: 6.0, bottom: 3.5),
|
||||
child: Text(
|
||||
changeIcon,
|
||||
style: TextStyle(
|
||||
color: changeColor,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
AverageDisplay(average: average)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/subject.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/average_display.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class GradeSubjectTile extends StatelessWidget {
|
||||
const GradeSubjectTile(this.subject, {Key? key, this.average = 0.0, this.groupAverage = 0.0, this.onTap, this.averageBefore = 0.0})
|
||||
: super(key: key);
|
||||
|
||||
final Subject subject;
|
||||
final void Function()? onTap;
|
||||
final double average;
|
||||
final double groupAverage;
|
||||
final double averageBefore;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color textColor = AppColors.of(context).text;
|
||||
|
||||
// Failing indicator
|
||||
if (average < 2.0 && average >= 1.0) {
|
||||
textColor = AppColors.of(context).red;
|
||||
}
|
||||
|
||||
final String changeIcon = average < averageBefore ? "▼" : "▲";
|
||||
final Color changeColor = average < averageBefore ? Colors.redAccent : Colors.lightGreenAccent.shade700;
|
||||
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: ListTile(
|
||||
minLeadingWidth: 32.0,
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 6.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onTap: onTap,
|
||||
leading: Icon(SubjectIcon.resolveVariant(subject: subject, context: context), color: textColor.withOpacity(.75)),
|
||||
title: Text(
|
||||
subject.renamedTo ?? subject.name.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 14.0, color: textColor, fontStyle: subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (groupAverage != 0 && averageBefore == 0.0) AverageDisplay(average: groupAverage, border: true),
|
||||
const SizedBox(width: 6.0),
|
||||
if (averageBefore != 0.0 && averageBefore != average) ...[
|
||||
AverageDisplay(average: averageBefore),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6.0, right: 6.0, bottom: 3.5),
|
||||
child: Text(
|
||||
changeIcon,
|
||||
style: TextStyle(
|
||||
color: changeColor,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
AverageDisplay(average: average)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'grade_view.i18n.dart';
|
||||
|
||||
class GradeView extends StatelessWidget {
|
||||
const GradeView(this.grade, {Key? key}) : super(key: key);
|
||||
|
||||
static show(Grade grade, {required BuildContext context}) => showBottomCard(context: context, child: GradeView(grade));
|
||||
|
||||
final Grade grade;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: GradeValueWidget(grade.value, fill: true),
|
||||
title: Text(
|
||||
grade.subject.renamedTo ?? grade.subject.name.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: grade.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
!Provider.of<SettingsProvider>(context, listen: false).presentationMode ? grade.teacher : "Tanár",
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
grade.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Grade Details
|
||||
Detail(
|
||||
title: "value".i18n,
|
||||
description: "${grade.value.valueName} " + percentText(),
|
||||
),
|
||||
if (grade.description != "") Detail(title: "description".i18n, description: grade.description),
|
||||
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}%" : "";
|
||||
}
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'grade_view.i18n.dart';
|
||||
|
||||
class GradeView extends StatelessWidget {
|
||||
const GradeView(this.grade, {Key? key}) : super(key: key);
|
||||
|
||||
static show(Grade grade, {required BuildContext context}) => showBottomCard(context: context, child: GradeView(grade));
|
||||
|
||||
final Grade grade;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: GradeValueWidget(grade.value, fill: true),
|
||||
title: Text(
|
||||
grade.subject.renamedTo ?? grade.subject.name.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: grade.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
!Provider.of<SettingsProvider>(context, listen: false).presentationMode ? grade.teacher : "Tanár",
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
grade.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Grade Details
|
||||
Detail(
|
||||
title: "value".i18n,
|
||||
description: "${grade.value.valueName} " + percentText(),
|
||||
),
|
||||
if (grade.description != "") Detail(title: "description".i18n, description: grade.description),
|
||||
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}%" : "";
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"value": "Value",
|
||||
"date": "Date",
|
||||
"description": "Description",
|
||||
"mode": "Type",
|
||||
},
|
||||
"hu_hu": {
|
||||
"value": "Érték",
|
||||
"date": "Írás ideje",
|
||||
"description": "Leírás",
|
||||
"mode": "Típus",
|
||||
},
|
||||
"de_de": {
|
||||
"value": "Notenwert",
|
||||
"date": "Prüfungszeit",
|
||||
"description": "Bezeichnung",
|
||||
"mode": "Typ",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"value": "Value",
|
||||
"date": "Date",
|
||||
"description": "Description",
|
||||
"mode": "Type",
|
||||
},
|
||||
"hu_hu": {
|
||||
"value": "Érték",
|
||||
"date": "Írás ideje",
|
||||
"description": "Leírás",
|
||||
"mode": "Típus",
|
||||
},
|
||||
"de_de": {
|
||||
"value": "Notenwert",
|
||||
"date": "Prüfungszeit",
|
||||
"description": "Bezeichnung",
|
||||
"mode": "Typ",
|
||||
}
|
||||
};
|
||||
|
||||
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,25 +1,25 @@
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/card_handle.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/grade/grade_view.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/viewable.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/grades/subject_grades_container.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class GradeViewable extends StatelessWidget {
|
||||
const GradeViewable(this.grade, {Key? key, this.padding}) : super(key: key);
|
||||
|
||||
final Grade grade;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final subject = SubjectGradesContainer.of(context) != null;
|
||||
final tile = GradeTile(grade, padding: subject ? EdgeInsets.zero : padding);
|
||||
|
||||
return Viewable(
|
||||
tile: subject ? SubjectGradesContainer(child: tile) : tile,
|
||||
view: CardHandle(child: GradeView(grade)),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/card_handle.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/grade/grade_view.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/viewable.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/grades/subject_grades_container.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class GradeViewable extends StatelessWidget {
|
||||
const GradeViewable(this.grade, {Key? key, this.padding}) : super(key: key);
|
||||
|
||||
final Grade grade;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final subject = SubjectGradesContainer.of(context) != null;
|
||||
final tile = GradeTile(grade, padding: subject ? EdgeInsets.zero : padding);
|
||||
|
||||
return Viewable(
|
||||
tile: subject ? SubjectGradesContainer(child: tile) : tile,
|
||||
view: CardHandle(child: GradeView(grade)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,158 +1,158 @@
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/grade/surprise_grade.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:rive/rive.dart';
|
||||
|
||||
import 'new_grades.i18n.dart';
|
||||
|
||||
class NewGradesSurprise extends StatelessWidget {
|
||||
const NewGradesSurprise(this.grades, {Key? key, this.censored = false}) : super(key: key);
|
||||
|
||||
final List<Grade> grades;
|
||||
final bool censored;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
width: 3.0,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
),
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: () => openingFun(context),
|
||||
minLeadingWidth: 54,
|
||||
leading: SizedBox(
|
||||
width: 44,
|
||||
height: 44,
|
||||
child: Center(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).colorScheme.secondary.withOpacity(.5),
|
||||
blurRadius: 18.0,
|
||||
)
|
||||
]),
|
||||
child: const RiveAnimation.asset("assets/animations/backpack-2.riv"),
|
||||
),
|
||||
),
|
||||
),
|
||||
title: censored
|
||||
? Wrap(
|
||||
children: [
|
||||
Container(
|
||||
width: 85,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.85),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
"new_grades".i18n,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: censored
|
||||
? Wrap(
|
||||
children: [
|
||||
Container(
|
||||
width: 125,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.45),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
"tap_to_open".i18n,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: censored
|
||||
? Wrap(
|
||||
children: [
|
||||
Container(
|
||||
width: 25,
|
||||
height: 25,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.45),
|
||||
borderRadius: BorderRadius.circular(25.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text.rich(
|
||||
TextSpan(children: [
|
||||
TextSpan(
|
||||
text: "${grades.length}",
|
||||
style: TextStyle(
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: AppColors.of(context).text.withOpacity(.2),
|
||||
offset: const Offset(2, 2),
|
||||
)
|
||||
],
|
||||
)),
|
||||
TextSpan(
|
||||
text: "x",
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
color: AppColors.of(context).text.withOpacity(.5),
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
)
|
||||
]),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 28.0,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void openingFun(BuildContext context) {
|
||||
final settings = Provider.of<SettingsProvider>(context, listen: false);
|
||||
if (!settings.gradeOpeningFun) return;
|
||||
|
||||
final gradeProvider = Provider.of<GradeProvider>(context, listen: false);
|
||||
|
||||
final newGrades = gradeProvider.grades.where((element) => element.date.isAfter(gradeProvider.lastSeenDate)).toList();
|
||||
newGrades.sort((a, b) => a.date.compareTo(b.date));
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
for (final grade in newGrades) {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => SurpriseGrade(grade),
|
||||
useRootNavigator: true,
|
||||
barrierDismissible: false,
|
||||
barrierColor: Colors.transparent,
|
||||
useSafeArea: false,
|
||||
);
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
}
|
||||
await gradeProvider.seenAll();
|
||||
});
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/models/settings.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/grade/surprise_grade.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:rive/rive.dart';
|
||||
|
||||
import 'new_grades.i18n.dart';
|
||||
|
||||
class NewGradesSurprise extends StatelessWidget {
|
||||
const NewGradesSurprise(this.grades, {Key? key, this.censored = false}) : super(key: key);
|
||||
|
||||
final List<Grade> grades;
|
||||
final bool censored;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
width: 3.0,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
),
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: () => openingFun(context),
|
||||
minLeadingWidth: 54,
|
||||
leading: SizedBox(
|
||||
width: 44,
|
||||
height: 44,
|
||||
child: Center(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).colorScheme.secondary.withOpacity(.5),
|
||||
blurRadius: 18.0,
|
||||
)
|
||||
]),
|
||||
child: const RiveAnimation.asset("assets/animations/backpack-2.riv"),
|
||||
),
|
||||
),
|
||||
),
|
||||
title: censored
|
||||
? Wrap(
|
||||
children: [
|
||||
Container(
|
||||
width: 85,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.85),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
"new_grades".i18n,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: censored
|
||||
? Wrap(
|
||||
children: [
|
||||
Container(
|
||||
width: 125,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.45),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
"tap_to_open".i18n,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: censored
|
||||
? Wrap(
|
||||
children: [
|
||||
Container(
|
||||
width: 25,
|
||||
height: 25,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.45),
|
||||
borderRadius: BorderRadius.circular(25.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text.rich(
|
||||
TextSpan(children: [
|
||||
TextSpan(
|
||||
text: "${grades.length}",
|
||||
style: TextStyle(
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: AppColors.of(context).text.withOpacity(.2),
|
||||
offset: const Offset(2, 2),
|
||||
)
|
||||
],
|
||||
)),
|
||||
TextSpan(
|
||||
text: "x",
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
color: AppColors.of(context).text.withOpacity(.5),
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
)
|
||||
]),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 28.0,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void openingFun(BuildContext context) {
|
||||
final settings = Provider.of<SettingsProvider>(context, listen: false);
|
||||
if (!settings.gradeOpeningFun) return;
|
||||
|
||||
final gradeProvider = Provider.of<GradeProvider>(context, listen: false);
|
||||
|
||||
final newGrades = gradeProvider.grades.where((element) => element.date.isAfter(gradeProvider.lastSeenDate)).toList();
|
||||
newGrades.sort((a, b) => a.date.compareTo(b.date));
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
for (final grade in newGrades) {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => SurpriseGrade(grade),
|
||||
useRootNavigator: true,
|
||||
barrierDismissible: false,
|
||||
barrierColor: Colors.transparent,
|
||||
useSafeArea: false,
|
||||
);
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
}
|
||||
await gradeProvider.seenAll();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"common": "Common",
|
||||
"uncommon": "Uncommon",
|
||||
"rare": "Rare",
|
||||
"epic": "Epic",
|
||||
"legendary": "Legendary",
|
||||
"new_grades": "New grades",
|
||||
"tap_to_open": "Tap to open now!",
|
||||
"open_subtitle": "Tap to open...",
|
||||
},
|
||||
"hu_hu": {
|
||||
"common": "Gyakori",
|
||||
"uncommon": "Nem gyakori",
|
||||
"rare": "Ritka",
|
||||
"epic": "Epikus",
|
||||
"legendary": "Legendás",
|
||||
"new_grades": "Új jegyek",
|
||||
"tap_to_open": "Nyisd ki őket!",
|
||||
"open_subtitle": "Nyomd meg a kinyitáshoz...",
|
||||
},
|
||||
"de_de": {
|
||||
"common": "Gemeinsam",
|
||||
"uncommon": "Gelegentlich",
|
||||
"rare": "Selten",
|
||||
"epic": "Episch",
|
||||
"legendary": "Legendär",
|
||||
"new_grades": "Neue Noten",
|
||||
"tap_to_open": "Tippen, um jetzt zu öffnen!",
|
||||
"open_subtitle": "Antippen zum Öffnen...",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"common": "Common",
|
||||
"uncommon": "Uncommon",
|
||||
"rare": "Rare",
|
||||
"epic": "Epic",
|
||||
"legendary": "Legendary",
|
||||
"new_grades": "New grades",
|
||||
"tap_to_open": "Tap to open now!",
|
||||
"open_subtitle": "Tap to open...",
|
||||
},
|
||||
"hu_hu": {
|
||||
"common": "Gyakori",
|
||||
"uncommon": "Nem gyakori",
|
||||
"rare": "Ritka",
|
||||
"epic": "Epikus",
|
||||
"legendary": "Legendás",
|
||||
"new_grades": "Új jegyek",
|
||||
"tap_to_open": "Nyisd ki őket!",
|
||||
"open_subtitle": "Nyomd meg a kinyitáshoz...",
|
||||
},
|
||||
"de_de": {
|
||||
"common": "Gemeinsam",
|
||||
"uncommon": "Gelegentlich",
|
||||
"rare": "Selten",
|
||||
"epic": "Episch",
|
||||
"legendary": "Legendär",
|
||||
"new_grades": "Neue Noten",
|
||||
"tap_to_open": "Tippen, um jetzt zu öffnen!",
|
||||
"open_subtitle": "Antippen zum Öffnen...",
|
||||
}
|
||||
};
|
||||
|
||||
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,389 +1,389 @@
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:animated_background/animated_background.dart' as bg;
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/home/particle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:rive/rive.dart' as rive;
|
||||
|
||||
import 'new_grades.i18n.dart';
|
||||
|
||||
class SurpriseGrade extends StatefulWidget {
|
||||
const SurpriseGrade(this.grade, {Key? key}) : super(key: key);
|
||||
|
||||
final Grade grade;
|
||||
|
||||
@override
|
||||
State<SurpriseGrade> createState() => _SurpriseGradeState();
|
||||
}
|
||||
|
||||
class _SurpriseGradeState extends State<SurpriseGrade> with TickerProviderStateMixin {
|
||||
late AnimationController _revealAnimFade;
|
||||
late AnimationController _revealAnimScale;
|
||||
late AnimationController _revealAnimGrade;
|
||||
late AnimationController _revealAnimParticle;
|
||||
late rive.RiveAnimationController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_revealAnimFade = AnimationController(vsync: this, duration: const Duration(milliseconds: 500));
|
||||
_revealAnimScale = AnimationController(vsync: this, duration: const Duration(milliseconds: 300));
|
||||
_revealAnimGrade = AnimationController(vsync: this, duration: const Duration(seconds: 1));
|
||||
_revealAnimParticle = AnimationController(vsync: this, duration: const Duration(seconds: 2));
|
||||
_revealAnimScale.animateTo(0.7, duration: Duration.zero);
|
||||
_controller = rive.SimpleAnimation('Timeline 1', autoplay: false);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_revealAnimFade.animateTo(1.0, curve: Curves.easeInOut);
|
||||
Future.delayed(const Duration(milliseconds: 200), () {
|
||||
_revealAnimScale.animateTo(1.0, curve: Curves.easeInOut).then((_) {
|
||||
setState(() => subtitle = true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
seed = Random().nextInt(100000000);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_revealAnimFade.dispose();
|
||||
_revealAnimScale.dispose();
|
||||
_revealAnimGrade.dispose();
|
||||
_revealAnimParticle.dispose();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool hold = false;
|
||||
bool subtitle = false;
|
||||
late int seed;
|
||||
|
||||
void reveal() async {
|
||||
if (!subtitle) {
|
||||
_revealAnimParticle.animateBack(0.0, curve: Curves.fastLinearToSlowEaseIn, duration: const Duration(milliseconds: 300));
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
_revealAnimGrade.animateBack(0.0, curve: Curves.fastLinearToSlowEaseIn);
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
_revealAnimFade.animateBack(0.0, curve: Curves.easeInOut);
|
||||
_revealAnimScale.animateBack(0.0, curve: Curves.easeInOut);
|
||||
if (mounted) Navigator.of(context).pop();
|
||||
return;
|
||||
}
|
||||
subtitle = false;
|
||||
setState(() => hold = false);
|
||||
_controller.isActive = true;
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
if (mounted) _revealAnimGrade.animateTo(1.0, curve: Curves.fastLinearToSlowEaseIn);
|
||||
await Future.delayed(const Duration(milliseconds: 700));
|
||||
if (mounted) await _revealAnimParticle.animateTo(1.0, curve: Curves.fastLinearToSlowEaseIn);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: _revealAnimFade,
|
||||
builder: (context, child) {
|
||||
return FadeTransition(
|
||||
opacity: _revealAnimFade,
|
||||
child: Material(
|
||||
color: Colors.black.withOpacity(.75),
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.secondary.withOpacity(.05),
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: RadialGradient(
|
||||
colors: [Colors.transparent, Colors.black],
|
||||
radius: 1.5,
|
||||
stops: [0.2, 1.0],
|
||||
),
|
||||
),
|
||||
child: bg.AnimatedBackground(
|
||||
vsync: this,
|
||||
behaviour: bg.RandomParticleBehaviour(
|
||||
options: bg.ParticleOptions(
|
||||
baseColor: Theme.of(context).colorScheme.secondary,
|
||||
spawnMinSpeed: 5.0,
|
||||
spawnMaxSpeed: 10.0,
|
||||
minOpacity: .05,
|
||||
maxOpacity: .08,
|
||||
spawnMinRadius: 30.0,
|
||||
spawnMaxRadius: 50.0,
|
||||
particleCount: 20,
|
||||
),
|
||||
),
|
||||
child: ScaleTransition(
|
||||
scale: _revealAnimScale,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: AnimatedBuilder(
|
||||
animation: _revealAnimGrade,
|
||||
builder: (context, child) {
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SlideTransition(
|
||||
position: _revealAnimGrade.drive(Tween(begin: Offset.zero, end: const Offset(0, 0.7))),
|
||||
child: AnimatedScale(
|
||||
scale: hold ? 1.1 : 1.0,
|
||||
curve: Curves.easeOutBack,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: GestureDetector(
|
||||
onLongPressDown: (_) => setState(() => hold = true),
|
||||
onLongPressEnd: (_) => reveal(),
|
||||
onLongPressCancel: reveal,
|
||||
child: ScaleTransition(
|
||||
scale: CurvedAnimation(curve: Curves.easeInOut, parent: _revealAnimGrade.drive(Tween(begin: 1.0, end: 0.8))),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 300,
|
||||
height: 300,
|
||||
child: rive.RiveAnimation.asset(
|
||||
"assets/animations/backpack-2.riv",
|
||||
fit: BoxFit.contain,
|
||||
controllers: [_controller],
|
||||
antialiasing: false,
|
||||
),
|
||||
),
|
||||
SlideTransition(
|
||||
position: _revealAnimParticle.drive(Tween(begin: const Offset(0, 0.3), end: const Offset(0, 0.8))),
|
||||
child: FadeTransition(
|
||||
opacity: _revealAnimParticle,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 32.0, sigmaY: 32.0),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32.0, vertical: 20.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(.3),
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
border: Border.all(color: Colors.black.withOpacity(.3), width: 1.0),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.grade.description != "")
|
||||
Text(
|
||||
widget.grade.description,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 26.0,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
widget.grade.subject.renamedTo ?? widget.grade.subject.name.capital(),
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(.8),
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 24.0,
|
||||
fontStyle: widget.grade.subject.isRenamed ? FontStyle.italic : null),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
"${widget.grade.value.weight}%",
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(.7),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20.0),
|
||||
Icon(
|
||||
SubjectIcon.resolveVariant(subject: widget.grade.subject, context: context),
|
||||
color: Colors.white,
|
||||
size: 82.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 42.0),
|
||||
AnimatedOpacity(
|
||||
opacity: subtitle ? 1.0 : 0.0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: Text(
|
||||
"open_subtitle".i18n,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(.8),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 24.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_revealAnimGrade.value > 0)
|
||||
AnimatedBuilder(
|
||||
animation: _revealAnimParticle,
|
||||
builder: (context, child) {
|
||||
bool shouldPaint = false;
|
||||
if (_revealAnimParticle.status == AnimationStatus.forward || _revealAnimParticle.status == AnimationStatus.reverse) {
|
||||
shouldPaint = true;
|
||||
}
|
||||
return ScaleTransition(
|
||||
scale: _revealAnimGrade,
|
||||
child: FadeTransition(
|
||||
opacity: _revealAnimGrade,
|
||||
child: SlideTransition(
|
||||
position: _revealAnimGrade.drive(Tween(begin: Offset.zero, end: const Offset(0, -0.6))),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SlideTransition(
|
||||
position: _revealAnimGrade.drive(Tween(begin: Offset.zero, end: const Offset(0, -0.9))),
|
||||
child: Text(
|
||||
["legendary", "epic", "rare", "uncommon", "common"][5 - widget.grade.value.value].i18n,
|
||||
style: TextStyle(
|
||||
fontSize: 46.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: gradeColor(context: context, value: widget.grade.value.value),
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: gradeColor(context: context, value: widget.grade.value.value).withOpacity(.5),
|
||||
blurRadius: 24.0,
|
||||
),
|
||||
Shadow(
|
||||
color: gradeColor(context: context, value: widget.grade.value.value).withOpacity(.3),
|
||||
offset: const Offset(-3, -3),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32.0),
|
||||
ScaleTransition(
|
||||
scale: CurvedAnimation(curve: Curves.easeInOutBack, parent: _revealAnimParticle.drive(Tween(begin: 0.6, end: 1.0))),
|
||||
child: CustomPaint(
|
||||
painter: PimpPainter(
|
||||
particle: Sprinkles(),
|
||||
controller: _revealAnimParticle,
|
||||
seed: seed + 1,
|
||||
shouldPaint: shouldPaint,
|
||||
),
|
||||
child: CustomPaint(
|
||||
painter: PimpPainter(
|
||||
particle: Sprinkles(),
|
||||
controller: _revealAnimParticle,
|
||||
seed: seed,
|
||||
shouldPaint: shouldPaint,
|
||||
),
|
||||
child: RotationTransition(
|
||||
turns:
|
||||
CurvedAnimation(curve: Curves.easeInBack, parent: _revealAnimGrade).drive(Tween(begin: 0.95, end: 1.0)),
|
||||
child: GradeValueWidget(
|
||||
widget.grade.value,
|
||||
fill: true,
|
||||
contrast: true,
|
||||
shadow: true,
|
||||
outline: true,
|
||||
size: 100.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PimpPainter extends CustomPainter {
|
||||
PimpPainter({required this.particle, required this.seed, required this.controller, required this.shouldPaint}) : super(repaint: controller);
|
||||
|
||||
final Particle particle;
|
||||
final int seed;
|
||||
final AnimationController controller;
|
||||
final bool shouldPaint;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
if (shouldPaint) {
|
||||
canvas.translate(size.width / 2, size.height / 2);
|
||||
particle.paint(canvas, size, controller.value, seed);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(PimpPainter oldDelegate) => shouldPaint;
|
||||
}
|
||||
|
||||
Color randomColor(int c) {
|
||||
c = c % 5;
|
||||
if (c == 0) return Colors.red.shade300;
|
||||
if (c == 1) return Colors.green.shade300;
|
||||
if (c == 2) return Colors.orange.shade300;
|
||||
if (c == 3) return Colors.blue.shade300;
|
||||
if (c == 4) return Colors.pink.shade300;
|
||||
if (c == 5) return Colors.brown.shade300;
|
||||
return Colors.black;
|
||||
}
|
||||
|
||||
class Sprinkles extends Particle {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size, progress, seed) {
|
||||
Random random = Random(seed);
|
||||
int randomMirrorOffset = random.nextInt(8) + 1;
|
||||
CompositeParticle(children: [
|
||||
Firework(),
|
||||
RectangleMirror.builder(
|
||||
numberOfParticles: 6,
|
||||
particleBuilder: (n) {
|
||||
return AnimatedPositionedParticle(
|
||||
begin: const Offset(0.0, -10.0),
|
||||
end: const Offset(0.0, -60.0),
|
||||
child: FadingRect(width: 5.0, height: 15.0, color: randomColor(n)),
|
||||
);
|
||||
},
|
||||
initialDistance: -pi / randomMirrorOffset),
|
||||
]).paint(canvas, size, progress, seed);
|
||||
}
|
||||
}
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:animated_background/animated_background.dart' as bg;
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/grade.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/home/particle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:rive/rive.dart' as rive;
|
||||
|
||||
import 'new_grades.i18n.dart';
|
||||
|
||||
class SurpriseGrade extends StatefulWidget {
|
||||
const SurpriseGrade(this.grade, {Key? key}) : super(key: key);
|
||||
|
||||
final Grade grade;
|
||||
|
||||
@override
|
||||
State<SurpriseGrade> createState() => _SurpriseGradeState();
|
||||
}
|
||||
|
||||
class _SurpriseGradeState extends State<SurpriseGrade> with TickerProviderStateMixin {
|
||||
late AnimationController _revealAnimFade;
|
||||
late AnimationController _revealAnimScale;
|
||||
late AnimationController _revealAnimGrade;
|
||||
late AnimationController _revealAnimParticle;
|
||||
late rive.RiveAnimationController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_revealAnimFade = AnimationController(vsync: this, duration: const Duration(milliseconds: 500));
|
||||
_revealAnimScale = AnimationController(vsync: this, duration: const Duration(milliseconds: 300));
|
||||
_revealAnimGrade = AnimationController(vsync: this, duration: const Duration(seconds: 1));
|
||||
_revealAnimParticle = AnimationController(vsync: this, duration: const Duration(seconds: 2));
|
||||
_revealAnimScale.animateTo(0.7, duration: Duration.zero);
|
||||
_controller = rive.SimpleAnimation('Timeline 1', autoplay: false);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_revealAnimFade.animateTo(1.0, curve: Curves.easeInOut);
|
||||
Future.delayed(const Duration(milliseconds: 200), () {
|
||||
_revealAnimScale.animateTo(1.0, curve: Curves.easeInOut).then((_) {
|
||||
setState(() => subtitle = true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
seed = Random().nextInt(100000000);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_revealAnimFade.dispose();
|
||||
_revealAnimScale.dispose();
|
||||
_revealAnimGrade.dispose();
|
||||
_revealAnimParticle.dispose();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool hold = false;
|
||||
bool subtitle = false;
|
||||
late int seed;
|
||||
|
||||
void reveal() async {
|
||||
if (!subtitle) {
|
||||
_revealAnimParticle.animateBack(0.0, curve: Curves.fastLinearToSlowEaseIn, duration: const Duration(milliseconds: 300));
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
_revealAnimGrade.animateBack(0.0, curve: Curves.fastLinearToSlowEaseIn);
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
_revealAnimFade.animateBack(0.0, curve: Curves.easeInOut);
|
||||
_revealAnimScale.animateBack(0.0, curve: Curves.easeInOut);
|
||||
if (mounted) Navigator.of(context).pop();
|
||||
return;
|
||||
}
|
||||
subtitle = false;
|
||||
setState(() => hold = false);
|
||||
_controller.isActive = true;
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
if (mounted) _revealAnimGrade.animateTo(1.0, curve: Curves.fastLinearToSlowEaseIn);
|
||||
await Future.delayed(const Duration(milliseconds: 700));
|
||||
if (mounted) await _revealAnimParticle.animateTo(1.0, curve: Curves.fastLinearToSlowEaseIn);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: _revealAnimFade,
|
||||
builder: (context, child) {
|
||||
return FadeTransition(
|
||||
opacity: _revealAnimFade,
|
||||
child: Material(
|
||||
color: Colors.black.withOpacity(.75),
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.secondary.withOpacity(.05),
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: RadialGradient(
|
||||
colors: [Colors.transparent, Colors.black],
|
||||
radius: 1.5,
|
||||
stops: [0.2, 1.0],
|
||||
),
|
||||
),
|
||||
child: bg.AnimatedBackground(
|
||||
vsync: this,
|
||||
behaviour: bg.RandomParticleBehaviour(
|
||||
options: bg.ParticleOptions(
|
||||
baseColor: Theme.of(context).colorScheme.secondary,
|
||||
spawnMinSpeed: 5.0,
|
||||
spawnMaxSpeed: 10.0,
|
||||
minOpacity: .05,
|
||||
maxOpacity: .08,
|
||||
spawnMinRadius: 30.0,
|
||||
spawnMaxRadius: 50.0,
|
||||
particleCount: 20,
|
||||
),
|
||||
),
|
||||
child: ScaleTransition(
|
||||
scale: _revealAnimScale,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: AnimatedBuilder(
|
||||
animation: _revealAnimGrade,
|
||||
builder: (context, child) {
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SlideTransition(
|
||||
position: _revealAnimGrade.drive(Tween(begin: Offset.zero, end: const Offset(0, 0.7))),
|
||||
child: AnimatedScale(
|
||||
scale: hold ? 1.1 : 1.0,
|
||||
curve: Curves.easeOutBack,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: GestureDetector(
|
||||
onLongPressDown: (_) => setState(() => hold = true),
|
||||
onLongPressEnd: (_) => reveal(),
|
||||
onLongPressCancel: reveal,
|
||||
child: ScaleTransition(
|
||||
scale: CurvedAnimation(curve: Curves.easeInOut, parent: _revealAnimGrade.drive(Tween(begin: 1.0, end: 0.8))),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 300,
|
||||
height: 300,
|
||||
child: rive.RiveAnimation.asset(
|
||||
"assets/animations/backpack-2.riv",
|
||||
fit: BoxFit.contain,
|
||||
controllers: [_controller],
|
||||
antialiasing: false,
|
||||
),
|
||||
),
|
||||
SlideTransition(
|
||||
position: _revealAnimParticle.drive(Tween(begin: const Offset(0, 0.3), end: const Offset(0, 0.8))),
|
||||
child: FadeTransition(
|
||||
opacity: _revealAnimParticle,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 32.0, sigmaY: 32.0),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32.0, vertical: 20.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(.3),
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
border: Border.all(color: Colors.black.withOpacity(.3), width: 1.0),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.grade.description != "")
|
||||
Text(
|
||||
widget.grade.description,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 26.0,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
widget.grade.subject.renamedTo ?? widget.grade.subject.name.capital(),
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(.8),
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 24.0,
|
||||
fontStyle: widget.grade.subject.isRenamed ? FontStyle.italic : null),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
"${widget.grade.value.weight}%",
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(.7),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20.0),
|
||||
Icon(
|
||||
SubjectIcon.resolveVariant(subject: widget.grade.subject, context: context),
|
||||
color: Colors.white,
|
||||
size: 82.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 42.0),
|
||||
AnimatedOpacity(
|
||||
opacity: subtitle ? 1.0 : 0.0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: Text(
|
||||
"open_subtitle".i18n,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(.8),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 24.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_revealAnimGrade.value > 0)
|
||||
AnimatedBuilder(
|
||||
animation: _revealAnimParticle,
|
||||
builder: (context, child) {
|
||||
bool shouldPaint = false;
|
||||
if (_revealAnimParticle.status == AnimationStatus.forward || _revealAnimParticle.status == AnimationStatus.reverse) {
|
||||
shouldPaint = true;
|
||||
}
|
||||
return ScaleTransition(
|
||||
scale: _revealAnimGrade,
|
||||
child: FadeTransition(
|
||||
opacity: _revealAnimGrade,
|
||||
child: SlideTransition(
|
||||
position: _revealAnimGrade.drive(Tween(begin: Offset.zero, end: const Offset(0, -0.6))),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SlideTransition(
|
||||
position: _revealAnimGrade.drive(Tween(begin: Offset.zero, end: const Offset(0, -0.9))),
|
||||
child: Text(
|
||||
["legendary", "epic", "rare", "uncommon", "common"][5 - widget.grade.value.value].i18n,
|
||||
style: TextStyle(
|
||||
fontSize: 46.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: gradeColor(context: context, value: widget.grade.value.value),
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: gradeColor(context: context, value: widget.grade.value.value).withOpacity(.5),
|
||||
blurRadius: 24.0,
|
||||
),
|
||||
Shadow(
|
||||
color: gradeColor(context: context, value: widget.grade.value.value).withOpacity(.3),
|
||||
offset: const Offset(-3, -3),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32.0),
|
||||
ScaleTransition(
|
||||
scale: CurvedAnimation(curve: Curves.easeInOutBack, parent: _revealAnimParticle.drive(Tween(begin: 0.6, end: 1.0))),
|
||||
child: CustomPaint(
|
||||
painter: PimpPainter(
|
||||
particle: Sprinkles(),
|
||||
controller: _revealAnimParticle,
|
||||
seed: seed + 1,
|
||||
shouldPaint: shouldPaint,
|
||||
),
|
||||
child: CustomPaint(
|
||||
painter: PimpPainter(
|
||||
particle: Sprinkles(),
|
||||
controller: _revealAnimParticle,
|
||||
seed: seed,
|
||||
shouldPaint: shouldPaint,
|
||||
),
|
||||
child: RotationTransition(
|
||||
turns:
|
||||
CurvedAnimation(curve: Curves.easeInBack, parent: _revealAnimGrade).drive(Tween(begin: 0.95, end: 1.0)),
|
||||
child: GradeValueWidget(
|
||||
widget.grade.value,
|
||||
fill: true,
|
||||
contrast: true,
|
||||
shadow: true,
|
||||
outline: true,
|
||||
size: 100.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PimpPainter extends CustomPainter {
|
||||
PimpPainter({required this.particle, required this.seed, required this.controller, required this.shouldPaint}) : super(repaint: controller);
|
||||
|
||||
final Particle particle;
|
||||
final int seed;
|
||||
final AnimationController controller;
|
||||
final bool shouldPaint;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
if (shouldPaint) {
|
||||
canvas.translate(size.width / 2, size.height / 2);
|
||||
particle.paint(canvas, size, controller.value, seed);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(PimpPainter oldDelegate) => shouldPaint;
|
||||
}
|
||||
|
||||
Color randomColor(int c) {
|
||||
c = c % 5;
|
||||
if (c == 0) return Colors.red.shade300;
|
||||
if (c == 1) return Colors.green.shade300;
|
||||
if (c == 2) return Colors.orange.shade300;
|
||||
if (c == 3) return Colors.blue.shade300;
|
||||
if (c == 4) return Colors.pink.shade300;
|
||||
if (c == 5) return Colors.brown.shade300;
|
||||
return Colors.black;
|
||||
}
|
||||
|
||||
class Sprinkles extends Particle {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size, progress, seed) {
|
||||
Random random = Random(seed);
|
||||
int randomMirrorOffset = random.nextInt(8) + 1;
|
||||
CompositeParticle(children: [
|
||||
Firework(),
|
||||
RectangleMirror.builder(
|
||||
numberOfParticles: 6,
|
||||
particleBuilder: (n) {
|
||||
return AnimatedPositionedParticle(
|
||||
begin: const Offset(0.0, -10.0),
|
||||
end: const Offset(0.0, -60.0),
|
||||
child: FadingRect(width: 5.0, height: 15.0, color: randomColor(n)),
|
||||
);
|
||||
},
|
||||
initialDistance: -pi / randomMirrorOffset),
|
||||
]).paint(canvas, size, progress, seed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +1,89 @@
|
||||
import 'dart:io';
|
||||
import 'package:filcnaplo/helpers/attachment_helper.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/message/image_view.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
import 'package:filcnaplo_kreta_api/models/homework.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'homework_attachment_tile.i18n.dart';
|
||||
|
||||
class HomeworkAttachmentTile extends StatelessWidget {
|
||||
const HomeworkAttachmentTile(this.attachment, {Key? key}) : super(key: key);
|
||||
|
||||
final HomeworkAttachment attachment;
|
||||
|
||||
Widget buildImage(BuildContext context) {
|
||||
return FutureBuilder<String>(
|
||||
future: attachment.download(context),
|
||||
builder: (context, snapshot) {
|
||||
return snapshot.hasData
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: Material(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context, rootNavigator: true).push(MaterialPageRoute(
|
||||
builder: (context) => ImageView(snapshot.data!),
|
||||
));
|
||||
},
|
||||
child: Ink.image(
|
||||
image: FileImage(File(snapshot.data ?? "")),
|
||||
height: 200.0,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary),
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (attachment.isImage) return buildImage(context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
onTap: () {
|
||||
attachment.open(context).then((value) {
|
||||
if (!value) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
||||
context: context,
|
||||
content: Text("Failed to open attachment".i18n),
|
||||
backgroundColor: AppColors.of(context).red,
|
||||
duration: const Duration(seconds: 1),
|
||||
));
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(children: [
|
||||
const Icon(FeatherIcons.paperclip),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Text(attachment.name, maxLines: 2, overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'dart:io';
|
||||
import 'package:filcnaplo/helpers/attachment_helper.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/message/image_view.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
import 'package:filcnaplo_kreta_api/models/homework.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'homework_attachment_tile.i18n.dart';
|
||||
|
||||
class HomeworkAttachmentTile extends StatelessWidget {
|
||||
const HomeworkAttachmentTile(this.attachment, {Key? key}) : super(key: key);
|
||||
|
||||
final HomeworkAttachment attachment;
|
||||
|
||||
Widget buildImage(BuildContext context) {
|
||||
return FutureBuilder<String>(
|
||||
future: attachment.download(context),
|
||||
builder: (context, snapshot) {
|
||||
return snapshot.hasData
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: Material(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context, rootNavigator: true).push(MaterialPageRoute(
|
||||
builder: (context) => ImageView(snapshot.data!),
|
||||
));
|
||||
},
|
||||
child: Ink.image(
|
||||
image: FileImage(File(snapshot.data ?? "")),
|
||||
height: 200.0,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary),
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (attachment.isImage) return buildImage(context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
onTap: () {
|
||||
attachment.open(context).then((value) {
|
||||
if (!value) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
||||
context: context,
|
||||
content: Text("Failed to open attachment".i18n),
|
||||
backgroundColor: AppColors.of(context).red,
|
||||
duration: const Duration(seconds: 1),
|
||||
));
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(children: [
|
||||
const Icon(FeatherIcons.paperclip),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Text(attachment.name, maxLines: 2, overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"Failed to open attachment": "Failed to open attachment",
|
||||
},
|
||||
"hu_hu": {
|
||||
"Failed to open attachment": "Nem sikerült megnyitni a mellékletet",
|
||||
},
|
||||
"de_de": {
|
||||
"Failed to open attachment": "Anhang konnte nicht geöffnet werden",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"Failed to open attachment": "Failed to open attachment",
|
||||
},
|
||||
"hu_hu": {
|
||||
"Failed to open attachment": "Nem sikerült megnyitni a mellékletet",
|
||||
},
|
||||
"de_de": {
|
||||
"Failed to open attachment": "Anhang konnte nicht geöffnet werden",
|
||||
}
|
||||
};
|
||||
|
||||
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,103 +1,103 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/homework.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
class HomeworkTile extends StatelessWidget {
|
||||
const HomeworkTile(this.homework, {Key? key, this.onTap, this.padding, this.censored = false}) : super(key: key);
|
||||
|
||||
final Homework homework;
|
||||
final void Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final bool censored;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: onTap,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
leading: SizedBox(
|
||||
width: 44,
|
||||
height: 44,
|
||||
child: censored
|
||||
? Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.55),
|
||||
borderRadius: BorderRadius.circular(60.0),
|
||||
),
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(top: 2.0),
|
||||
child: Icon(
|
||||
SubjectIcon.resolveVariant(subjectName: homework.subjectName, context: context),
|
||||
size: 28.0,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
),
|
||||
),
|
||||
title: censored
|
||||
? Wrap(
|
||||
children: [
|
||||
Container(
|
||||
width: 160,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.85),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
homework.subjectName.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: censored
|
||||
? Wrap(
|
||||
children: [
|
||||
Container(
|
||||
width: 100,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.45),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
homework.content.escapeHtml().replaceAll('\n', ' '),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: censored
|
||||
? Container(
|
||||
width: 15,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.45),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
FeatherIcons.home,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/homework.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
class HomeworkTile extends StatelessWidget {
|
||||
const HomeworkTile(this.homework, {Key? key, this.onTap, this.padding, this.censored = false}) : super(key: key);
|
||||
|
||||
final Homework homework;
|
||||
final void Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final bool censored;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: onTap,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
leading: SizedBox(
|
||||
width: 44,
|
||||
height: 44,
|
||||
child: censored
|
||||
? Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.55),
|
||||
borderRadius: BorderRadius.circular(60.0),
|
||||
),
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(top: 2.0),
|
||||
child: Icon(
|
||||
SubjectIcon.resolveVariant(subjectName: homework.subjectName, context: context),
|
||||
size: 28.0,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
),
|
||||
),
|
||||
title: censored
|
||||
? Wrap(
|
||||
children: [
|
||||
Container(
|
||||
width: 160,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.85),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
homework.subjectName.capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: censored
|
||||
? Wrap(
|
||||
children: [
|
||||
Container(
|
||||
width: 100,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.45),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
homework.content.escapeHtml().replaceAll('\n', ' '),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: censored
|
||||
? Container(
|
||||
width: 15,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.of(context).text.withOpacity(.45),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
FeatherIcons.home,
|
||||
color: AppColors.of(context).text.withOpacity(.75),
|
||||
),
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,88 +1,88 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/homework.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/sliding_bottom_sheet.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_attachment_tile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
import 'homework_view.i18n.dart';
|
||||
|
||||
class HomeworkView extends StatelessWidget {
|
||||
const HomeworkView(this.homework, {Key? key}) : super(key: key);
|
||||
|
||||
final Homework homework;
|
||||
|
||||
static show(Homework homework, {required BuildContext context}) {
|
||||
showSlidingBottomSheet(context: context, child: HomeworkView(homework));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> attachmentTiles = [];
|
||||
|
||||
for (var attachment in homework.attachments) {
|
||||
attachmentTiles.add(Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4.0),
|
||||
child: HomeworkAttachmentTile(
|
||||
attachment,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
SubjectIcon.resolveVariant(subjectName: homework.subjectName, context: context),
|
||||
size: 36.0,
|
||||
),
|
||||
title: Text(
|
||||
homework.subjectName.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
homework.teacher,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
homework.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Details
|
||||
if (homework.deadline.year != 0) Detail(title: "deadline".i18n, description: homework.deadline.format(context)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 6.0),
|
||||
child: SelectableLinkify(
|
||||
text: homework.content.escapeHtml(),
|
||||
options: const LinkifyOptions(looseUrl: true, removeWww: true),
|
||||
onOpen: (link) {
|
||||
launch(link.url,
|
||||
customTabsOption: CustomTabsOption(
|
||||
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
showPageTitle: true,
|
||||
));
|
||||
},
|
||||
style: const TextStyle(fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
|
||||
// Attachments
|
||||
...attachmentTiles,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/homework.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/sliding_bottom_sheet.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_attachment_tile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
import 'homework_view.i18n.dart';
|
||||
|
||||
class HomeworkView extends StatelessWidget {
|
||||
const HomeworkView(this.homework, {Key? key}) : super(key: key);
|
||||
|
||||
final Homework homework;
|
||||
|
||||
static show(Homework homework, {required BuildContext context}) {
|
||||
showSlidingBottomSheet(context: context, child: HomeworkView(homework));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> attachmentTiles = [];
|
||||
|
||||
for (var attachment in homework.attachments) {
|
||||
attachmentTiles.add(Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4.0),
|
||||
child: HomeworkAttachmentTile(
|
||||
attachment,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
SubjectIcon.resolveVariant(subjectName: homework.subjectName, context: context),
|
||||
size: 36.0,
|
||||
),
|
||||
title: Text(
|
||||
homework.subjectName.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
homework.teacher,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
homework.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Details
|
||||
if (homework.deadline.year != 0) Detail(title: "deadline".i18n, description: homework.deadline.format(context)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 6.0),
|
||||
child: SelectableLinkify(
|
||||
text: homework.content.escapeHtml(),
|
||||
options: const LinkifyOptions(looseUrl: true, removeWww: true),
|
||||
onOpen: (link) {
|
||||
launch(link.url,
|
||||
customTabsOption: CustomTabsOption(
|
||||
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
showPageTitle: true,
|
||||
));
|
||||
},
|
||||
style: const TextStyle(fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
|
||||
// Attachments
|
||||
...attachmentTiles,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"deadline": "Deadline",
|
||||
},
|
||||
"hu_hu": {
|
||||
"deadline": "Határidő",
|
||||
},
|
||||
"de_de": {
|
||||
"deadline": "Termin",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"deadline": "Deadline",
|
||||
},
|
||||
"hu_hu": {
|
||||
"deadline": "Határidő",
|
||||
},
|
||||
"de_de": {
|
||||
"deadline": "Termin",
|
||||
}
|
||||
};
|
||||
|
||||
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,18 +1,18 @@
|
||||
import 'package:filcnaplo_kreta_api/models/homework.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HomeworkViewable extends StatelessWidget {
|
||||
const HomeworkViewable(this.homework, {Key? key}) : super(key: key);
|
||||
|
||||
final Homework homework;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return HomeworkTile(
|
||||
homework,
|
||||
onTap: () => HomeworkView.show(homework, context: context),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/homework.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HomeworkViewable extends StatelessWidget {
|
||||
const HomeworkViewable(this.homework, {Key? key}) : super(key: key);
|
||||
|
||||
final Homework homework;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return HomeworkTile(
|
||||
homework,
|
||||
onTap: () => HomeworkView.show(homework, context: context),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +1,75 @@
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'changed_lesson_tile.i18n.dart';
|
||||
|
||||
class ChangedLessonTile extends StatelessWidget {
|
||||
const ChangedLessonTile(this.lesson, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Lesson lesson;
|
||||
final void Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String lessonIndexTrailing = "";
|
||||
|
||||
// Only put a trailing . if its a digit
|
||||
if (RegExp(r'\d').hasMatch(lesson.lessonIndex)) lessonIndexTrailing = ".";
|
||||
|
||||
Color accent = Theme.of(context).colorScheme.secondary;
|
||||
|
||||
if (lesson.substituteTeacher != "") {
|
||||
accent = AppColors.of(context).yellow;
|
||||
}
|
||||
|
||||
if (lesson.status?.name == "Elmaradt") {
|
||||
accent = AppColors.of(context).red;
|
||||
}
|
||||
|
||||
return Material(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: onTap,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
leading: SizedBox(
|
||||
width: 44.0,
|
||||
height: 44.0,
|
||||
child: Center(
|
||||
child: Text(
|
||||
lesson.lessonIndex + lessonIndexTrailing,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 30.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: accent,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
lesson.substituteTeacher != "" ? "substituted".i18n : "cancelled".i18n,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
lesson.subject.renamedTo ?? lesson.subject.name.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
trailing: const Icon(FeatherIcons.arrowRight),
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'changed_lesson_tile.i18n.dart';
|
||||
|
||||
class ChangedLessonTile extends StatelessWidget {
|
||||
const ChangedLessonTile(this.lesson, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Lesson lesson;
|
||||
final void Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String lessonIndexTrailing = "";
|
||||
|
||||
// Only put a trailing . if its a digit
|
||||
if (RegExp(r'\d').hasMatch(lesson.lessonIndex)) lessonIndexTrailing = ".";
|
||||
|
||||
Color accent = Theme.of(context).colorScheme.secondary;
|
||||
|
||||
if (lesson.substituteTeacher != "") {
|
||||
accent = AppColors.of(context).yellow;
|
||||
}
|
||||
|
||||
if (lesson.status?.name == "Elmaradt") {
|
||||
accent = AppColors.of(context).red;
|
||||
}
|
||||
|
||||
return Material(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: onTap,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
leading: SizedBox(
|
||||
width: 44.0,
|
||||
height: 44.0,
|
||||
child: Center(
|
||||
child: Text(
|
||||
lesson.lessonIndex + lessonIndexTrailing,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 30.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: accent,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
lesson.substituteTeacher != "" ? "substituted".i18n : "cancelled".i18n,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
lesson.subject.renamedTo ?? lesson.subject.name.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
trailing: const Icon(FeatherIcons.arrowRight),
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"cancelled": "Cancelled lesson",
|
||||
"substituted": "Substituted lesson",
|
||||
},
|
||||
"hu_hu": {
|
||||
"cancelled": "Elmaradó óra",
|
||||
"substituted": "Helyettesített óra",
|
||||
},
|
||||
"de_de": {
|
||||
"cancelled": "Abgesagte Stunde",
|
||||
"substituted": "Vertretene Stunden",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"cancelled": "Cancelled lesson",
|
||||
"substituted": "Substituted lesson",
|
||||
},
|
||||
"hu_hu": {
|
||||
"cancelled": "Elmaradó óra",
|
||||
"substituted": "Helyettesített óra",
|
||||
},
|
||||
"de_de": {
|
||||
"cancelled": "Abgesagte Stunde",
|
||||
"substituted": "Vertretene Stunden",
|
||||
}
|
||||
};
|
||||
|
||||
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,18 +1,18 @@
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/lesson/changed_lesson_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ChangedLessonViewable extends StatelessWidget {
|
||||
const ChangedLessonViewable(this.lesson, {Key? key}) : super(key: key);
|
||||
|
||||
final Lesson lesson;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangedLessonTile(
|
||||
lesson,
|
||||
onTap: () => TimetablePage.jump(context, lesson: lesson),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/lesson/changed_lesson_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ChangedLessonViewable extends StatelessWidget {
|
||||
const ChangedLessonViewable(this.lesson, {Key? key}) : super(key: key);
|
||||
|
||||
final Lesson lesson;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangedLessonTile(
|
||||
lesson,
|
||||
onTap: () => TimetablePage.jump(context, lesson: lesson),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +1,80 @@
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'lesson_view.i18n.dart';
|
||||
|
||||
class LessonView extends StatelessWidget {
|
||||
const LessonView(this.lesson, {Key? key}) : super(key: key);
|
||||
|
||||
final Lesson lesson;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color accent = Theme.of(context).colorScheme.secondary;
|
||||
String lessonIndexTrailing = "";
|
||||
|
||||
if (RegExp(r'\d').hasMatch(lesson.lessonIndex)) lessonIndexTrailing = ".";
|
||||
|
||||
if (lesson.substituteTeacher != "") {
|
||||
accent = AppColors.of(context).yellow;
|
||||
}
|
||||
|
||||
if (lesson.status?.name == "Elmaradt") {
|
||||
accent = AppColors.of(context).red;
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
ListTile(
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Text(
|
||||
lesson.lessonIndex + lessonIndexTrailing,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 38.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: accent,
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
lesson.subject.renamedTo ?? lesson.subject.name.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
lesson.substituteTeacher == "" ? lesson.teacher : lesson.substituteTeacher,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
lesson.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Details
|
||||
if (lesson.room != "") Detail(title: "Room".i18n, 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),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static show(Lesson lesson, {required BuildContext context}) {
|
||||
showBottomCard(context: context, child: LessonView(lesson));
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/detail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'lesson_view.i18n.dart';
|
||||
|
||||
class LessonView extends StatelessWidget {
|
||||
const LessonView(this.lesson, {Key? key}) : super(key: key);
|
||||
|
||||
final Lesson lesson;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color accent = Theme.of(context).colorScheme.secondary;
|
||||
String lessonIndexTrailing = "";
|
||||
|
||||
if (RegExp(r'\d').hasMatch(lesson.lessonIndex)) lessonIndexTrailing = ".";
|
||||
|
||||
if (lesson.substituteTeacher != "") {
|
||||
accent = AppColors.of(context).yellow;
|
||||
}
|
||||
|
||||
if (lesson.status?.name == "Elmaradt") {
|
||||
accent = AppColors.of(context).red;
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
ListTile(
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Text(
|
||||
lesson.lessonIndex + lessonIndexTrailing,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 38.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: accent,
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
lesson.subject.renamedTo ?? lesson.subject.name.capital(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
lesson.substituteTeacher == "" ? lesson.teacher : lesson.substituteTeacher,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
lesson.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Details
|
||||
if (lesson.room != "") Detail(title: "Room".i18n, 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),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static show(Lesson lesson, {required BuildContext context}) {
|
||||
showBottomCard(context: context, child: LessonView(lesson));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"Room": "Room",
|
||||
"Description": "Description",
|
||||
"Lesson Number": "Lesson Number",
|
||||
"Group": "Group",
|
||||
},
|
||||
"hu_hu": {
|
||||
"Room": "Terem",
|
||||
"Description": "Leírás",
|
||||
"Lesson Number": "Éves óraszám",
|
||||
"Group": "Csoport",
|
||||
},
|
||||
"de_de": {
|
||||
"Room": "Raum",
|
||||
"Description": "Bezeichnung",
|
||||
"Lesson Number": "Ordinalzahl",
|
||||
"Group": "Gruppe",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"Room": "Room",
|
||||
"Description": "Description",
|
||||
"Lesson Number": "Lesson Number",
|
||||
"Group": "Group",
|
||||
},
|
||||
"hu_hu": {
|
||||
"Room": "Terem",
|
||||
"Description": "Leírás",
|
||||
"Lesson Number": "Éves óraszám",
|
||||
"Group": "Csoport",
|
||||
},
|
||||
"de_de": {
|
||||
"Room": "Raum",
|
||||
"Description": "Bezeichnung",
|
||||
"Lesson Number": "Ordinalzahl",
|
||||
"Group": "Gruppe",
|
||||
}
|
||||
};
|
||||
|
||||
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,25 +1,25 @@
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/viewable.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/card_handle.dart';
|
||||
import 'package:filcnaplo/ui/widgets/lesson/lesson_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/lesson/lesson_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LessonViewable extends StatelessWidget {
|
||||
const LessonViewable(this.lesson, {Key? key, this.swapDesc = false}) : super(key: key);
|
||||
|
||||
final Lesson lesson;
|
||||
final bool swapDesc;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tile = LessonTile(lesson, swapDesc: swapDesc);
|
||||
|
||||
if (lesson.subject.id == '' || tile.lesson.isEmpty) return tile;
|
||||
|
||||
return Viewable(
|
||||
tile: tile,
|
||||
view: CardHandle(child: LessonView(lesson)),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/viewable.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/card_handle.dart';
|
||||
import 'package:filcnaplo/ui/widgets/lesson/lesson_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/lesson/lesson_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LessonViewable extends StatelessWidget {
|
||||
const LessonViewable(this.lesson, {Key? key, this.swapDesc = false}) : super(key: key);
|
||||
|
||||
final Lesson lesson;
|
||||
final bool swapDesc;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tile = LessonTile(lesson, swapDesc: swapDesc);
|
||||
|
||||
if (lesson.subject.id == '' || tile.lesson.isEmpty) return tile;
|
||||
|
||||
return Viewable(
|
||||
tile: tile,
|
||||
view: CardHandle(child: LessonView(lesson)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +1,83 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:filcnaplo_kreta_api/models/attachment.dart';
|
||||
import 'package:filcnaplo/helpers/attachment_helper.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/message/image_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
class AttachmentTile extends StatelessWidget {
|
||||
const AttachmentTile(this.attachment, {Key? key}) : super(key: key);
|
||||
|
||||
final Attachment attachment;
|
||||
|
||||
Widget buildImage(BuildContext context) {
|
||||
return FutureBuilder<String>(
|
||||
future: attachment.download(context),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: Material(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return ImageView(snapshot.data!);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Ink.image(
|
||||
image: FileImage(File(snapshot.data ?? "")),
|
||||
height: 200.0,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary),
|
||||
));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (attachment.isImage) return buildImage(context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
onTap: () {
|
||||
attachment.open(context);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(children: [
|
||||
const Icon(FeatherIcons.paperclip),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Text(attachment.name, maxLines: 2, overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:filcnaplo_kreta_api/models/attachment.dart';
|
||||
import 'package:filcnaplo/helpers/attachment_helper.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/message/image_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
|
||||
class AttachmentTile extends StatelessWidget {
|
||||
const AttachmentTile(this.attachment, {Key? key}) : super(key: key);
|
||||
|
||||
final Attachment attachment;
|
||||
|
||||
Widget buildImage(BuildContext context) {
|
||||
return FutureBuilder<String>(
|
||||
future: attachment.download(context),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: Material(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return ImageView(snapshot.data!);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Ink.image(
|
||||
image: FileImage(File(snapshot.data ?? "")),
|
||||
height: 200.0,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary),
|
||||
));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (attachment.isImage) return buildImage(context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
onTap: () {
|
||||
attachment.open(context);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(children: [
|
||||
const Icon(FeatherIcons.paperclip),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Text(attachment.name, maxLines: 2, overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:filcnaplo/helpers/share_helper.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
|
||||
class ImageView extends StatelessWidget {
|
||||
const ImageView(this.path, {Key? key}) : super(key: key);
|
||||
|
||||
final String path;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
child: SafeArea(
|
||||
minimum: const EdgeInsets.only(top: 24.0),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: BackButton(color: AppColors.of(context).text),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: IconButton(
|
||||
onPressed: () => ShareHelper.shareFile(path),
|
||||
icon: Icon(FeatherIcons.share2, color: AppColors.of(context).text),
|
||||
splashRadius: 24.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: PhotoView(
|
||||
imageProvider: FileImage(File(path)),
|
||||
maxScale: 4.0,
|
||||
minScale: PhotoViewComputedScale.contained,
|
||||
backgroundDecoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:filcnaplo/helpers/share_helper.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
|
||||
class ImageView extends StatelessWidget {
|
||||
const ImageView(this.path, {Key? key}) : super(key: key);
|
||||
|
||||
final String path;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
child: SafeArea(
|
||||
minimum: const EdgeInsets.only(top: 24.0),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: BackButton(color: AppColors.of(context).text),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: IconButton(
|
||||
onPressed: () => ShareHelper.shareFile(path),
|
||||
icon: Icon(FeatherIcons.share2, color: AppColors.of(context).text),
|
||||
splashRadius: 24.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: PhotoView(
|
||||
imageProvider: FileImage(File(path)),
|
||||
maxScale: 4.0,
|
||||
minScale: PhotoViewComputedScale.contained,
|
||||
backgroundDecoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/message.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/message/message_view_tile.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MessageView extends StatefulWidget {
|
||||
const MessageView(this.messages, {Key? key}) : super(key: key);
|
||||
|
||||
final List<Message> messages;
|
||||
|
||||
static show(List<Message> messages, {required BuildContext context}) =>
|
||||
Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute(builder: (context) => MessageView(messages)));
|
||||
|
||||
@override
|
||||
_MessageViewState createState() => _MessageViewState();
|
||||
}
|
||||
|
||||
class _MessageViewState extends State<MessageView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leadingWidth: 64.0,
|
||||
leading: BackButton(color: AppColors.of(context).text),
|
||||
elevation: 0,
|
||||
actions: const [
|
||||
// Padding(
|
||||
// padding: EdgeInsets.only(right: 8.0),
|
||||
// child: IconButton(
|
||||
// onPressed: () {},
|
||||
// icon: Icon(FeatherIcons.archive, color: AppColors.of(context).text),
|
||||
// splashRadius: 32.0,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: widget.messages.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: MessageViewTile(widget.messages[index]),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/message.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/message/message_view_tile.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MessageView extends StatefulWidget {
|
||||
const MessageView(this.messages, {Key? key}) : super(key: key);
|
||||
|
||||
final List<Message> messages;
|
||||
|
||||
static show(List<Message> messages, {required BuildContext context}) =>
|
||||
Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute(builder: (context) => MessageView(messages)));
|
||||
|
||||
@override
|
||||
_MessageViewState createState() => _MessageViewState();
|
||||
}
|
||||
|
||||
class _MessageViewState extends State<MessageView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leadingWidth: 64.0,
|
||||
leading: BackButton(color: AppColors.of(context).text),
|
||||
elevation: 0,
|
||||
actions: const [
|
||||
// Padding(
|
||||
// padding: EdgeInsets.only(right: 8.0),
|
||||
// child: IconButton(
|
||||
// onPressed: () {},
|
||||
// icon: Icon(FeatherIcons.archive, color: AppColors.of(context).text),
|
||||
// splashRadius: 32.0,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: widget.messages.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: MessageViewTile(widget.messages[index]),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,122 +1,122 @@
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo/utils/color.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/message.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/message/attachment_tile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
|
||||
// import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'message_view_tile.i18n.dart';
|
||||
|
||||
class MessageViewTile extends StatelessWidget {
|
||||
const MessageViewTile(this.message, {Key? key}) : super(key: key);
|
||||
|
||||
final Message message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
UserProvider user = Provider.of<UserProvider>(context, listen: false);
|
||||
String recipientLabel = "";
|
||||
|
||||
if (message.recipients.any((r) => r.name == user.student?.name)) recipientLabel = "me".i18n;
|
||||
|
||||
if (recipientLabel != "" && message.recipients.length > 1) {
|
||||
recipientLabel += " +";
|
||||
recipientLabel += message.recipients.where((r) => r.name != user.student?.name).length.toString();
|
||||
}
|
||||
|
||||
if (recipientLabel == "") {
|
||||
// note: convertint to set to remove duplicates
|
||||
recipientLabel += message.recipients.map((r) => r.name).toSet().join(", ");
|
||||
}
|
||||
|
||||
List<Widget> attachments = [];
|
||||
for (var a in message.attachments) {
|
||||
attachments.add(AttachmentTile(a));
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Subject
|
||||
Text(
|
||||
message.subject,
|
||||
softWrap: true,
|
||||
maxLines: 10,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 24.0,
|
||||
),
|
||||
),
|
||||
|
||||
// Author
|
||||
ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: ProfileImage(
|
||||
name: message.author,
|
||||
backgroundColor: ColorUtils.stringToColor(message.author),
|
||||
),
|
||||
title: Text(
|
||||
message.author,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
),
|
||||
subtitle: Text(
|
||||
"to".i18n + " " + recipientLabel,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
// IconButton(
|
||||
// onPressed: () {},
|
||||
// icon: Icon(FeatherIcons.cornerUpLeft, color: AppColors.of(context).text),
|
||||
// splashRadius: 24.0,
|
||||
// padding: EdgeInsets.zero,
|
||||
// visualDensity: VisualDensity.compact,
|
||||
// ),
|
||||
// IconButton(
|
||||
// onPressed: () {},
|
||||
// icon: Icon(FeatherIcons.share2, color: AppColors.of(context).text),
|
||||
// splashRadius: 24.0,
|
||||
// padding: EdgeInsets.zero,
|
||||
// visualDensity: VisualDensity.compact,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Content
|
||||
Panel(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: SelectableLinkify(
|
||||
text: message.content.escapeHtml(),
|
||||
options: const LinkifyOptions(looseUrl: true, removeWww: true),
|
||||
onOpen: (link) {
|
||||
launch(link.url,
|
||||
customTabsOption: CustomTabsOption(
|
||||
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
showPageTitle: true,
|
||||
));
|
||||
},
|
||||
style: const TextStyle(fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
|
||||
// Attachments
|
||||
...attachments,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/api/providers/user_provider.dart';
|
||||
import 'package:filcnaplo/utils/color.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/message.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/message/attachment_tile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
|
||||
// import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'message_view_tile.i18n.dart';
|
||||
|
||||
class MessageViewTile extends StatelessWidget {
|
||||
const MessageViewTile(this.message, {Key? key}) : super(key: key);
|
||||
|
||||
final Message message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
UserProvider user = Provider.of<UserProvider>(context, listen: false);
|
||||
String recipientLabel = "";
|
||||
|
||||
if (message.recipients.any((r) => r.name == user.student?.name)) recipientLabel = "me".i18n;
|
||||
|
||||
if (recipientLabel != "" && message.recipients.length > 1) {
|
||||
recipientLabel += " +";
|
||||
recipientLabel += message.recipients.where((r) => r.name != user.student?.name).length.toString();
|
||||
}
|
||||
|
||||
if (recipientLabel == "") {
|
||||
// note: convertint to set to remove duplicates
|
||||
recipientLabel += message.recipients.map((r) => r.name).toSet().join(", ");
|
||||
}
|
||||
|
||||
List<Widget> attachments = [];
|
||||
for (var a in message.attachments) {
|
||||
attachments.add(AttachmentTile(a));
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Subject
|
||||
Text(
|
||||
message.subject,
|
||||
softWrap: true,
|
||||
maxLines: 10,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 24.0,
|
||||
),
|
||||
),
|
||||
|
||||
// Author
|
||||
ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: ProfileImage(
|
||||
name: message.author,
|
||||
backgroundColor: ColorUtils.stringToColor(message.author),
|
||||
),
|
||||
title: Text(
|
||||
message.author,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
),
|
||||
subtitle: Text(
|
||||
"to".i18n + " " + recipientLabel,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
// IconButton(
|
||||
// onPressed: () {},
|
||||
// icon: Icon(FeatherIcons.cornerUpLeft, color: AppColors.of(context).text),
|
||||
// splashRadius: 24.0,
|
||||
// padding: EdgeInsets.zero,
|
||||
// visualDensity: VisualDensity.compact,
|
||||
// ),
|
||||
// IconButton(
|
||||
// onPressed: () {},
|
||||
// icon: Icon(FeatherIcons.share2, color: AppColors.of(context).text),
|
||||
// splashRadius: 24.0,
|
||||
// padding: EdgeInsets.zero,
|
||||
// visualDensity: VisualDensity.compact,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Content
|
||||
Panel(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: SelectableLinkify(
|
||||
text: message.content.escapeHtml(),
|
||||
options: const LinkifyOptions(looseUrl: true, removeWww: true),
|
||||
onOpen: (link) {
|
||||
launch(link.url,
|
||||
customTabsOption: CustomTabsOption(
|
||||
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
showPageTitle: true,
|
||||
));
|
||||
},
|
||||
style: const TextStyle(fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
|
||||
// Attachments
|
||||
...attachments,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"me": "me",
|
||||
"to": "to",
|
||||
},
|
||||
"hu_hu": {
|
||||
"me": "én",
|
||||
"to": "Címzett:",
|
||||
},
|
||||
"de_de": {
|
||||
"me": "mich",
|
||||
"to": "zu",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"me": "me",
|
||||
"to": "to",
|
||||
},
|
||||
"hu_hu": {
|
||||
"me": "én",
|
||||
"to": "Címzett:",
|
||||
},
|
||||
"de_de": {
|
||||
"me": "mich",
|
||||
"to": "zu",
|
||||
}
|
||||
};
|
||||
|
||||
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,32 +1,32 @@
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/message.dart';
|
||||
import 'package:filcnaplo/ui/widgets/message/message_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/message/message_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MessageViewable extends StatelessWidget {
|
||||
const MessageViewable(this.message, {Key? key}) : super(key: key);
|
||||
|
||||
final Message message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OpenContainer(
|
||||
openBuilder: (context, _) {
|
||||
return MessageView([message]);
|
||||
},
|
||||
closedBuilder: (context, VoidCallback openContainer) {
|
||||
return MessageTile(message);
|
||||
},
|
||||
closedElevation: 0,
|
||||
openShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
closedShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
middleColor: Theme.of(context).colorScheme.background,
|
||||
openColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
closedColor: Theme.of(context).colorScheme.background,
|
||||
transitionType: ContainerTransitionType.fadeThrough,
|
||||
transitionDuration: const Duration(milliseconds: 400),
|
||||
useRootNavigator: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/message.dart';
|
||||
import 'package:filcnaplo/ui/widgets/message/message_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/message/message_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MessageViewable extends StatelessWidget {
|
||||
const MessageViewable(this.message, {Key? key}) : super(key: key);
|
||||
|
||||
final Message message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return OpenContainer(
|
||||
openBuilder: (context, _) {
|
||||
return MessageView([message]);
|
||||
},
|
||||
closedBuilder: (context, VoidCallback openContainer) {
|
||||
return MessageTile(message);
|
||||
},
|
||||
closedElevation: 0,
|
||||
openShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
closedShape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
middleColor: Theme.of(context).colorScheme.background,
|
||||
openColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
closedColor: Theme.of(context).colorScheme.background,
|
||||
transitionType: ContainerTransitionType.fadeThrough,
|
||||
transitionDuration: const Duration(milliseconds: 400),
|
||||
useRootNavigator: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
import 'package:filcnaplo_kreta_api/models/note.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'miss_tile.i18n.dart';
|
||||
|
||||
class MissTile extends StatelessWidget {
|
||||
const MissTile(this.note, {Key? key}) : super(key: key);
|
||||
|
||||
final Note note;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: Icon(_missIcon(), color: Theme.of(context).colorScheme.secondary, size: 36.0),
|
||||
visualDensity: VisualDensity.compact,
|
||||
title: Text(
|
||||
_missName(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
note.content.split("órán nem volt")[0].capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
IconData _missIcon() {
|
||||
if (note.type?.name == "HaziFeladatHiany") {
|
||||
return FeatherIcons.home;
|
||||
} else if (note.type?.name == "Felszereleshiany") {
|
||||
return FeatherIcons.book;
|
||||
}
|
||||
return FeatherIcons.slash;
|
||||
}
|
||||
|
||||
String _missName() {
|
||||
if (note.type?.name == "HaziFeladatHiany") {
|
||||
return "Missing homework".i18n;
|
||||
} else if (note.type?.name == "Felszereleshiany") {
|
||||
return "Missing equipment".i18n;
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/note.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'miss_tile.i18n.dart';
|
||||
|
||||
class MissTile extends StatelessWidget {
|
||||
const MissTile(this.note, {Key? key}) : super(key: key);
|
||||
|
||||
final Note note;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: Icon(_missIcon(), color: Theme.of(context).colorScheme.secondary, size: 36.0),
|
||||
visualDensity: VisualDensity.compact,
|
||||
title: Text(
|
||||
_missName(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
note.content.split("órán nem volt")[0].capital(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
IconData _missIcon() {
|
||||
if (note.type?.name == "HaziFeladatHiany") {
|
||||
return FeatherIcons.home;
|
||||
} else if (note.type?.name == "Felszereleshiany") {
|
||||
return FeatherIcons.book;
|
||||
}
|
||||
return FeatherIcons.slash;
|
||||
}
|
||||
|
||||
String _missName() {
|
||||
if (note.type?.name == "HaziFeladatHiany") {
|
||||
return "Missing homework".i18n;
|
||||
} else if (note.type?.name == "Felszereleshiany") {
|
||||
return "Missing equipment".i18n;
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"Missing homework": "Missing homework",
|
||||
"Missing equipment": "Missing equipment",
|
||||
},
|
||||
"hu_hu": {
|
||||
"Missing homework": "Házi feladat hiány",
|
||||
"Missing equipment": "Felszerelés Hiány",
|
||||
},
|
||||
"de_de": {
|
||||
"Missing homework": "Fehlende Hausaufgaben",
|
||||
"Missing equipment": "Fehlende Ausrüstung",
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"Missing homework": "Missing homework",
|
||||
"Missing equipment": "Missing equipment",
|
||||
},
|
||||
"hu_hu": {
|
||||
"Missing homework": "Házi feladat hiány",
|
||||
"Missing equipment": "Felszerelés Hiány",
|
||||
},
|
||||
"de_de": {
|
||||
"Missing homework": "Fehlende Hausaufgaben",
|
||||
"Missing equipment": "Fehlende Ausrüstung",
|
||||
}
|
||||
};
|
||||
|
||||
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,35 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'missed_exam_tile.i18n.dart';
|
||||
|
||||
class MissedExamTile extends StatelessWidget {
|
||||
const MissedExamTile(this.missedExams, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final List<Lesson> missedExams;
|
||||
final Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: PanelButton(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6),
|
||||
leading: SizedBox(
|
||||
width: 36,
|
||||
height: 36,
|
||||
child: Icon(
|
||||
FeatherIcons.slash,
|
||||
color: AppColors.of(context).red.withOpacity(.75),
|
||||
size: 28.0,
|
||||
)),
|
||||
title: Text("missed_exams".plural(missedExams.length).fill([missedExams.length])),
|
||||
trailing: const Icon(FeatherIcons.arrowRight),
|
||||
onPressed: onTap,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'missed_exam_tile.i18n.dart';
|
||||
|
||||
class MissedExamTile extends StatelessWidget {
|
||||
const MissedExamTile(this.missedExams, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final List<Lesson> missedExams;
|
||||
final Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: PanelButton(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6),
|
||||
leading: SizedBox(
|
||||
width: 36,
|
||||
height: 36,
|
||||
child: Icon(
|
||||
FeatherIcons.slash,
|
||||
color: AppColors.of(context).red.withOpacity(.75),
|
||||
size: 28.0,
|
||||
)),
|
||||
title: Text("missed_exams".plural(missedExams.length).fill([missedExams.length])),
|
||||
trailing: const Icon(FeatherIcons.arrowRight),
|
||||
onPressed: onTap,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"goodmorning": "Good morning, %s!",
|
||||
"goodafternoon": "Good afternoon, %s!",
|
||||
"goodevening": "Good evening, %s!",
|
||||
"goodrest": "⛱️ Have a nice holiday, %s!",
|
||||
"happybirthday": "🎂 Happy birthday, %s!",
|
||||
"merryxmas": "🎄 Merry Christmas, %s!",
|
||||
"happynewyear": "🎉 Happy New Year, %s!",
|
||||
"empty": "Nothing to see here.",
|
||||
"All": "All",
|
||||
"Grades": "Grades",
|
||||
"Messages": "Messages",
|
||||
"Absences": "Absences",
|
||||
"update_available": "Update Available",
|
||||
"missed_exams": "You missed %s exams this week.".one("You missed an exam this week."),
|
||||
"missed_exam_contact": "Contact %s, to resolve it!",
|
||||
},
|
||||
"hu_hu": {
|
||||
"goodmorning": "Jó reggelt, %s!",
|
||||
"goodafternoon": "Szép napot, %s!",
|
||||
"goodevening": "Szép estét, %s!",
|
||||
"goodrest": "⛱️ Jó szünetet, %s!",
|
||||
"happybirthday": "🎂 Boldog születésnapot, %s!",
|
||||
"merryxmas": "🎄 Boldog Karácsonyt, %s!",
|
||||
"happynewyear": "🎉 Boldog új évet, %s!",
|
||||
"empty": "Nincs itt semmi látnivaló.",
|
||||
"All": "Összes",
|
||||
"Grades": "Jegyek",
|
||||
"Messages": "Üzenetek",
|
||||
"Absences": "Hiányok",
|
||||
"update_available": "Frissítés elérhető",
|
||||
"missed_exams": "Ezen a héten hiányoztál %s dolgozatról.".one("Ezen a héten hiányoztál egy dolgozatról."),
|
||||
"missed_exam_contact": "Keresd %s-t, ha pótolni szeretnéd!",
|
||||
},
|
||||
"de_de": {
|
||||
"goodmorning": "Guten morgen, %s!",
|
||||
"goodafternoon": "Guten Tag, %s!",
|
||||
"goodevening": "Guten Abend, %s!",
|
||||
"goodrest": "⛱️ Schöne Ferien, %s!",
|
||||
"happybirthday": "🎂 Alles Gute zum Geburtstag, %s!",
|
||||
"merryxmas": "🎄 Frohe Weihnachten, %s!",
|
||||
"happynewyear": "🎉 Frohes neues Jahr, %s!",
|
||||
"empty": "Hier gibt es nichts zu sehen.",
|
||||
"All": "Alles",
|
||||
"Grades": "Noten",
|
||||
"Messages": "Nachrichten",
|
||||
"Absences": "Fehlen",
|
||||
"update_available": "Update verfügbar",
|
||||
"missed_exams": "Diese Woche haben Sie %s Prüfungen verpasst.".one("Diese Woche haben Sie eine Prüfung verpasst."),
|
||||
"missed_exam_contact": "Wenden Sie sich an %s, um sie zu erneuern!",
|
||||
},
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"goodmorning": "Good morning, %s!",
|
||||
"goodafternoon": "Good afternoon, %s!",
|
||||
"goodevening": "Good evening, %s!",
|
||||
"goodrest": "⛱️ Have a nice holiday, %s!",
|
||||
"happybirthday": "🎂 Happy birthday, %s!",
|
||||
"merryxmas": "🎄 Merry Christmas, %s!",
|
||||
"happynewyear": "🎉 Happy New Year, %s!",
|
||||
"empty": "Nothing to see here.",
|
||||
"All": "All",
|
||||
"Grades": "Grades",
|
||||
"Messages": "Messages",
|
||||
"Absences": "Absences",
|
||||
"update_available": "Update Available",
|
||||
"missed_exams": "You missed %s exams this week.".one("You missed an exam this week."),
|
||||
"missed_exam_contact": "Contact %s, to resolve it!",
|
||||
},
|
||||
"hu_hu": {
|
||||
"goodmorning": "Jó reggelt, %s!",
|
||||
"goodafternoon": "Szép napot, %s!",
|
||||
"goodevening": "Szép estét, %s!",
|
||||
"goodrest": "⛱️ Jó szünetet, %s!",
|
||||
"happybirthday": "🎂 Boldog születésnapot, %s!",
|
||||
"merryxmas": "🎄 Boldog Karácsonyt, %s!",
|
||||
"happynewyear": "🎉 Boldog új évet, %s!",
|
||||
"empty": "Nincs itt semmi látnivaló.",
|
||||
"All": "Összes",
|
||||
"Grades": "Jegyek",
|
||||
"Messages": "Üzenetek",
|
||||
"Absences": "Hiányok",
|
||||
"update_available": "Frissítés elérhető",
|
||||
"missed_exams": "Ezen a héten hiányoztál %s dolgozatról.".one("Ezen a héten hiányoztál egy dolgozatról."),
|
||||
"missed_exam_contact": "Keresd %s-t, ha pótolni szeretnéd!",
|
||||
},
|
||||
"de_de": {
|
||||
"goodmorning": "Guten morgen, %s!",
|
||||
"goodafternoon": "Guten Tag, %s!",
|
||||
"goodevening": "Guten Abend, %s!",
|
||||
"goodrest": "⛱️ Schöne Ferien, %s!",
|
||||
"happybirthday": "🎂 Alles Gute zum Geburtstag, %s!",
|
||||
"merryxmas": "🎄 Frohe Weihnachten, %s!",
|
||||
"happynewyear": "🎉 Frohes neues Jahr, %s!",
|
||||
"empty": "Hier gibt es nichts zu sehen.",
|
||||
"All": "Alles",
|
||||
"Grades": "Noten",
|
||||
"Messages": "Nachrichten",
|
||||
"Absences": "Fehlen",
|
||||
"update_available": "Update verfügbar",
|
||||
"missed_exams": "Diese Woche haben Sie %s Prüfungen verpasst.".one("Diese Woche haben Sie eine Prüfung verpasst."),
|
||||
"missed_exam_contact": "Wenden Sie sich an %s, um sie zu erneuern!",
|
||||
},
|
||||
};
|
||||
|
||||
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,61 +1,61 @@
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'missed_exam_tile.i18n.dart';
|
||||
|
||||
class MissedExamView extends StatelessWidget {
|
||||
const MissedExamView(this.missedExams, {Key? key}) : super(key: key);
|
||||
|
||||
final List<Lesson> missedExams;
|
||||
|
||||
static show(List<Lesson> missedExams, {required BuildContext context}) => showRoundedModalBottomSheet(context, child: MissedExamView(missedExams));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> tiles = missedExams.map((e) => MissedExamViewTile(e)).toList();
|
||||
return Column(children: tiles);
|
||||
}
|
||||
}
|
||||
|
||||
class MissedExamViewTile extends StatelessWidget {
|
||||
const MissedExamViewTile(this.lesson, {Key? key, this.padding}) : super(key: key);
|
||||
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final Lesson lesson;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
||||
child: ListTile(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
leading: Icon(
|
||||
SubjectIcon.resolveVariant(subject: lesson.subject, context: context),
|
||||
color: AppColors.of(context).text.withOpacity(.8),
|
||||
size: 32.0,
|
||||
),
|
||||
title: Text(
|
||||
"${lesson.subject.renamedTo ?? lesson.subject.name.capital()} • ${lesson.date.format(context)}",
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
"missed_exam_contact".i18n.fill([lesson.teacher]),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: const Icon(FeatherIcons.arrowRight),
|
||||
onTap: () {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
TimetablePage.jump(context, lesson: lesson);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/helpers/subject.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart';
|
||||
import 'package:filcnaplo_mobile_ui/pages/timetable/timetable_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'missed_exam_tile.i18n.dart';
|
||||
|
||||
class MissedExamView extends StatelessWidget {
|
||||
const MissedExamView(this.missedExams, {Key? key}) : super(key: key);
|
||||
|
||||
final List<Lesson> missedExams;
|
||||
|
||||
static show(List<Lesson> missedExams, {required BuildContext context}) => showRoundedModalBottomSheet(context, child: MissedExamView(missedExams));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> tiles = missedExams.map((e) => MissedExamViewTile(e)).toList();
|
||||
return Column(children: tiles);
|
||||
}
|
||||
}
|
||||
|
||||
class MissedExamViewTile extends StatelessWidget {
|
||||
const MissedExamViewTile(this.lesson, {Key? key, this.padding}) : super(key: key);
|
||||
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final Lesson lesson;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
||||
child: ListTile(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
leading: Icon(
|
||||
SubjectIcon.resolveVariant(subject: lesson.subject, context: context),
|
||||
color: AppColors.of(context).text.withOpacity(.8),
|
||||
size: 32.0,
|
||||
),
|
||||
title: Text(
|
||||
"${lesson.subject.renamedTo ?? lesson.subject.name.capital()} • ${lesson.date.format(context)}",
|
||||
style: TextStyle(fontWeight: FontWeight.w600, fontStyle: lesson.subject.isRenamed ? FontStyle.italic : null),
|
||||
),
|
||||
subtitle: Text(
|
||||
"missed_exam_contact".i18n.fill([lesson.teacher]),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: const Icon(FeatherIcons.arrowRight),
|
||||
onTap: () {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
TimetablePage.jump(context, lesson: lesson);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/missed_exam/missed_exam_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/missed_exam/missed_exam_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MissedExamViewable extends StatelessWidget {
|
||||
const MissedExamViewable(this.missedExams, {Key? key}) : super(key: key);
|
||||
|
||||
final List<Lesson> missedExams;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MissedExamTile(
|
||||
missedExams,
|
||||
onTap: () => MissedExamView.show(missedExams, context: context),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/lesson.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/missed_exam/missed_exam_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/missed_exam/missed_exam_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MissedExamViewable extends StatelessWidget {
|
||||
const MissedExamViewable(this.missedExams, {Key? key}) : super(key: key);
|
||||
|
||||
final List<Lesson> missedExams;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MissedExamTile(
|
||||
missedExams,
|
||||
onTap: () => MissedExamView.show(missedExams, context: context),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
import 'package:filcnaplo/utils/color.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/note.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NoteTile extends StatelessWidget {
|
||||
const NoteTile(this.note, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Note note;
|
||||
final void Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: onTap,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
leading: ProfileImage(
|
||||
name: note.teacher,
|
||||
radius: 22.0,
|
||||
backgroundColor: ColorUtils.stringToColor(note.teacher),
|
||||
),
|
||||
title: Text(
|
||||
note.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
note.content.replaceAll('\n', ' '),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/utils/color.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/note.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NoteTile extends StatelessWidget {
|
||||
const NoteTile(this.note, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Note note;
|
||||
final void Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
contentPadding: const EdgeInsets.only(left: 8.0, right: 12.0),
|
||||
onTap: onTap,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
leading: ProfileImage(
|
||||
name: note.teacher,
|
||||
radius: 22.0,
|
||||
backgroundColor: ColorUtils.stringToColor(note.teacher),
|
||||
),
|
||||
title: Text(
|
||||
note.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
note.content.replaceAll('\n', ' '),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,72 @@
|
||||
import 'package:filcnaplo/utils/color.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/note.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/sliding_bottom_sheet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
|
||||
class NoteView extends StatelessWidget {
|
||||
const NoteView(this.note, {Key? key}) : super(key: key);
|
||||
|
||||
final Note note;
|
||||
|
||||
static void show(Note note, {required BuildContext context}) => showSlidingBottomSheet(context: context, child: NoteView(note));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
ListTile(
|
||||
leading: ProfileImage(
|
||||
name: note.teacher,
|
||||
radius: 22.0,
|
||||
backgroundColor: ColorUtils.stringToColor(note.teacher),
|
||||
),
|
||||
title: Text(
|
||||
note.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
note.teacher,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
note.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Details
|
||||
SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: SelectableLinkify(
|
||||
text: note.content.escapeHtml(),
|
||||
options: const LinkifyOptions(looseUrl: true, removeWww: true),
|
||||
onOpen: (link) {
|
||||
launch(link.url,
|
||||
customTabsOption: CustomTabsOption(
|
||||
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
showPageTitle: true,
|
||||
));
|
||||
},
|
||||
style: const TextStyle(fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/utils/color.dart';
|
||||
import 'package:filcnaplo_kreta_api/models/note.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
|
||||
import 'package:filcnaplo/utils/format.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/sliding_bottom_sheet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
|
||||
class NoteView extends StatelessWidget {
|
||||
const NoteView(this.note, {Key? key}) : super(key: key);
|
||||
|
||||
final Note note;
|
||||
|
||||
static void show(Note note, {required BuildContext context}) => showSlidingBottomSheet(context: context, child: NoteView(note));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
ListTile(
|
||||
leading: ProfileImage(
|
||||
name: note.teacher,
|
||||
radius: 22.0,
|
||||
backgroundColor: ColorUtils.stringToColor(note.teacher),
|
||||
),
|
||||
title: Text(
|
||||
note.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
note.teacher,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: Text(
|
||||
note.date.format(context),
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
|
||||
// Details
|
||||
SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: SelectableLinkify(
|
||||
text: note.content.escapeHtml(),
|
||||
options: const LinkifyOptions(looseUrl: true, removeWww: true),
|
||||
onOpen: (link) {
|
||||
launch(link.url,
|
||||
customTabsOption: CustomTabsOption(
|
||||
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
showPageTitle: true,
|
||||
));
|
||||
},
|
||||
style: const TextStyle(fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import 'package:filcnaplo_kreta_api/models/note.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/note/note_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/note/note_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NoteViewable extends StatelessWidget {
|
||||
const NoteViewable(this.note, {Key? key}) : super(key: key);
|
||||
|
||||
final Note note;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return NoteTile(
|
||||
note,
|
||||
onTap: () => NoteView.show(note, context: context),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo_kreta_api/models/note.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/note/note_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/note/note_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NoteViewable extends StatelessWidget {
|
||||
const NoteViewable(this.note, {Key? key}) : super(key: key);
|
||||
|
||||
final Note note;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return NoteTile(
|
||||
note,
|
||||
onTap: () => NoteView.show(note, context: context),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,107 +1,107 @@
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:i18n_extension/i18n_widget.dart';
|
||||
|
||||
class StatisticsTile extends StatelessWidget {
|
||||
const StatisticsTile({
|
||||
Key? key,
|
||||
required this.value,
|
||||
this.title,
|
||||
this.decimal = true,
|
||||
this.color,
|
||||
this.valueSuffix = '',
|
||||
this.fill = false,
|
||||
this.outline = false,
|
||||
}) : super(key: key);
|
||||
|
||||
final double value;
|
||||
final Widget? title;
|
||||
final bool decimal;
|
||||
final Color? color;
|
||||
final String valueSuffix;
|
||||
final bool fill;
|
||||
final bool outline;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String valueText;
|
||||
if (decimal) {
|
||||
valueText = value.toStringAsFixed(2);
|
||||
} else {
|
||||
valueText = value.toStringAsFixed(0);
|
||||
}
|
||||
if (I18n.of(context).locale.languageCode != "en") valueText = valueText.replaceAll(".", ",");
|
||||
|
||||
if (value.isNaN) {
|
||||
valueText = "?";
|
||||
}
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
offset: const Offset(0, 21),
|
||||
blurRadius: 23.0,
|
||||
color: Theme.of(context).shadowColor,
|
||||
)
|
||||
],
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 140.0,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
if (title != null)
|
||||
DefaultTextStyle(
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18.0,
|
||||
),
|
||||
child: title!,
|
||||
),
|
||||
if (title != null) const SizedBox(height: 4.0),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 4.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
|
||||
decoration: BoxDecoration(
|
||||
color: fill ? (color ?? gradeColor(context: context, value: value)).withOpacity(.2) : null,
|
||||
border: outline || fill
|
||||
? Border.all(
|
||||
color: (color ?? gradeColor(context: context, value: value)).withOpacity(outline ? 1.0 : 0.0),
|
||||
width: fill ? 2.0 : 5.0,
|
||||
)
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(45.0),
|
||||
),
|
||||
child: AutoSizeText.rich(
|
||||
TextSpan(
|
||||
text: valueText,
|
||||
children: [
|
||||
if (valueSuffix != "")
|
||||
TextSpan(
|
||||
text: valueSuffix,
|
||||
style: const TextStyle(fontSize: 24.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
maxLines: 1,
|
||||
minFontSize: 5,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: color ?? gradeColor(context: context, value: value),
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 32.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:i18n_extension/i18n_widget.dart';
|
||||
|
||||
class StatisticsTile extends StatelessWidget {
|
||||
const StatisticsTile({
|
||||
Key? key,
|
||||
required this.value,
|
||||
this.title,
|
||||
this.decimal = true,
|
||||
this.color,
|
||||
this.valueSuffix = '',
|
||||
this.fill = false,
|
||||
this.outline = false,
|
||||
}) : super(key: key);
|
||||
|
||||
final double value;
|
||||
final Widget? title;
|
||||
final bool decimal;
|
||||
final Color? color;
|
||||
final String valueSuffix;
|
||||
final bool fill;
|
||||
final bool outline;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String valueText;
|
||||
if (decimal) {
|
||||
valueText = value.toStringAsFixed(2);
|
||||
} else {
|
||||
valueText = value.toStringAsFixed(0);
|
||||
}
|
||||
if (I18n.of(context).locale.languageCode != "en") valueText = valueText.replaceAll(".", ",");
|
||||
|
||||
if (value.isNaN) {
|
||||
valueText = "?";
|
||||
}
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
offset: const Offset(0, 21),
|
||||
blurRadius: 23.0,
|
||||
color: Theme.of(context).shadowColor,
|
||||
)
|
||||
],
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 140.0,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
if (title != null)
|
||||
DefaultTextStyle(
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18.0,
|
||||
),
|
||||
child: title!,
|
||||
),
|
||||
if (title != null) const SizedBox(height: 4.0),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 4.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
|
||||
decoration: BoxDecoration(
|
||||
color: fill ? (color ?? gradeColor(context: context, value: value)).withOpacity(.2) : null,
|
||||
border: outline || fill
|
||||
? Border.all(
|
||||
color: (color ?? gradeColor(context: context, value: value)).withOpacity(outline ? 1.0 : 0.0),
|
||||
width: fill ? 2.0 : 5.0,
|
||||
)
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(45.0),
|
||||
),
|
||||
child: AutoSizeText.rich(
|
||||
TextSpan(
|
||||
text: valueText,
|
||||
children: [
|
||||
if (valueSuffix != "")
|
||||
TextSpan(
|
||||
text: valueSuffix,
|
||||
style: const TextStyle(fontSize: 24.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
maxLines: 1,
|
||||
minFontSize: 5,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: color ?? gradeColor(context: context, value: value),
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 32.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import 'package:filcnaplo/models/release.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'update_tile.i18n.dart';
|
||||
|
||||
class UpdateTile extends StatelessWidget {
|
||||
const UpdateTile(this.release, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Release release;
|
||||
final Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: PanelButton(
|
||||
onPressed: onTap,
|
||||
title: Text("update_available".i18n),
|
||||
leading: const Icon(FeatherIcons.download),
|
||||
trailing: Text(
|
||||
release.tag,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/models/release.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'update_tile.i18n.dart';
|
||||
|
||||
class UpdateTile extends StatelessWidget {
|
||||
const UpdateTile(this.release, {Key? key, this.onTap, this.padding}) : super(key: key);
|
||||
|
||||
final Release release;
|
||||
final Function()? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: PanelButton(
|
||||
onPressed: onTap,
|
||||
title: Text("update_available".i18n),
|
||||
leading: const Icon(FeatherIcons.download),
|
||||
trailing: Text(
|
||||
release.tag,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"update_available": "Update Available",
|
||||
},
|
||||
"hu_hu": {
|
||||
"update_available": "Frissítés elérhető",
|
||||
},
|
||||
"de_de": {
|
||||
"update_available": "Update verfügbar",
|
||||
},
|
||||
};
|
||||
|
||||
String get i18n => localize(this, _t);
|
||||
String fill(List<Object> params) => localizeFill(this, params);
|
||||
String plural(int value) => localizePlural(value, this, _t);
|
||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"update_available": "Update Available",
|
||||
},
|
||||
"hu_hu": {
|
||||
"update_available": "Frissítés elérhető",
|
||||
},
|
||||
"de_de": {
|
||||
"update_available": "Update verfügbar",
|
||||
},
|
||||
};
|
||||
|
||||
String get i18n => localize(this, _t);
|
||||
String fill(List<Object> params) => localizeFill(this, params);
|
||||
String plural(int value) => localizePlural(value, this, _t);
|
||||
String version(Object modifier) => localizeVersion(modifier, this, _t);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import 'package:filcnaplo/models/release.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/update/update_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/update/updates_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UpdateViewable extends StatelessWidget {
|
||||
const UpdateViewable(this.release, {Key? key}) : super(key: key);
|
||||
|
||||
final Release release;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return UpdateTile(
|
||||
release,
|
||||
onTap: () => UpdateView.show(release, context: context),
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/models/release.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/update/update_tile.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/widgets/update/updates_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UpdateViewable extends StatelessWidget {
|
||||
const UpdateViewable(this.release, {Key? key}) : super(key: key);
|
||||
|
||||
final Release release;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return UpdateTile(
|
||||
release,
|
||||
onTap: () => UpdateView.show(release, context: context),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,170 +1,170 @@
|
||||
import 'package:filcnaplo/api/providers/status_provider.dart';
|
||||
import 'package:filcnaplo/models/release.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/color.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/material_action_button.dart';
|
||||
import 'package:filcnaplo/helpers/update_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'updates_view.i18n.dart';
|
||||
|
||||
class UpdateView extends StatefulWidget {
|
||||
const UpdateView(this.release, {Key? key}) : super(key: key);
|
||||
|
||||
final Release release;
|
||||
|
||||
static void show(Release release, {required BuildContext context}) => showBottomCard(context: context, child: UpdateView(release));
|
||||
|
||||
@override
|
||||
_UpdateViewState createState() => _UpdateViewState();
|
||||
}
|
||||
|
||||
class _UpdateViewState extends State<UpdateView> {
|
||||
double progress = 0.0;
|
||||
UpdateState state = UpdateState.none;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"new_update".i18n,
|
||||
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 18.0),
|
||||
),
|
||||
Text(
|
||||
"${widget.release.version}",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16.0,
|
||||
color: AppColors.of(context).text.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
child: Image.asset(
|
||||
"assets/icons/ic_launcher.png",
|
||||
width: 64.0,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Description
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 8.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 125.0,
|
||||
child: Markdown(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
data: widget.release.body,
|
||||
onTapLink: (text, href, title) => launch(href ?? ""),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Download button
|
||||
Center(
|
||||
child: MaterialActionButton(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (state == UpdateState.downloading || state == UpdateState.preparing)
|
||||
Container(
|
||||
height: 18.0,
|
||||
width: 18.0,
|
||||
margin: const EdgeInsets.only(right: 8.0),
|
||||
child: CircularProgressIndicator(
|
||||
value: progress > 0.05 ? progress : null,
|
||||
color: ColorUtils.foregroundColor(AppColors.of(context).filc),
|
||||
),
|
||||
),
|
||||
Text(["download".i18n, "downloading".i18n, "downloading".i18n, "installing".i18n][state.index].toUpperCase()),
|
||||
],
|
||||
),
|
||||
backgroundColor: AppColors.of(context).filc,
|
||||
onPressed: state == UpdateState.none ? () => downloadPrecheck() : null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String fmtSize() => "${(widget.release.downloads.first.size / 1024 / 1024).toStringAsFixed(1)} MB";
|
||||
|
||||
void downloadPrecheck() {
|
||||
final status = Provider.of<StatusProvider>(context, listen: false);
|
||||
if (status.networkType == ConnectivityResult.mobile) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text("mobileAlertTitle".i18n),
|
||||
content: Text("mobileAlertDesc".i18n.fill([fmtSize()])),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: Text("no".i18n),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: Text("yes".i18n),
|
||||
),
|
||||
],
|
||||
),
|
||||
).then((value) => value ? download() : null);
|
||||
} else {
|
||||
download();
|
||||
}
|
||||
}
|
||||
|
||||
void download() {
|
||||
widget.release
|
||||
.install(updateCallback: (p, s) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
progress = p;
|
||||
state = s;
|
||||
});
|
||||
}
|
||||
})
|
||||
.then((_) => Navigator.of(context).maybePop())
|
||||
.catchError((error, stackTrace) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
||||
context: context,
|
||||
content: Text("error".i18n),
|
||||
backgroundColor: AppColors.of(context).red,
|
||||
));
|
||||
setState(() => state = UpdateState.none);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
import 'package:filcnaplo/api/providers/status_provider.dart';
|
||||
import 'package:filcnaplo/models/release.dart';
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo/utils/color.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
|
||||
import 'package:filcnaplo_mobile_ui/common/material_action_button.dart';
|
||||
import 'package:filcnaplo/helpers/update_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'updates_view.i18n.dart';
|
||||
|
||||
class UpdateView extends StatefulWidget {
|
||||
const UpdateView(this.release, {Key? key}) : super(key: key);
|
||||
|
||||
final Release release;
|
||||
|
||||
static void show(Release release, {required BuildContext context}) => showBottomCard(context: context, child: UpdateView(release));
|
||||
|
||||
@override
|
||||
_UpdateViewState createState() => _UpdateViewState();
|
||||
}
|
||||
|
||||
class _UpdateViewState extends State<UpdateView> {
|
||||
double progress = 0.0;
|
||||
UpdateState state = UpdateState.none;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"new_update".i18n,
|
||||
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 18.0),
|
||||
),
|
||||
Text(
|
||||
"${widget.release.version}",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16.0,
|
||||
color: AppColors.of(context).text.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
child: Image.asset(
|
||||
"assets/icons/ic_launcher.png",
|
||||
width: 64.0,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Description
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 8.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 125.0,
|
||||
child: Markdown(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
data: widget.release.body,
|
||||
onTapLink: (text, href, title) => launch(href ?? ""),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Download button
|
||||
Center(
|
||||
child: MaterialActionButton(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (state == UpdateState.downloading || state == UpdateState.preparing)
|
||||
Container(
|
||||
height: 18.0,
|
||||
width: 18.0,
|
||||
margin: const EdgeInsets.only(right: 8.0),
|
||||
child: CircularProgressIndicator(
|
||||
value: progress > 0.05 ? progress : null,
|
||||
color: ColorUtils.foregroundColor(AppColors.of(context).filc),
|
||||
),
|
||||
),
|
||||
Text(["download".i18n, "downloading".i18n, "downloading".i18n, "installing".i18n][state.index].toUpperCase()),
|
||||
],
|
||||
),
|
||||
backgroundColor: AppColors.of(context).filc,
|
||||
onPressed: state == UpdateState.none ? () => downloadPrecheck() : null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String fmtSize() => "${(widget.release.downloads.first.size / 1024 / 1024).toStringAsFixed(1)} MB";
|
||||
|
||||
void downloadPrecheck() {
|
||||
final status = Provider.of<StatusProvider>(context, listen: false);
|
||||
if (status.networkType == ConnectivityResult.mobile) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text("mobileAlertTitle".i18n),
|
||||
content: Text("mobileAlertDesc".i18n.fill([fmtSize()])),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: Text("no".i18n),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: Text("yes".i18n),
|
||||
),
|
||||
],
|
||||
),
|
||||
).then((value) => value ? download() : null);
|
||||
} else {
|
||||
download();
|
||||
}
|
||||
}
|
||||
|
||||
void download() {
|
||||
widget.release
|
||||
.install(updateCallback: (p, s) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
progress = p;
|
||||
state = s;
|
||||
});
|
||||
}
|
||||
})
|
||||
.then((_) => Navigator.of(context).maybePop())
|
||||
.catchError((error, stackTrace) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
|
||||
context: context,
|
||||
content: Text("error".i18n),
|
||||
backgroundColor: AppColors.of(context).red,
|
||||
));
|
||||
setState(() => state = UpdateState.none);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"new_update": "New Update",
|
||||
"download": "download",
|
||||
"downloading": "downloading",
|
||||
"installing": "installing",
|
||||
"error": "Failed to install update!",
|
||||
"no": "No",
|
||||
"yes": "Yes",
|
||||
"mobileAlertTitle": "Hold up!",
|
||||
"mobileAlertDesc": "You're on mobile network trying to download a %s update. Are you sure you want to continue?"
|
||||
},
|
||||
"hu_hu": {
|
||||
"new_update": "Új frissítés",
|
||||
"download": "Letöltés",
|
||||
"downloading": "Letöltés",
|
||||
"installing": "Telepítés",
|
||||
"error": "Nem sikerült telepíteni a frissítést!",
|
||||
"no": "Nem",
|
||||
"yes": "Igen",
|
||||
"mobileAlertTitle": "Figyelem!",
|
||||
"mobileAlertDesc": "Jelenleg mobil interneten vagy, és egy %s méretű frissítést próbálsz letölteni. Biztosan folytatod?"
|
||||
},
|
||||
"de_de": {
|
||||
"new_update": "Neues Update",
|
||||
"download": "herunterladen",
|
||||
"downloading": "Herunterladen",
|
||||
"installing": "Installation",
|
||||
"error": "Update konnte nicht installiert werden!",
|
||||
"no": "Nein",
|
||||
"yes": "Ja",
|
||||
"mobileAlertTitle": "Achtung!",
|
||||
"mobileAlertDesc":
|
||||
"Sie befinden sich gerade im mobilen Internet und versuchen, ein %s Update herunterzuladen. Sind Sie sicher, dass Sie weitermachen wollen?"
|
||||
},
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension Localization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"new_update": "New Update",
|
||||
"download": "download",
|
||||
"downloading": "downloading",
|
||||
"installing": "installing",
|
||||
"error": "Failed to install update!",
|
||||
"no": "No",
|
||||
"yes": "Yes",
|
||||
"mobileAlertTitle": "Hold up!",
|
||||
"mobileAlertDesc": "You're on mobile network trying to download a %s update. Are you sure you want to continue?"
|
||||
},
|
||||
"hu_hu": {
|
||||
"new_update": "Új frissítés",
|
||||
"download": "Letöltés",
|
||||
"downloading": "Letöltés",
|
||||
"installing": "Telepítés",
|
||||
"error": "Nem sikerült telepíteni a frissítést!",
|
||||
"no": "Nem",
|
||||
"yes": "Igen",
|
||||
"mobileAlertTitle": "Figyelem!",
|
||||
"mobileAlertDesc": "Jelenleg mobil interneten vagy, és egy %s méretű frissítést próbálsz letölteni. Biztosan folytatod?"
|
||||
},
|
||||
"de_de": {
|
||||
"new_update": "Neues Update",
|
||||
"download": "herunterladen",
|
||||
"downloading": "Herunterladen",
|
||||
"installing": "Installation",
|
||||
"error": "Update konnte nicht installiert werden!",
|
||||
"no": "Nein",
|
||||
"yes": "Ja",
|
||||
"mobileAlertTitle": "Achtung!",
|
||||
"mobileAlertDesc":
|
||||
"Sie befinden sich gerade im mobilen Internet und versuchen, ein %s Update herunterzuladen. Sind Sie sicher, dass Sie weitermachen wollen?"
|
||||
},
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user