remelem mukszik

This commit is contained in:
ReinerRego
2023-05-26 21:51:21 +02:00
parent baec76c29f
commit 0ece9382af
170 changed files with 15575 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
class AccountTile extends StatelessWidget {
const AccountTile({Key? key, this.onTap, this.onTapMenu, this.profileImage, this.name, this.username}) : super(key: key);
final void Function()? onTap;
final void Function()? onTapMenu;
final Widget? profileImage;
final Widget? name;
final Widget? username;
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: ListTile(
visualDensity: VisualDensity.compact,
contentPadding: const EdgeInsets.only(left: 12.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
onTap: onTap,
onLongPress: onTapMenu,
leading: profileImage,
title: name,
subtitle: username,
trailing: onTapMenu != null
? Material(
color: Colors.transparent,
child: IconButton(
splashRadius: 24.0,
onPressed: onTapMenu,
icon: Icon(FeatherIcons.moreVertical, color: AppColors.of(context).text.withOpacity(0.8)),
),
)
: null,
),
);
}
}

View File

@@ -0,0 +1,54 @@
import 'package:filcnaplo/models/user.dart';
import 'package:filcnaplo/utils/color.dart';
import 'package:filcnaplo_mobile_ui/common/bottom_card.dart';
import 'package:filcnaplo_mobile_ui/common/detail.dart';
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/accounts/account_tile.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'account_view.i18n.dart';
class AccountView extends StatelessWidget {
const AccountView(this.user, {Key? key}) : super(key: key);
final User user;
static void show(User user, {required BuildContext context}) => showBottomCard(context: context, child: AccountView(user));
@override
Widget build(BuildContext context) {
List<String> _nameParts = user.name.split(" ");
String _firstName = _nameParts.length > 1 ? _nameParts[1] : _nameParts[0];
return Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AccountTile(
profileImage: ProfileImage(
name: _firstName,
backgroundColor: ColorUtils.stringToColor(user.name),
role: user.role,
),
name: SelectableText(
user.name,
style: const TextStyle(fontWeight: FontWeight.w500),
maxLines: 2,
minLines: 1,
),
username: SelectableText(user.username),
),
// User details
Detail(title: "birthdate".i18n, description: DateFormat("yyyy. MM. dd.").format(user.student.birth)),
Detail(title: "school".i18n, description: user.student.school.name),
if (user.student.className != null) Detail(title: "class".i18n, description: user.student.className!),
if (user.student.address != null) Detail(title: "address".i18n, description: user.student.address!),
if (user.student.parents.isNotEmpty)
Detail(title: "parents".plural(user.student.parents.length), description: user.student.parents.join(", ")),
],
),
);
}
}

View File

@@ -0,0 +1,33 @@
import 'package:i18n_extension/i18n_extension.dart';
extension Localization on String {
static final _t = Translations.byLocale("hu_hu") +
{
"en_en": {
"birthdate": "Birth date",
"school": "School",
"class": "Class",
"address": "Home address",
"parents": "Parents".one("Parent"),
},
"hu_hu": {
"birthdate": "Születési dátum",
"school": "Iskola",
"class": "Osztály",
"address": "Lakcím",
"parents": "Szülők".one("Szülő"),
},
"de_de": {
"birthdate": "Geburtsdatum",
"school": "Schule",
"class": "Klasse",
"address": "Wohnanschrift",
"parents": "Eltern",
},
};
String get i18n => localize(this, _t);
String fill(List<Object> params) => localizeFill(this, params);
String plural(int value) => localizePlural(value, this, _t);
String version(Object modifier) => localizeVersion(modifier, this, _t);
}

View File

@@ -0,0 +1,81 @@
import 'package:filcnaplo/helpers/subject.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:flutter/material.dart';
class SubjectIconGallery extends StatelessWidget {
const SubjectIconGallery({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
leading: BackButton(color: AppColors.of(context).text),
title: Text(
"Subject Icon Gallery",
style: TextStyle(color: AppColors.of(context).text),
),
),
body: ListView(
children: const [
SubjectIconItem("Matematika"),
SubjectIconItem("Magyar Nyelv"),
SubjectIconItem("Nyelvtan"),
SubjectIconItem("Irodalom"),
SubjectIconItem("Történelem"),
SubjectIconItem("Földrajz"),
SubjectIconItem("Rajz"),
SubjectIconItem("Vizuális kultúra"),
SubjectIconItem("Fizika"),
SubjectIconItem("Ének"),
SubjectIconItem("Testnevelés"),
SubjectIconItem("Kémia"),
SubjectIconItem("Biológia"),
SubjectIconItem("Természetismeret"),
SubjectIconItem("Erkölcstan"),
SubjectIconItem("Pénzügy"),
SubjectIconItem("Informatika"),
SubjectIconItem("Digitális kultúra"),
SubjectIconItem("Programozás"),
SubjectIconItem("Hálózat"),
SubjectIconItem("Színház technika"),
SubjectIconItem("Média"),
SubjectIconItem("Elektronika"),
SubjectIconItem("Gépészet"),
SubjectIconItem("Technika"),
SubjectIconItem("Tánc"),
SubjectIconItem("Filozófia"),
SubjectIconItem("Osztályfőnöki"),
SubjectIconItem("Gazdaság"),
SubjectIconItem("Szorgalom"),
SubjectIconItem("Magatartás"),
SubjectIconItem("Angol nyelv"),
SubjectIconItem("Linux"),
],
),
);
}
}
class SubjectIconItem extends StatelessWidget {
const SubjectIconItem(this.name, {Key? key}) : super(key: key);
final String name;
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(
SubjectIcon.resolveVariant(subjectName: name, context: context),
color: AppColors.of(context).text,
),
title: Text(
name,
style: TextStyle(
color: AppColors.of(context).text,
fontWeight: FontWeight.w500,
),
),
);
}
}

View File

@@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'settings_screen.i18n.dart';
class PrivacyView extends StatelessWidget {
const PrivacyView({Key? key}) : super(key: key);
static void show(BuildContext context) => showDialog(context: context, builder: (context) => const PrivacyView(), barrierDismissible: true);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 100.0, horizontal: 32.0),
child: Material(
borderRadius: BorderRadius.circular(12.0),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: ListView(
physics: const BouncingScrollPhysics(),
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("privacy".i18n),
),
SelectableLinkify(
text: """
A Filc Napló egy kliensalkalmazás, segítségével az e-Kréta rendszeréből letöltheted és felhasználóbarát módon megjelenítheted az adataidat.
Tanulmányi adataid csak közvetlenül az alkalmazás és a Kréta-szerverek között közlekednek, titkosított kapcsolaton keresztül.
A Filc fejlesztői és üzemeltetői a tanulmányi adataidat semmilyen célból nem másolják, nem tárolják és harmadik félnek nem továbbítják. Ezeket így az e-Kréta Informatikai Zrt. kezeli, az ő tájékoztatójukat itt találod: https://tudasbazis.ekreta.hu/pages/viewpage.action?pageId=4065038.
Azok törlésével vagy módosítával kapcsolatban keresd az osztályfőnöködet vagy az iskolád rendszergazdáját.
Az alkalmazás névtelen használati statisztikákat gyűjt, ezek alapján tudjuk meghatározni a felhasználók és a telepítések számát. Ezt a beállításokban kikapcsolhatod.
Kérünk, hogy ha csak teheted, hagyd ezt a funkciót bekapcsolva.
Amikor az alkalmazás hibába ütközik, lehetőség van hibajelentés küldésére.
Ez személyes- vagy tanulmányi adatokat nem tartalmaz, viszont részletes információval szolgál a hibáról és eszközödről.
A küldés előtt megjelenő képernyőn a te felelősséged átnézni a továbbításra kerülő adatsort.
A hibajelentéseket a Filc fejlesztői felületén és egy privát Discord szobában tároljuk, ezekhez csak az app fejlesztői férnek hozzá.
Az alkalmazás belépéskor a GitHub API segítségével ellenőrzi, hogy elérhető-e új verzió, és kérésre innen is tölti le a telepítőt.
Ha az adataiddal kapcsolatban bármilyen kérdésed van (törlés, módosítás, adathordozás), keress minket a filcnaplo@filcnaplo.hu címen.
Az alkalmazás használatával jelzed, hogy ezt a tájékoztatót tudomásul vetted.
Utolsó módosítás: 2021. 09. 25.
""",
onOpen: (link) => launch(link.url,
customTabsOption: CustomTabsOption(
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
showPageTitle: true,
)),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,548 @@
// ignore_for_file: prefer_function_declarations_over_variables
import 'dart:io';
import 'package:filcnaplo/helpers/quick_actions.dart';
import 'package:filcnaplo/icons/filc_icons.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/theme/observer.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo_kreta_api/models/week.dart';
import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu.dart';
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_item.dart';
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart';
import 'package:filcnaplo_mobile_ui/common/filter_bar.dart';
import 'package:filcnaplo_mobile_ui/common/material_action_button.dart';
import 'package:filcnaplo/ui/widgets/grade/grade_tile.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:i18n_extension/i18n_widget.dart';
import 'package:provider/provider.dart';
import 'package:filcnaplo_mobile_ui/common/screens.i18n.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart';
import 'package:flutter_material_color_picker/flutter_material_color_picker.dart';
import 'package:filcnaplo/models/icon_pack.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/theme.dart';
class SettingsHelper {
static const Map<String, String> langMap = {"en": "🇬🇧 English", "hu": "🇭🇺 Magyar", "de": "🇩🇪 Deutsch"};
static const Map<Pages, String> pageTitle = {
Pages.home: "home",
Pages.grades: "grades",
Pages.timetable: "timetable",
Pages.messages: "messages",
Pages.absences: "absences",
};
static Map<VibrationStrength, String> vibrationTitle = {
VibrationStrength.off: "voff",
VibrationStrength.light: "vlight",
VibrationStrength.medium: "vmedium",
VibrationStrength.strong: "vstrong",
};
static Map<Pages, String> localizedPageTitles() => pageTitle.map((key, value) => MapEntry(key, ScreensLocalization(value).i18n));
static Map<VibrationStrength, String> localizedVibrationTitles() =>
vibrationTitle.map((key, value) => MapEntry(key, SettingsLocalization(value).i18n));
static void language(BuildContext context) {
showBottomSheetMenu(
context,
items: List.generate(langMap.length, (index) {
String lang = langMap.keys.toList()[index];
return BottomSheetMenuItem(
onPressed: () {
Provider.of<SettingsProvider>(context, listen: false).update(language: lang);
I18n.of(context).locale = Locale(lang, lang.toUpperCase());
Navigator.of(context).maybePop();
if (Platform.isAndroid || Platform.isIOS) {
setupQuickActions();
}
},
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(langMap.values.toList()[index]),
if (lang == I18n.of(context).locale.languageCode)
Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.secondary,
),
],
),
);
}),
);
}
static void iconPack(BuildContext context) {
final settings = Provider.of<SettingsProvider>(context, listen: false);
showBottomSheetMenu(
context,
items: List.generate(IconPack.values.length, (index) {
IconPack current = IconPack.values[index];
return BottomSheetMenuItem(
onPressed: () {
settings.update(iconPack: current);
Navigator.of(context).maybePop();
},
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(current.name.capital()),
if (current == settings.iconPack)
Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.secondary,
),
],
),
);
}),
);
}
static void startPage(BuildContext context) {
Map<Pages, IconData> pageIcons = {
Pages.home: FilcIcons.home,
Pages.grades: FeatherIcons.bookmark,
Pages.timetable: FeatherIcons.calendar,
Pages.messages: FeatherIcons.messageSquare,
Pages.absences: FeatherIcons.clock,
};
showBottomSheetMenu(
context,
items: List.generate(Pages.values.length, (index) {
return BottomSheetMenuItem(
onPressed: () {
Provider.of<SettingsProvider>(context, listen: false).update(startPage: Pages.values[index]);
Navigator.of(context).maybePop();
},
title: Row(
children: [
Icon(pageIcons[Pages.values[index]], size: 20.0, color: Theme.of(context).colorScheme.secondary),
const SizedBox(width: 16.0),
Text(localizedPageTitles()[Pages.values[index]] ?? ""),
const Spacer(),
if (Pages.values[index] == Provider.of<SettingsProvider>(context, listen: false).startPage)
Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.secondary,
),
],
),
);
}),
);
}
static void rounding(BuildContext context) {
showRoundedModalBottomSheet(
context,
child: const RoundingSetting(),
);
}
static void theme(BuildContext context) {
var settings = Provider.of<SettingsProvider>(context, listen: false);
void Function(ThemeMode) setTheme = (mode) {
settings.update(theme: mode);
Provider.of<ThemeModeObserver>(context, listen: false).changeTheme(mode);
Navigator.of(context).maybePop();
};
showBottomSheetMenu(context, items: [
BottomSheetMenuItem(
onPressed: () => setTheme(ThemeMode.system),
title: Row(
children: [
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(FeatherIcons.smartphone, size: 20.0, color: Theme.of(context).colorScheme.secondary),
),
Text(SettingsLocalization("system").i18n),
const Spacer(),
if (settings.theme == ThemeMode.system)
Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.secondary,
),
],
),
),
BottomSheetMenuItem(
onPressed: () => setTheme(ThemeMode.light),
title: Row(
children: [
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(FeatherIcons.sun, size: 20.0, color: Theme.of(context).colorScheme.secondary),
),
Text(SettingsLocalization("light").i18n),
const Spacer(),
if (settings.theme == ThemeMode.light)
Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.secondary,
),
],
),
),
BottomSheetMenuItem(
onPressed: () => setTheme(ThemeMode.dark),
title: Row(
children: [
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(FeatherIcons.moon, size: 20.0, color: Theme.of(context).colorScheme.secondary),
),
Text(SettingsLocalization("dark").i18n),
const Spacer(),
if (settings.theme == ThemeMode.dark)
Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.secondary,
),
],
),
)
]);
}
static void accentColor(BuildContext context) {
Navigator.of(context, rootNavigator: true).push(
PageRouteBuilder(
pageBuilder: (context, _, __) => const PremiumCustomAccentColorSetting(),
transitionDuration: Duration.zero,
reverseTransitionDuration: Duration.zero,
),
);
}
static void gradeColors(BuildContext context) {
showRoundedModalBottomSheet(
context,
child: const GradeColorsSetting(),
);
}
static void vibrate(BuildContext context) {
showBottomSheetMenu(
context,
items: List.generate(VibrationStrength.values.length, (index) {
VibrationStrength value = VibrationStrength.values[index];
return BottomSheetMenuItem(
onPressed: () {
Provider.of<SettingsProvider>(context, listen: false).update(vibrate: value);
Navigator.of(context).maybePop();
},
title: Row(
children: [
Container(
width: 12.0,
height: 12.0,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary.withOpacity((index + 1) / (vibrationTitle.length + 1)),
shape: BoxShape.circle,
),
),
const SizedBox(width: 16.0),
Text(localizedVibrationTitles()[value] ?? "?"),
const Spacer(),
if (value == Provider.of<SettingsProvider>(context, listen: false).vibrate)
Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.secondary,
),
],
),
);
}),
);
}
static void bellDelay(BuildContext context) {
showRoundedModalBottomSheet(
context,
child: const BellDelaySetting(),
);
}
}
// Rounding modal
class RoundingSetting extends StatefulWidget {
const RoundingSetting({Key? key}) : super(key: key);
@override
_RoundingSettingState createState() => _RoundingSettingState();
}
class _RoundingSettingState extends State<RoundingSetting> {
late double rounding;
@override
void initState() {
super.initState();
rounding = Provider.of<SettingsProvider>(context, listen: false).rounding / 10;
}
@override
Widget build(BuildContext context) {
int roundingResult;
if (4.5 >= 4.5.floor() + rounding) {
roundingResult = 5;
} else {
roundingResult = 4;
}
return Column(children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Slider(
value: rounding,
min: 0.1,
max: 0.9,
divisions: 8,
label: rounding.toStringAsFixed(1),
activeColor: Theme.of(context).colorScheme.secondary,
thumbColor: Theme.of(context).colorScheme.secondary,
onChanged: (v) => setState(() => rounding = v),
),
),
Container(
width: 50.0,
padding: const EdgeInsets.only(right: 16.0),
child: Center(
child: Text(rounding.toStringAsFixed(1),
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 18.0,
)),
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("4.5", style: TextStyle(fontSize: 26.0, fontWeight: FontWeight.w500)),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Icon(FeatherIcons.arrowRight, color: Colors.grey),
),
GradeValueWidget(GradeValue(roundingResult, "", "", 100), fill: true, size: 32.0),
],
),
Padding(
padding: const EdgeInsets.only(bottom: 12.0, top: 6.0),
child: MaterialActionButton(
child: Text(SettingsLocalization("done").i18n),
onPressed: () {
Provider.of<SettingsProvider>(context, listen: false).update(rounding: (rounding * 10).toInt());
Navigator.of(context).maybePop();
},
),
),
]);
}
}
// Bell Delay Modal
class BellDelaySetting extends StatefulWidget {
const BellDelaySetting({Key? key}) : super(key: key);
@override
State<BellDelaySetting> createState() => _BellDelaySettingState();
}
class _BellDelaySettingState extends State<BellDelaySetting> with SingleTickerProviderStateMixin {
late TabController _tabController;
late Duration currentDelay;
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this, initialIndex: Provider.of<SettingsProvider>(context, listen: false).bellDelay > 0 ? 1 : 0);
currentDelay = Duration(seconds: Provider.of<SettingsProvider>(context, listen: false).bellDelay);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
FilterBar(
scrollable: false,
items: [
Tab(text: SettingsLocalization("delay").i18n),
Tab(text: SettingsLocalization("hurry").i18n),
],
controller: _tabController,
onTap: (i) async {
// swap current page with target page
setState(() {
currentDelay = i == 0 ? -currentDelay.abs() : currentDelay.abs();
});
},
),
SizedBox(
height: 200,
child: CupertinoTheme(
data: CupertinoThemeData(
brightness: Theme.of(context).brightness,
),
child: CupertinoTimerPicker(
key: UniqueKey(),
mode: CupertinoTimerPickerMode.ms,
initialTimerDuration: currentDelay.abs(),
onTimerDurationChanged: (Duration d) {
HapticFeedback.selectionClick();
currentDelay = _tabController.index == 0 ? -d : d;
},
),
),
),
Text(SettingsLocalization("sync_help").i18n,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12.0, fontWeight: FontWeight.w500, color: AppColors.of(context).text.withOpacity(.75))),
Padding(
padding: const EdgeInsets.only(bottom: 12.0, top: 6.0),
child: Column(
children: [
MaterialActionButton(
backgroundColor: AppColors.of(context).filc,
child: Text(SettingsLocalization("sync").i18n),
onPressed: () {
final lessonProvider = Provider.of<TimetableProvider>(context, listen: false);
Duration? closest;
DateTime now = DateTime.now();
for (var lesson in lessonProvider.getWeek(Week.current()) ?? []) {
Duration sdiff = lesson.start.difference(now);
Duration ediff = lesson.end.difference(now);
if (closest == null || sdiff.abs() < closest.abs()) closest = sdiff;
if (ediff.abs() < closest.abs()) closest = ediff;
}
if (closest != null) {
if (closest.inHours.abs() >= 1) return;
currentDelay = closest;
Provider.of<SettingsProvider>(context, listen: false).update(bellDelay: currentDelay.inSeconds);
_tabController.index = currentDelay.inSeconds > 0 ? 1 : 0;
setState(() {});
}
},
),
MaterialActionButton(
child: Text(SettingsLocalization("done").i18n),
onPressed: () {
//Provider.of<SettingsProvider>(context, listen: false).update(context, rounding: (r * 10).toInt());
Provider.of<SettingsProvider>(context, listen: false).update(bellDelay: currentDelay.inSeconds);
Navigator.of(context).maybePop();
},
),
],
),
),
],
);
}
}
class GradeColorsSetting extends StatefulWidget {
const GradeColorsSetting({Key? key}) : super(key: key);
@override
_GradeColorsSettingState createState() => _GradeColorsSettingState();
}
class _GradeColorsSettingState extends State<GradeColorsSetting> {
Color currentColor = const Color(0x00000000);
late SettingsProvider settings;
@override
void initState() {
super.initState();
settings = Provider.of<SettingsProvider>(context, listen: false);
}
@override
Widget build(BuildContext context) {
return Column(children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(5, (index) {
return ClipOval(
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () {
currentColor = settings.gradeColors[index];
showRoundedModalBottomSheet(
context,
child: Column(children: [
MaterialColorPicker(
selectedColor: settings.gradeColors[index],
onColorChange: (v) {
setState(() {
currentColor = v;
});
},
allowShades: true,
elevation: 0,
physics: const NeverScrollableScrollPhysics(),
),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
MaterialActionButton(
onPressed: () {
List<Color> colors = List.castFrom(settings.gradeColors);
var defaultColors = SettingsProvider.defaultSettings().gradeColors;
colors[index] = defaultColors[index];
settings.update(gradeColors: colors);
Navigator.of(context).maybePop();
},
child: Text(SettingsLocalization("reset").i18n),
),
MaterialActionButton(
onPressed: () {
List<Color> colors = List.castFrom(settings.gradeColors);
colors[index] = currentColor.withAlpha(255);
settings.update(gradeColors: settings.gradeColors);
Navigator.of(context).maybePop();
},
child: Text(SettingsLocalization("done").i18n),
),
],
),
),
]),
).then((value) => setState(() {}));
},
child: GradeValueWidget(GradeValue(index + 1, "", "", 0), fill: true, size: 36.0),
),
),
);
}),
),
),
]);
}
}

View File

@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
Route settingsRoute(Widget widget) {
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => widget,
transitionDuration: const Duration(milliseconds: 500),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
var curve = Curves.ease;
var curveTween = CurveTween(curve: curve);
var begin = const Offset(0.0, 1.0);
var end = Offset.zero;
var tween = Tween(begin: begin, end: end).chain(curveTween);
var offsetAnimation = animation.drive(tween);
return SlideTransition(
position: offsetAnimation,
child: child,
);
},
);
}

View File

@@ -0,0 +1,816 @@
import 'package:filcnaplo/api/providers/update_provider.dart';
import 'package:filcnaplo/theme/colors/accent.dart';
import 'package:filcnaplo/theme/observer.dart';
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
import 'package:filcnaplo_kreta_api/providers/event_provider.dart';
import 'package:filcnaplo_kreta_api/providers/exam_provider.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:filcnaplo_kreta_api/providers/homework_provider.dart';
import 'package:filcnaplo_kreta_api/providers/message_provider.dart';
import 'package:filcnaplo_kreta_api/providers/note_provider.dart';
import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/utils/format.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/models/user.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_mobile_ui/common/action_button.dart';
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu.dart';
import 'package:filcnaplo_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_item.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
import 'package:filcnaplo_mobile_ui/common/profile_image/profile_image.dart';
import 'package:filcnaplo_mobile_ui/common/system_chrome.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/update/updates_view.dart';
import 'package:filcnaplo_mobile_ui/premium/components/active_sponsor_card.dart';
import 'package:filcnaplo_mobile_ui/premium/premium_button.dart';
import 'package:filcnaplo_mobile_ui/screens/news/news_screen.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/accounts/account_tile.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/accounts/account_view.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/debug/subject_icon_gallery.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/privacy_view.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/settings_helper.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart' as tabs;
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:filcnaplo/utils/color.dart';
import 'package:url_launcher/url_launcher.dart';
import 'settings_screen.i18n.dart';
import 'package:flutter/services.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/nickname.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/profile_pic.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/icon_pack.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/modify_subject_names.dart';
class SettingsScreen extends StatefulWidget {
const SettingsScreen({Key? key}) : super(key: key);
@override
_SettingsScreenState createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> with SingleTickerProviderStateMixin {
int devmodeCountdown = 3;
bool __ss = false; // secret settings
late UserProvider user;
late UpdateProvider updateProvider;
late SettingsProvider settings;
late KretaClient kretaClient;
late String firstName;
List<Widget> accountTiles = [];
late AnimationController _hideContainersController;
Future<void> restore() => Future.wait([
Provider.of<GradeProvider>(context, listen: false).restore(),
Provider.of<TimetableProvider>(context, listen: false).restoreUser(),
Provider.of<ExamProvider>(context, listen: false).restore(),
Provider.of<HomeworkProvider>(context, listen: false).restore(),
Provider.of<MessageProvider>(context, listen: false).restore(),
Provider.of<NoteProvider>(context, listen: false).restore(),
Provider.of<EventProvider>(context, listen: false).restore(),
Provider.of<AbsenceProvider>(context, listen: false).restore(),
Provider.of<KretaClient>(context, listen: false).refreshLogin(),
]);
void buildAccountTiles() {
accountTiles = [];
user.getUsers().forEach((account) {
if (account.id == user.id) return;
String _firstName;
List<String> _nameParts = user.displayName?.split(" ") ?? ["?"];
if (!settings.presentationMode) {
_firstName = _nameParts.length > 1 ? _nameParts[1] : _nameParts[0];
} else {
_firstName = "Béla";
}
accountTiles.add(AccountTile(
name: Text(!settings.presentationMode ? account.name : "Béla", style: const TextStyle(fontWeight: FontWeight.w500)),
username: Text(!settings.presentationMode ? account.username : "72469696969"),
profileImage: ProfileImage(
name: _firstName,
backgroundColor: !settings.presentationMode ? ColorUtils.stringToColor(account.name) : Theme.of(context).colorScheme.secondary,
role: account.role,
),
onTap: () {
user.setUser(account.id);
restore().then((_) => user.setUser(account.id));
Navigator.of(context).pop();
},
onTapMenu: () => _showBottomSheet(account),
));
});
}
void _showBottomSheet(User u) {
showBottomSheetMenu(context, items: [
BottomSheetMenuItem(
onPressed: () => AccountView.show(u, context: context),
icon: const Icon(FeatherIcons.user),
title: Text("personal_details".i18n),
),
BottomSheetMenuItem(
onPressed: () => _openDKT(u),
icon: Icon(FeatherIcons.grid, color: AppColors.of(context).teal),
title: Text("open_dkt".i18n),
),
const UserMenuNickname(),
const UserMenuProfilePic(),
// BottomSheetMenuItem(
// onPressed: () {},
// icon: Icon(FeatherIcons.camera),
// title: Text("edit_profile_picture".i18n),
// ),
// BottomSheetMenuItem(
// onPressed: () {},
// icon: Icon(FeatherIcons.trash2, color: AppColors.of(context).red),
// title: Text("remove_profile_picture".i18n),
// ),
]);
}
void _openDKT(User u) => tabs.launch("https://dkttanulo.e-kreta.hu/sso?id_token=${kretaClient.idToken}",
customTabsOption: tabs.CustomTabsOption(
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
showPageTitle: true,
));
@override
void initState() {
super.initState();
_hideContainersController = AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
}
@override
Widget build(BuildContext context) {
user = Provider.of<UserProvider>(context);
settings = Provider.of<SettingsProvider>(context);
updateProvider = Provider.of<UpdateProvider>(context);
kretaClient = Provider.of<KretaClient>(context);
List<String> nameParts = user.displayName?.split(" ") ?? ["?"];
if (!settings.presentationMode) {
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
} else {
firstName = "Béla";
}
String startPageTitle = SettingsHelper.localizedPageTitles()[settings.startPage] ?? "?";
String themeModeText = {ThemeMode.light: "light".i18n, ThemeMode.dark: "dark".i18n, ThemeMode.system: "system".i18n}[settings.theme] ?? "?";
String languageText = SettingsHelper.langMap[settings.language] ?? "?";
String vibrateTitle = {
VibrationStrength.off: "voff".i18n,
VibrationStrength.light: "vlight".i18n,
VibrationStrength.medium: "vmedium".i18n,
VibrationStrength.strong: "vstrong".i18n,
}[settings.vibrate] ??
"?";
buildAccountTiles();
if (settings.developerMode) devmodeCountdown = -1;
return AnimatedBuilder(
animation: _hideContainersController,
builder: (context, child) => Opacity(
opacity: 1 - _hideContainersController.value,
child: Column(
children: [
const SizedBox(height: 32.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
splashRadius: 32.0,
onPressed: () => _showBottomSheet(user.getUser(user.id ?? "")),
icon: Icon(FeatherIcons.moreVertical, color: AppColors.of(context).text.withOpacity(0.8)),
),
IconButton(
splashRadius: 26.0,
onPressed: () {
Navigator.of(context).pop();
},
icon: Icon(FeatherIcons.x, color: AppColors.of(context).text.withOpacity(0.8)),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: ProfileImage(
heroTag: "profile",
radius: 36.0,
onTap: () => _showBottomSheet(user.getUser(user.id ?? "")),
name: firstName,
badge: updateProvider.available,
role: user.role,
profilePictureString: user.picture,
backgroundColor:
!settings.presentationMode ? ColorUtils.stringToColor(user.displayName ?? "?") : Theme.of(context).colorScheme.secondary,
),
),
Padding(
padding: const EdgeInsets.only(top: 4.0, bottom: 12.0),
child: GestureDetector(
onTap: () => _showBottomSheet(user.getUser(user.id ?? "")),
onDoubleTap: () => setState(() => __ss = true),
child: Text(
!settings.presentationMode ? (user.displayName ?? "?") : "Béla",
maxLines: 1,
softWrap: false,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.w600, color: AppColors.of(context).text),
),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
child: Column(
children: [
// Account list
...accountTiles,
if (accountTiles.isNotEmpty)
Center(
child: Container(
margin: const EdgeInsets.only(top: 12.0, bottom: 4.0),
height: 3.0,
width: 75.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: AppColors.of(context).text.withOpacity(.25),
),
),
),
// Account settings
PanelButton(
onPressed: () {
Navigator.of(context).pushNamed("login_back").then((value) {
setSystemChrome(context);
});
},
title: Text("add_user".i18n),
leading: const Icon(FeatherIcons.userPlus),
),
PanelButton(
onPressed: () async {
String? userId = user.id;
if (userId == null) return;
// Delete User
user.removeUser(userId);
await Provider.of<DatabaseProvider>(context, listen: false).store.removeUser(userId);
// If no other Users left, go back to LoginScreen
if (user.getUsers().isNotEmpty) {
user.setUser(user.getUsers().first.id);
restore().then((_) => user.setUser(user.getUsers().first.id));
} else {
Navigator.of(context).pushNamedAndRemoveUntil("login", (_) => false);
}
},
title: Text("log_out".i18n),
leading: Icon(FeatherIcons.logOut, color: AppColors.of(context).red),
),
],
),
),
),
// Updates
if (updateProvider.available)
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
child: PanelButton(
onPressed: () => _openUpdates(context),
title: Text("update_available".i18n),
leading: const Icon(FeatherIcons.download),
trailing: Text(
updateProvider.releases.first.tag,
style: TextStyle(
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.secondary,
),
),
),
),
),
// const Padding(
// padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
// child: PremiumBannerButton(),
// ),
if (!context.watch<PremiumProvider>().hasPremium)
const ClipRect(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12.0),
child: PremiumButton(),
),
)
else
const Padding(
padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: ActiveSponsorCard(),
),
// General Settings
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
title: Text("general".i18n),
child: Column(
children: [
PanelButton(
onPressed: () {
SettingsHelper.language(context);
setState(() {});
},
title: Text("language".i18n),
leading: const Icon(FeatherIcons.globe),
trailing: Text(languageText),
),
PanelButton(
onPressed: () {
SettingsHelper.startPage(context);
setState(() {});
},
title: Text("startpage".i18n),
leading: const Icon(FeatherIcons.play),
trailing: Text(startPageTitle.capital()),
),
PanelButton(
onPressed: () {
SettingsHelper.rounding(context);
setState(() {});
},
title: Text("rounding".i18n),
leading: const Icon(FeatherIcons.gitCommit),
trailing: Text((settings.rounding / 10).toStringAsFixed(1)),
),
PanelButton(
onPressed: () {
SettingsHelper.vibrate(context);
setState(() {});
},
title: Text("vibrate".i18n),
leading: const Icon(FeatherIcons.radio),
trailing: Text(vibrateTitle),
),
PanelButton(
padding: const EdgeInsets.only(left: 14.0),
onPressed: () {
SettingsHelper.bellDelay(context);
setState(() {});
},
title: Text(
"bell_delay".i18n,
style: TextStyle(color: AppColors.of(context).text.withOpacity(settings.bellDelayEnabled ? 1.0 : .5)),
),
leading: settings.bellDelayEnabled
? const Icon(FeatherIcons.bell)
: Icon(FeatherIcons.bellOff, color: AppColors.of(context).text.withOpacity(.25)),
trailingDivider: true,
trailing: Switch(
onChanged: (v) => settings.update(bellDelayEnabled: v),
value: settings.bellDelayEnabled,
activeColor: Theme.of(context).colorScheme.secondary,
),
),
],
),
),
),
if (kDebugMode)
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
title: const Text("Debug"),
child: Column(
children: [
PanelButton(
title: const Text("Subject Icon Gallery"),
leading: const Icon(CupertinoIcons.rectangle_3_offgrid_fill),
trailing: const Icon(Icons.arrow_forward),
onPressed: () {
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute(builder: (context) => const SubjectIconGallery()),
);
},
)
],
),
),
),
// Secret Settings
if (__ss)
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
title: Text("secret".i18n),
child: Column(
children: [
// Good student mode
Material(
type: MaterialType.transparency,
child: SwitchListTile(
contentPadding: const EdgeInsets.only(left: 12.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
title: Text("goodstudent".i18n, style: const TextStyle(fontWeight: FontWeight.w500)),
onChanged: (v) {
if (v) {
showDialog(
context: context,
builder: (context) => WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
title: Text("attention".i18n),
content: Text("goodstudent_disclaimer".i18n),
actions: [
ActionButton(
label: "understand".i18n,
onTap: () {
Navigator.of(context).pop();
settings.update(goodStudent: v);
Provider.of<GradeProvider>(context, listen: false).convertBySettings();
})
],
),
),
);
} else {
settings.update(goodStudent: v);
Provider.of<GradeProvider>(context, listen: false).convertBySettings();
}
},
value: settings.goodStudent,
activeColor: Theme.of(context).colorScheme.secondary,
),
),
// Presentation mode
Material(
type: MaterialType.transparency,
child: SwitchListTile(
contentPadding: const EdgeInsets.only(left: 12.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
title: const Text("Presentation Mode", style: TextStyle(fontWeight: FontWeight.w500)),
onChanged: (v) => settings.update(presentationMode: v),
value: settings.presentationMode,
activeColor: Theme.of(context).colorScheme.secondary,
),
),
],
),
),
),
// Theme Settings
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
title: Text("appearance".i18n),
child: Column(
children: [
PanelButton(
onPressed: () {
SettingsHelper.theme(context);
setState(() {});
},
title: Text("theme".i18n),
leading: const Icon(FeatherIcons.sun),
trailing: Text(themeModeText),
),
PanelButton(
onPressed: () async {
await _hideContainersController.forward();
SettingsHelper.accentColor(context);
setState(() {});
_hideContainersController.reset();
},
title: Text("color".i18n),
leading: const Icon(FeatherIcons.droplet),
trailing: Container(
width: 12.0,
height: 12.0,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary,
shape: BoxShape.circle,
),
),
),
PanelButton(
onPressed: () {
SettingsHelper.gradeColors(context);
setState(() {});
},
title: Text("grade_colors".i18n),
leading: const Icon(FeatherIcons.star),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(
5,
(i) => Container(
margin: const EdgeInsets.only(left: 2.0),
width: 12.0,
height: 12.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: settings.gradeColors[i],
),
),
),
),
),
Material(
type: MaterialType.transparency,
child: SwitchListTile(
contentPadding: const EdgeInsets.only(left: 12.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
title: Row(
children: [
Icon(
FeatherIcons.barChart,
color: settings.graphClassAvg ? Theme.of(context).colorScheme.secondary : AppColors.of(context).text.withOpacity(.25),
),
const SizedBox(width: 24.0),
Expanded(
child: Text(
"graph_class_avg".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
color: AppColors.of(context).text.withOpacity(settings.graphClassAvg ? 1.0 : .5),
),
),
),
],
),
onChanged: (v) => settings.update(graphClassAvg: v),
value: settings.graphClassAvg,
activeColor: Theme.of(context).colorScheme.secondary,
),
),
const PremiumIconPackSelector(),
],
),
),
),
// Notifications
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
title: Text("notifications".i18n),
child: Material(
type: MaterialType.transparency,
child: SwitchListTile(
contentPadding: const EdgeInsets.only(left: 12.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
title: Row(
children: [
Icon(
Icons.newspaper_outlined,
color: settings.newsEnabled ? Theme.of(context).colorScheme.secondary : AppColors.of(context).text.withOpacity(.25),
),
const SizedBox(width: 24.0),
Expanded(
child: Text(
"news".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
color: AppColors.of(context).text.withOpacity(settings.newsEnabled ? 1.0 : .5),
),
),
),
],
),
onChanged: (v) => settings.update(newsEnabled: v),
value: settings.newsEnabled,
activeColor: Theme.of(context).colorScheme.secondary,
),
),
),
),
// Extras
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
title: Text("extras".i18n),
child: Column(
children: [
Material(
type: MaterialType.transparency,
child: SwitchListTile(
contentPadding: const EdgeInsets.only(left: 12.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
title: Row(
children: [
Icon(
FeatherIcons.gift,
color: settings.gradeOpeningFun ? Theme.of(context).colorScheme.secondary : AppColors.of(context).text.withOpacity(.25),
),
const SizedBox(width: 24.0),
Expanded(
child: Text(
"surprise_grades".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
color: AppColors.of(context).text.withOpacity(settings.gradeOpeningFun ? 1.0 : .5),
),
),
),
],
),
onChanged: (v) => settings.update(gradeOpeningFun: v),
value: settings.gradeOpeningFun,
activeColor: Theme.of(context).colorScheme.secondary,
),
),
MenuRenamedSubjects(
settings: settings,
),
],
),
),
),
// About
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
title: Text("about".i18n),
child: Column(children: [
PanelButton(
leading: const Icon(FeatherIcons.atSign),
title: const Text("Discord"),
onPressed: () => launchUrl(Uri.parse("https://filcnaplo.hu/discord"), mode: LaunchMode.externalApplication),
),
PanelButton(
leading: const Icon(FeatherIcons.globe),
title: const Text("www.filcnaplo.hu"),
onPressed: () => launchUrl(Uri.parse("https://filcnaplo.hu"), mode: LaunchMode.externalApplication),
),
PanelButton(
leading: const Icon(FeatherIcons.github),
title: const Text("Github"),
onPressed: () => launchUrl(Uri.parse("https://github.com/filc"), mode: LaunchMode.externalApplication),
),
PanelButton(
leading: const Icon(FeatherIcons.mail),
title: Text("news".i18n),
onPressed: () => _openNews(context),
),
PanelButton(
leading: const Icon(FeatherIcons.lock),
title: Text("privacy".i18n),
onPressed: () => _openPrivacy(context),
),
PanelButton(
leading: const Icon(FeatherIcons.award),
title: Text("licenses".i18n),
onPressed: () => showLicensePage(context: context),
),
Tooltip(
message: "data_collected".i18n,
padding: const EdgeInsets.all(4.0),
textStyle: TextStyle(fontWeight: FontWeight.w500, color: AppColors.of(context).text),
decoration: BoxDecoration(color: Theme.of(context).colorScheme.background),
child: Material(
type: MaterialType.transparency,
child: SwitchListTile(
contentPadding: const EdgeInsets.only(left: 12.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
secondary: Icon(
FeatherIcons.barChart2,
color: settings.xFilcId != "none" ? Theme.of(context).colorScheme.secondary : AppColors.of(context).text.withOpacity(.25),
),
title: Text(
"Analytics".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
color: AppColors.of(context).text.withOpacity(settings.xFilcId != "none" ? 1.0 : .5),
),
),
subtitle: Text(
"Anonymous Usage Analytics".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(settings.xFilcId != "none" ? .5 : .2),
),
),
onChanged: (v) {
String newId;
if (v == false) {
newId = "none";
} else if (settings.xFilcId == "none") {
newId = SettingsProvider.defaultSettings().xFilcId;
} else {
newId = settings.xFilcId;
}
settings.update(xFilcId: newId);
},
value: settings.xFilcId != "none",
activeColor: Theme.of(context).colorScheme.secondary,
),
),
),
]),
),
),
if (settings.developerMode)
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: Panel(
title: const Text("Developer Settings"),
child: Column(
children: [
Material(
type: MaterialType.transparency,
child: SwitchListTile(
contentPadding: const EdgeInsets.only(left: 12.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
title: const Text("Developer Mode", style: TextStyle(fontWeight: FontWeight.w500)),
onChanged: (v) => settings.update(developerMode: false),
value: settings.developerMode,
activeColor: Theme.of(context).colorScheme.secondary,
),
),
PanelButton(
leading: const Icon(FeatherIcons.copy),
title: const Text("Copy JWT"),
onPressed: () => Clipboard.setData(ClipboardData(text: Provider.of<KretaClient>(context, listen: false).accessToken)),
),
if (Provider.of<PremiumProvider>(context, listen: false).hasPremium)
PanelButton(
leading: const Icon(FeatherIcons.key),
title: const Text("Remove Premium"),
onPressed: () {
Provider.of<PremiumProvider>(context, listen: false).activate(removePremium: true);
settings.update(accentColor: AccentColor.filc, store: true);
Provider.of<ThemeModeObserver>(context, listen: false).changeTheme(settings.theme);
},
),
],
),
),
),
SafeArea(
top: false,
child: Center(
child: GestureDetector(
child: const Panel(title: Text("v" + String.fromEnvironment("APPVER", defaultValue: "?"))),
onTap: () {
if (devmodeCountdown > 0) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
duration: const Duration(milliseconds: 200),
content: Text("You are $devmodeCountdown taps away from Developer Mode."),
));
setState(() => devmodeCountdown--);
} else if (devmodeCountdown == 0) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text("Developer Mode successfully activated."),
));
settings.update(developerMode: true);
setState(() => devmodeCountdown--);
}
},
),
),
),
],
),
),
);
}
void _openNews(BuildContext context) =>
Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute(builder: (context) => const NewsScreen()));
void _openUpdates(BuildContext context) => UpdateView.show(updateProvider.releases.first, context: context);
void _openPrivacy(BuildContext context) => PrivacyView.show(context);
}

View File

@@ -0,0 +1,194 @@
import 'package:i18n_extension/i18n_extension.dart';
extension SettingsLocalization on String {
static final _t = Translations.byLocale("hu_hu") +
{
"en_en": {
"personal_details": "Personal Details",
"open_dkt": "Open DKT",
"edit_nickname": "Edit Nickname",
"edit_profile_picture": "Edit Profile Picture",
"remove_profile_picture": "Remove Profile Picture",
"select_profile_picture": "to select a picture",
"click_here": "Click here",
"light": "Light",
"dark": "Dark",
"system": "System",
"add_user": "Add User",
"log_out": "Log Out",
"update_available": "Update Available",
"general": "General",
"language": "Language",
"startpage": "Startpage",
"rounding": "Rounding",
"appearance": "Appearance",
"theme": "Theme",
"color": "Color",
"grade_colors": "Grade Colors",
"notifications": "Notifications",
"news": "News",
"extras": "Extras",
"about": "About",
"supporters": "Supporters",
"privacy": "Privacy Policy",
"licenses": "Licenses",
"vibrate": "Vibration",
"voff": "Off",
"vlight": "Light",
"vmedium": "Medium",
"vstrong": "Strong",
"cancel": "Cancel",
"done": "Done",
"reset": "Reset",
"open": "Open",
"data_collected": "Data collected: Platform (eg. Android), App version (eg. 3.0.0), Unique Install Identifier",
"Analytics": "Analytics",
"Anonymous Usage Analytics": "Anonymous Usage Analytics",
"graph_class_avg": "Class average on graph",
"goodstudent": "Good student mode",
"attention": "Attention!",
"goodstudent_disclaimer":
"Filc Napló® Informatikai Zrt. can not be held liable for the usage of this feature.\n\n(if your mother beats you up because you showed her fake grades, you can only blame yourself for it)",
"understand": "I understand",
"secret": "Secret Settings",
"bell_delay": "Bell Delay",
"delay": "Delay",
"hurry": "Hurry",
"sync": "Synchronize",
"sync_help": "Press the Synchronize button when the bell rings.",
"surprise_grades": "Surprise Grades",
"icon_pack": "Icon Pack",
"change_username": "Set a nickname",
"Accent Color": "Accent Color",
"Background Color": "Background Color",
"Highlight Color": "Highlight Color",
"Adaptive Theme": "Adaptive Theme",
},
"hu_hu": {
"personal_details": "Személyes információk",
"open_dkt": "DKT megnyitása",
"edit_nickname": "Becenév szerkesztése",
"edit_profile_picture": "Profil-kép szerkesztése",
"remove_profile_picture": "Profil-kép törlése",
"select_profile_picture": "a kép kiválasztásához",
"click_here": "Kattints ide",
"light": "Világos",
"dark": "Sötét",
"system": "Rendszer",
"add_user": "Felhasználó hozzáadása",
"log_out": "Kijelentkezés",
"update_available": "Frissítés elérhető",
"general": "Általános",
"language": "Nyelv",
"startpage": "Kezdőlap",
"rounding": "Kerekítés",
"appearance": "Kinézet",
"theme": "Téma",
"color": "Színek",
"grade_colors": "Jegyek színei",
"notifications": "Értesítések",
"news": "Hírek",
"extras": "Extrák",
"about": "Névjegy",
"supporters": "Támogatók",
"privacy": "Adatvédelmi irányelvek",
"licenses": "Licenszek",
"vibrate": "Rezgés",
"voff": "Kikapcsolás",
"vlight": "Alacsony",
"vmedium": "Közepes",
"vstrong": "Erős",
"cancel": "Mégsem",
"done": "Kész",
"reset": "Visszaállítás",
"open": "Megnyitás",
"data_collected": "Gyűjtött adat: Platform (pl. Android), App verzió (pl. 3.0.0), Egyedi telepítési azonosító",
"Analytics": "Analitika",
"Anonymous Usage Analytics": "Névtelen használati analitika",
"graph_class_avg": "Osztályátlag a grafikonon",
"goodstudent": "Jó tanuló mód",
"attention": "Figyelem!",
"goodstudent_disclaimer":
"A Filc Napló® Informatikai Zrt. minden felelősséget elhárít a funkció használatával kapcsolatban.\n\n(Értsd: ha az anyád megver, mert megtévesztő ábrákat mutattál neki, azért csakis magadadat hibáztathatod.)",
"understand": "Értem",
"secret": "Titkos Beállítások",
"bell_delay": "Csengő eltolódása",
"delay": "Késleltetés",
"hurry": "Siettetés",
"sync": "Szinkronizálás",
"sync_help": "Csengetéskor nyomd meg a Szinkronizálás gombot.",
"surprise_grades": "Meglepetés jegyek",
"icon_pack": "Ikon séma",
"change_username": "Becenév beállítása",
"Accent Color": "Egyedi szín",
"Background Color": "Háttér színe",
"Highlight Color": "Panelek színe",
"Adaptive Theme": "Adaptív téma",
},
"de_de": {
"personal_details": "Persönliche Angaben",
"open_dkt": "Öffnen DKT",
"edit_nickname": "Spitznamen bearbeiten",
"edit_profile_picture": "Profilbild bearbeiten",
"remove_profile_picture": "Profilbild entfernen",
"select_profile_picture": "um ein Bild auszuwählen",
"click_here": "Klick hier",
"light": "Licht",
"dark": "Dunkel",
"system": "System",
"add_user": "Benutzer hinzufügen",
"log_out": "Abmelden",
"update_available": "Update verfügbar",
"general": "Allgemein",
"language": "Sprache",
"startpage": "Startseite",
"rounding": "Rundung",
"appearance": "Erscheinungsbild",
"theme": "Thema",
"color": "Farbe",
"grade_colors": "Grad Farben",
"notifications": "Benachrichtigungen",
"news": "Nachrichten",
"extras": "Extras",
"about": "Informationen",
"supporters": "Unterstützer",
"privacy": "Datenschutzbestimmungen",
"licenses": "Lizenzen",
"vibrate": "Vibration",
"voff": "Aus",
"vlight": "Leicht",
"vmedium": "Mittel",
"vstrong": "Stark",
"cancel": "Abbrechen",
"done": "Fertig",
"reset": "Zurücksetzen",
"open": "Öffnen",
"data_collected": "Erhobene Daten: Plattform (z.B. Android), App version (z.B. 3.0.0), Eindeutige Installationskennung",
"Analytics": "Analytik",
"Anonymous Usage Analytics": "Anonyme Nutzungsanalyse",
"graph_class_avg": "Klassendurchschnitt in der Grafik",
"goodstudent": "Guter Student Modus",
"attention": "Achtung!",
"goodstudent_disclaimer": "Same in English.",
"understand": "Ich verstehe",
"secret": "Geheime Einstellungen",
"bell_delay": "Klingelverzögerung",
"delay": "Verzögern",
"hurry": "Eile",
"sync": "Synchronisieren",
"sync_help": "Drücken Sie die Sync-Taste, wenn die Glocke läutet.",
"surprise_grades": "Überraschungsnoten",
"icon_pack": "Icon-Pack",
"change_username": "Einen Spitznamen festlegen",
"Accent Color": "Accent Color",
"Background Color": "Background Color",
"Highlight Color": "Highlight Color",
"Adaptive Theme": "Adaptive Theme",
},
};
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);
}