Rename everything filcnaplo-related to refilc
This commit is contained in:
39
refilc_premium/lib/ui/mobile/settings/icon_pack.dart
Normal file
39
refilc_premium/lib/ui/mobile/settings/icon_pack.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
import 'package:refilc/models/settings.dart';
|
||||
import 'package:refilc_mobile_ui/common/panel/panel_button.dart';
|
||||
import 'package:refilc_mobile_ui/screens/settings/settings_helper.dart';
|
||||
import 'package:refilc_premium/models/premium_scopes.dart';
|
||||
import 'package:refilc_premium/providers/premium_provider.dart';
|
||||
import 'package:refilc_premium/ui/mobile/premium/upsell.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:refilc_mobile_ui/screens/settings/settings_screen.i18n.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:refilc/utils/format.dart';
|
||||
|
||||
class PremiumIconPackSelector extends StatelessWidget {
|
||||
const PremiumIconPackSelector({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settings = Provider.of<SettingsProvider>(context);
|
||||
|
||||
return PanelButton(
|
||||
onPressed: () {
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||
.hasScope(PremiumScopes.customIcons)) {
|
||||
PremiumLockedFeatureUpsell.show(
|
||||
context: context, feature: PremiumFeature.iconpack);
|
||||
return;
|
||||
}
|
||||
|
||||
SettingsHelper.iconPack(context);
|
||||
},
|
||||
title: Text("icon_pack".i18n),
|
||||
leading: const Icon(FeatherIcons.grid),
|
||||
trailing: Text(
|
||||
settings.iconPack.name.capital(),
|
||||
style: const TextStyle(fontSize: 14.0),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
72
refilc_premium/lib/ui/mobile/settings/modify_names.i18n.dart
Normal file
72
refilc_premium/lib/ui/mobile/settings/modify_names.i18n.dart
Normal file
@@ -0,0 +1,72 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension SettingsLocalization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
// subject rename
|
||||
"renamed_subjects": "Renamed Subjects",
|
||||
"rename_subjects": "Rename Subjects",
|
||||
"rename_subject": "Rename Subject",
|
||||
"select_subject": "Select Subject",
|
||||
"modified_name": "Modified Name",
|
||||
"modify_subjects": "Modify Subjects",
|
||||
"cancel": "Cancel",
|
||||
"done": "Done",
|
||||
"rename_new_subject": "Rename New Subject",
|
||||
"italics_toggle": "Italic Font",
|
||||
// teacher rename
|
||||
"renamed_teachers": "Renamed Teachers",
|
||||
"rename_teachers": "Rename Teachers",
|
||||
"rename_teacher": "Rename Teacher",
|
||||
"select_teacher": "Select Teacher",
|
||||
"modify_teachers": "Modify Teachers",
|
||||
"rename_new_teacher": "Rename New Teacher",
|
||||
},
|
||||
"hu_hu": {
|
||||
// subject rename
|
||||
"renamed_subjects": "Átnevezett Tantárgyaid",
|
||||
"rename_subjects": "Tantárgyak átnevezése",
|
||||
"rename_subject": "Tantárgy átnevezése",
|
||||
"select_subject": "Válassz tantárgyat",
|
||||
"modified_name": "Módosított név",
|
||||
"modify_subjects": "Tantárgyak módosítása",
|
||||
"cancel": "Mégse",
|
||||
"done": "Kész",
|
||||
"rename_new_subject": "Új tantárgy átnevezése",
|
||||
"italics_toggle": "Dőlt betűs megjelenítés",
|
||||
// teacher rename
|
||||
"renamed_teachers": "Átnevezett tanáraid",
|
||||
"rename_teachers": "Tanárok átnevezése",
|
||||
"rename_teacher": "Tanár átnevezése",
|
||||
"select_teacher": "Válassz tanárt",
|
||||
"modify_teachers": "Tanárok módosítása",
|
||||
"rename_new_teacher": "Új tanár átnevezése",
|
||||
},
|
||||
"de_de": {
|
||||
// subject rename
|
||||
"renamed_subjects": "Umbenannte Fächer",
|
||||
"rename_subjects": "Fächer umbenennen",
|
||||
"rename_subject": "Fach umbenennen",
|
||||
"select_subject": "Fach auswählen",
|
||||
"modified_name": "Geänderter Name",
|
||||
"modify_subjects": "Fächer ändern",
|
||||
"cancel": "Abbrechen",
|
||||
"done": "Erledigt",
|
||||
"rename_new_subject": "Neues Fach umbenennen",
|
||||
"italics_toggle": "Kursivschrift umschalten",
|
||||
// teacher rename
|
||||
"renamed_teachers": "Renamed Teachers",
|
||||
"rename_teachers": "Rename Teachers",
|
||||
"rename_teacher": "Rename Teacher",
|
||||
"select_teacher": "Select Teacher",
|
||||
"modify_teachers": "Modify Teachers",
|
||||
"rename_new_teacher": "Rename New Teacher",
|
||||
},
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
438
refilc_premium/lib/ui/mobile/settings/modify_subject_names.dart
Normal file
438
refilc_premium/lib/ui/mobile/settings/modify_subject_names.dart
Normal file
@@ -0,0 +1,438 @@
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:refilc/api/providers/database_provider.dart';
|
||||
import 'package:refilc/api/providers/user_provider.dart';
|
||||
import 'package:refilc/helpers/subject.dart';
|
||||
import 'package:refilc/models/settings.dart';
|
||||
import 'package:refilc/theme/colors/colors.dart';
|
||||
import 'package:refilc/utils/format.dart';
|
||||
import 'package:refilc_kreta_api/models/subject.dart';
|
||||
import 'package:refilc_kreta_api/providers/absence_provider.dart';
|
||||
import 'package:refilc_kreta_api/providers/grade_provider.dart';
|
||||
import 'package:refilc_kreta_api/providers/timetable_provider.dart';
|
||||
import 'package:refilc_mobile_ui/common/panel/panel.dart';
|
||||
import 'package:refilc_mobile_ui/common/panel/panel_button.dart';
|
||||
import 'package:refilc_premium/models/premium_scopes.dart';
|
||||
import 'package:refilc_premium/providers/premium_provider.dart';
|
||||
import 'package:refilc_premium/ui/mobile/premium/upsell.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'modify_names.i18n.dart';
|
||||
|
||||
class MenuRenamedSubjects extends StatelessWidget {
|
||||
const MenuRenamedSubjects({Key? key, required this.settings})
|
||||
: super(key: key);
|
||||
|
||||
final SettingsProvider settings;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PanelButton(
|
||||
padding: const EdgeInsets.only(left: 14.0),
|
||||
onPressed: () {
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||
.hasScope(PremiumScopes.renameSubjects)) {
|
||||
PremiumLockedFeatureUpsell.show(
|
||||
context: context, feature: PremiumFeature.subjectrename);
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.of(context, rootNavigator: true).push(
|
||||
CupertinoPageRoute(builder: (context) => const ModifySubjectNames()),
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
"rename_subjects".i18n,
|
||||
style: TextStyle(
|
||||
color: AppColors.of(context)
|
||||
.text
|
||||
.withOpacity(settings.renamedSubjectsEnabled ? 1.0 : .5)),
|
||||
),
|
||||
leading: settings.renamedSubjectsEnabled
|
||||
? const Icon(FeatherIcons.penTool)
|
||||
: Icon(FeatherIcons.penTool,
|
||||
color: AppColors.of(context).text.withOpacity(.25)),
|
||||
trailingDivider: true,
|
||||
trailing: Switch(
|
||||
onChanged: (v) async {
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||
.hasScope(PremiumScopes.renameSubjects)) {
|
||||
PremiumLockedFeatureUpsell.show(
|
||||
context: context, feature: PremiumFeature.subjectrename);
|
||||
return;
|
||||
}
|
||||
|
||||
settings.update(renamedSubjectsEnabled: v);
|
||||
await Provider.of<GradeProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<TimetableProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<AbsenceProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
},
|
||||
value: settings.renamedSubjectsEnabled,
|
||||
activeColor: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ModifySubjectNames extends StatefulWidget {
|
||||
const ModifySubjectNames({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ModifySubjectNames> createState() => _ModifySubjectNamesState();
|
||||
}
|
||||
|
||||
class _ModifySubjectNamesState extends State<ModifySubjectNames> {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
final _subjectName = TextEditingController();
|
||||
String? selectedSubjectId;
|
||||
|
||||
late List<Subject> subjects;
|
||||
late UserProvider user;
|
||||
late DatabaseProvider dbProvider;
|
||||
late SettingsProvider settings;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
subjects = Provider.of<GradeProvider>(context, listen: false)
|
||||
.grades
|
||||
.map((e) => e.subject)
|
||||
.toSet()
|
||||
.toList()
|
||||
..sort((a, b) => a.name.compareTo(b.name));
|
||||
user = Provider.of<UserProvider>(context, listen: false);
|
||||
dbProvider = Provider.of<DatabaseProvider>(context, listen: false);
|
||||
}
|
||||
|
||||
Future<Map<String, String>> fetchRenamedSubjects() async {
|
||||
return await dbProvider.userQuery.renamedSubjects(userId: user.id!);
|
||||
}
|
||||
|
||||
void showRenameDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(builder: (context, setS) {
|
||||
return AlertDialog(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14.0))),
|
||||
title: Text("rename_subject".i18n),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DropdownButton2(
|
||||
items: subjects
|
||||
.map((item) => DropdownMenuItem<String>(
|
||||
value: item.id,
|
||||
child: Text(
|
||||
item.name,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.of(context).text,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
onChanged: (String? v) async {
|
||||
final renamedSubs = await fetchRenamedSubjects();
|
||||
|
||||
setS(() {
|
||||
selectedSubjectId = v;
|
||||
|
||||
if (renamedSubs.containsKey(selectedSubjectId)) {
|
||||
_subjectName.text = renamedSubs[selectedSubjectId]!;
|
||||
} else {
|
||||
_subjectName.text = "";
|
||||
}
|
||||
});
|
||||
},
|
||||
iconSize: 14,
|
||||
iconEnabledColor: AppColors.of(context).text,
|
||||
iconDisabledColor: AppColors.of(context).text,
|
||||
underline: const SizedBox(),
|
||||
itemHeight: 40,
|
||||
itemPadding: const EdgeInsets.only(left: 14, right: 14),
|
||||
buttonWidth: 50,
|
||||
dropdownWidth: 300,
|
||||
dropdownPadding: null,
|
||||
buttonDecoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
dropdownDecoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
dropdownElevation: 8,
|
||||
scrollbarRadius: const Radius.circular(40),
|
||||
scrollbarThickness: 6,
|
||||
scrollbarAlwaysShow: true,
|
||||
offset: const Offset(-10, -10),
|
||||
buttonSplashColor: Colors.transparent,
|
||||
customButton: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12.0, horizontal: 8.0),
|
||||
child: Text(
|
||||
selectedSubjectId == null
|
||||
? "select_subject".i18n
|
||||
: subjects
|
||||
.firstWhere(
|
||||
(element) => element.id == selectedSubjectId)
|
||||
.name,
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.of(context).text.withOpacity(0.75)),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Icon(FeatherIcons.arrowDown, size: 32),
|
||||
),
|
||||
TextField(
|
||||
controller: _subjectName,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderSide:
|
||||
const BorderSide(color: Colors.grey, width: 1.5),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide:
|
||||
const BorderSide(color: Colors.grey, width: 1.5),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
hintText: "modified_name".i18n,
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(
|
||||
FeatherIcons.x,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_subjectName.text = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
"cancel".i18n,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).maybePop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(
|
||||
"done".i18n,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (selectedSubjectId != null) {
|
||||
final renamedSubs = await fetchRenamedSubjects();
|
||||
|
||||
renamedSubs[selectedSubjectId!] = _subjectName.text;
|
||||
await dbProvider.userStore
|
||||
.storeRenamedSubjects(renamedSubs, userId: user.id!);
|
||||
await Provider.of<GradeProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<TimetableProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<AbsenceProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
}
|
||||
Navigator.of(context).pop(true);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
).then((val) {
|
||||
_subjectName.text = "";
|
||||
selectedSubjectId = null;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
settings = Provider.of<SettingsProvider>(context);
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
appBar: AppBar(
|
||||
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
leading: BackButton(color: AppColors.of(context).text),
|
||||
title: Text(
|
||||
"modify_subjects".i18n,
|
||||
style: TextStyle(color: AppColors.of(context).text),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Panel(
|
||||
child: SwitchListTile(
|
||||
title: Text("italics_toggle".i18n),
|
||||
onChanged: (value) =>
|
||||
settings.update(renamedSubjectsItalics: value),
|
||||
value: settings.renamedSubjectsItalics,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
InkWell(
|
||||
onTap: showRenameDialog,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 18.0, horizontal: 12.0),
|
||||
child: Center(
|
||||
child: Text(
|
||||
"rename_new_subject".i18n,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
color: AppColors.of(context).text.withOpacity(.85),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
FutureBuilder<Map<String, String>>(
|
||||
future: fetchRenamedSubjects(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return Panel(
|
||||
title: Text("renamed_subjects".i18n),
|
||||
child: Column(
|
||||
children: snapshot.data!.keys.map(
|
||||
(key) {
|
||||
Subject? subject = subjects
|
||||
.firstWhere((element) => key == element.id);
|
||||
String renameTo = snapshot.data![key]!;
|
||||
return RenamedSubjectItem(
|
||||
subject: subject,
|
||||
renamedTo: renameTo,
|
||||
modifyCallback: () {
|
||||
setState(() {
|
||||
selectedSubjectId = subject.id;
|
||||
_subjectName.text = renameTo;
|
||||
});
|
||||
showRenameDialog();
|
||||
},
|
||||
removeCallback: () {
|
||||
setState(() {
|
||||
Map<String, String> subs =
|
||||
Map.from(snapshot.data!);
|
||||
subs.remove(key);
|
||||
dbProvider.userStore.storeRenamedSubjects(
|
||||
subs,
|
||||
userId: user.id!);
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class RenamedSubjectItem extends StatelessWidget {
|
||||
const RenamedSubjectItem({
|
||||
Key? key,
|
||||
required this.subject,
|
||||
required this.renamedTo,
|
||||
required this.modifyCallback,
|
||||
required this.removeCallback,
|
||||
}) : super(key: key);
|
||||
|
||||
final Subject subject;
|
||||
final String renamedTo;
|
||||
final void Function() modifyCallback;
|
||||
final void Function() removeCallback;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
minLeadingWidth: 32.0,
|
||||
dense: true,
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onTap: () {},
|
||||
leading: Icon(
|
||||
SubjectIcon.resolveVariant(subject: subject, context: context),
|
||||
color: AppColors.of(context).text.withOpacity(.75)),
|
||||
title: InkWell(
|
||||
onTap: modifyCallback,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
subject.name.capital(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
color: AppColors.of(context).text.withOpacity(.75)),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
renamedTo,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing: InkWell(
|
||||
onTap: removeCallback,
|
||||
child: Icon(FeatherIcons.trash,
|
||||
color: AppColors.of(context).red.withOpacity(.75)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
437
refilc_premium/lib/ui/mobile/settings/modify_teacher_names.dart
Normal file
437
refilc_premium/lib/ui/mobile/settings/modify_teacher_names.dart
Normal file
@@ -0,0 +1,437 @@
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:refilc/api/providers/database_provider.dart';
|
||||
import 'package:refilc/api/providers/user_provider.dart';
|
||||
import 'package:refilc/models/settings.dart';
|
||||
import 'package:refilc/theme/colors/colors.dart';
|
||||
import 'package:refilc/utils/format.dart';
|
||||
import 'package:refilc_kreta_api/models/teacher.dart';
|
||||
import 'package:refilc_kreta_api/providers/absence_provider.dart';
|
||||
import 'package:refilc_kreta_api/providers/grade_provider.dart';
|
||||
import 'package:refilc_kreta_api/providers/timetable_provider.dart';
|
||||
import 'package:refilc_mobile_ui/common/panel/panel.dart';
|
||||
import 'package:refilc_mobile_ui/common/panel/panel_button.dart';
|
||||
import 'package:refilc_premium/models/premium_scopes.dart';
|
||||
import 'package:refilc_premium/providers/premium_provider.dart';
|
||||
import 'package:refilc_premium/ui/mobile/premium/upsell.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'modify_names.i18n.dart';
|
||||
|
||||
class MenuRenamedTeachers extends StatelessWidget {
|
||||
const MenuRenamedTeachers({Key? key, required this.settings})
|
||||
: super(key: key);
|
||||
|
||||
final SettingsProvider settings;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PanelButton(
|
||||
padding: const EdgeInsets.only(left: 14.0),
|
||||
onPressed: () {
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||
.hasScope(PremiumScopes.renameTeachers)) {
|
||||
PremiumLockedFeatureUpsell.show(
|
||||
context: context, feature: PremiumFeature.teacherrename);
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.of(context, rootNavigator: true).push(
|
||||
CupertinoPageRoute(builder: (context) => const ModifyTeacherNames()),
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
"rename_teachers".i18n,
|
||||
style: TextStyle(
|
||||
color: AppColors.of(context)
|
||||
.text
|
||||
.withOpacity(settings.renamedTeachersEnabled ? 1.0 : .5)),
|
||||
),
|
||||
leading: settings.renamedTeachersEnabled
|
||||
? const Icon(FeatherIcons.users)
|
||||
: Icon(FeatherIcons.users,
|
||||
color: AppColors.of(context).text.withOpacity(.25)),
|
||||
trailingDivider: true,
|
||||
trailing: Switch(
|
||||
onChanged: (v) async {
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||
.hasScope(PremiumScopes.renameTeachers)) {
|
||||
PremiumLockedFeatureUpsell.show(
|
||||
context: context, feature: PremiumFeature.teacherrename);
|
||||
return;
|
||||
}
|
||||
|
||||
settings.update(renamedTeachersEnabled: v);
|
||||
await Provider.of<GradeProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<TimetableProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<AbsenceProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
},
|
||||
value: settings.renamedTeachersEnabled,
|
||||
activeColor: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ModifyTeacherNames extends StatefulWidget {
|
||||
const ModifyTeacherNames({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ModifyTeacherNames> createState() => _ModifyTeacherNamesState();
|
||||
}
|
||||
|
||||
class _ModifyTeacherNamesState extends State<ModifyTeacherNames> {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
final _teacherName = TextEditingController();
|
||||
String? selectedTeacherId;
|
||||
|
||||
late List<Teacher> teachers;
|
||||
late UserProvider user;
|
||||
late DatabaseProvider dbProvider;
|
||||
late SettingsProvider settings;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
teachers = (Provider.of<GradeProvider>(context, listen: false)
|
||||
.grades
|
||||
.map((e) => e.teacher)
|
||||
.toSet()
|
||||
.toList()
|
||||
..sort((a, b) => a.name.compareTo(b.name)));
|
||||
user = Provider.of<UserProvider>(context, listen: false);
|
||||
dbProvider = Provider.of<DatabaseProvider>(context, listen: false);
|
||||
}
|
||||
|
||||
Future<Map<String, String>> fetchRenamedTeachers() async {
|
||||
return await dbProvider.userQuery.renamedTeachers(userId: user.id!);
|
||||
}
|
||||
|
||||
void showRenameDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(builder: (context, setS) {
|
||||
return AlertDialog(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14.0))),
|
||||
title: Text("rename_teacher".i18n),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DropdownButton2(
|
||||
items: teachers
|
||||
.map((item) => DropdownMenuItem<String>(
|
||||
value: item.id,
|
||||
child: Text(
|
||||
item.name,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.of(context).text,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
onChanged: (String? v) async {
|
||||
final renamedSubs = await fetchRenamedTeachers();
|
||||
|
||||
setS(() {
|
||||
selectedTeacherId = v;
|
||||
|
||||
if (renamedSubs.containsKey(selectedTeacherId)) {
|
||||
_teacherName.text = renamedSubs[selectedTeacherId]!;
|
||||
} else {
|
||||
_teacherName.text = "";
|
||||
}
|
||||
});
|
||||
},
|
||||
iconSize: 14,
|
||||
iconEnabledColor: AppColors.of(context).text,
|
||||
iconDisabledColor: AppColors.of(context).text,
|
||||
underline: const SizedBox(),
|
||||
itemHeight: 40,
|
||||
itemPadding: const EdgeInsets.only(left: 14, right: 14),
|
||||
buttonWidth: 50,
|
||||
dropdownWidth: 300,
|
||||
dropdownPadding: null,
|
||||
buttonDecoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
dropdownDecoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
dropdownElevation: 8,
|
||||
scrollbarRadius: const Radius.circular(40),
|
||||
scrollbarThickness: 6,
|
||||
scrollbarAlwaysShow: true,
|
||||
offset: const Offset(-10, -10),
|
||||
buttonSplashColor: Colors.transparent,
|
||||
customButton: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12.0, horizontal: 8.0),
|
||||
child: Text(
|
||||
selectedTeacherId == null
|
||||
? "select_teacher".i18n
|
||||
: teachers
|
||||
.firstWhere(
|
||||
(element) => element.id == selectedTeacherId)
|
||||
.name,
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.of(context).text.withOpacity(0.75)),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Icon(FeatherIcons.arrowDown, size: 32),
|
||||
),
|
||||
TextField(
|
||||
controller: _teacherName,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderSide:
|
||||
const BorderSide(color: Colors.grey, width: 1.5),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide:
|
||||
const BorderSide(color: Colors.grey, width: 1.5),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
hintText: "modified_name".i18n,
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(
|
||||
FeatherIcons.x,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_teacherName.text = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
"cancel".i18n,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).maybePop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(
|
||||
"done".i18n,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (selectedTeacherId != null) {
|
||||
final renamedSubs = await fetchRenamedTeachers();
|
||||
|
||||
renamedSubs[selectedTeacherId!] = _teacherName.text;
|
||||
await dbProvider.userStore
|
||||
.storeRenamedTeachers(renamedSubs, userId: user.id!);
|
||||
await Provider.of<GradeProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<TimetableProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
await Provider.of<AbsenceProvider>(context, listen: false)
|
||||
.convertBySettings();
|
||||
}
|
||||
Navigator.of(context).pop(true);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
).then((val) {
|
||||
_teacherName.text = "";
|
||||
selectedTeacherId = null;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
settings = Provider.of<SettingsProvider>(context);
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
appBar: AppBar(
|
||||
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
leading: BackButton(color: AppColors.of(context).text),
|
||||
title: Text(
|
||||
"modify_teachers".i18n,
|
||||
style: TextStyle(color: AppColors.of(context).text),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Panel(
|
||||
// child: SwitchListTile(
|
||||
|
||||
// title: Text("italics_toggle".i18n),
|
||||
// onChanged: (value) =>
|
||||
// settings.update(renamedTeachersItalics: value),
|
||||
// value: settings.renamedTeachersItalics,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// height: 20,
|
||||
// ),
|
||||
InkWell(
|
||||
onTap: showRenameDialog,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey, width: 2),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 18.0, horizontal: 12.0),
|
||||
child: Center(
|
||||
child: Text(
|
||||
"rename_new_teacher".i18n,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
color: AppColors.of(context).text.withOpacity(.85),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
FutureBuilder<Map<String, String>>(
|
||||
future: fetchRenamedTeachers(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return Panel(
|
||||
title: Text("renamed_teachers".i18n),
|
||||
child: Column(
|
||||
children: snapshot.data!.keys.map(
|
||||
(key) {
|
||||
Teacher? teacher = teachers
|
||||
.firstWhere((element) => key == element.id);
|
||||
String renameTo = snapshot.data![key]!;
|
||||
return RenamedTeacherItem(
|
||||
teacher: teacher,
|
||||
renamedTo: renameTo,
|
||||
modifyCallback: () {
|
||||
setState(() {
|
||||
selectedTeacherId = teacher.id;
|
||||
_teacherName.text = renameTo;
|
||||
});
|
||||
showRenameDialog();
|
||||
},
|
||||
removeCallback: () {
|
||||
setState(() {
|
||||
Map<String, String> subs =
|
||||
Map.from(snapshot.data!);
|
||||
subs.remove(key);
|
||||
dbProvider.userStore.storeRenamedTeachers(
|
||||
subs,
|
||||
userId: user.id!);
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class RenamedTeacherItem extends StatelessWidget {
|
||||
const RenamedTeacherItem({
|
||||
Key? key,
|
||||
required this.teacher,
|
||||
required this.renamedTo,
|
||||
required this.modifyCallback,
|
||||
required this.removeCallback,
|
||||
}) : super(key: key);
|
||||
|
||||
final Teacher teacher;
|
||||
final String renamedTo;
|
||||
final void Function() modifyCallback;
|
||||
final void Function() removeCallback;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
minLeadingWidth: 32.0,
|
||||
dense: true,
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onTap: () {},
|
||||
leading: Icon(FeatherIcons.user,
|
||||
color: AppColors.of(context).text.withOpacity(.75)),
|
||||
title: InkWell(
|
||||
onTap: modifyCallback,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
teacher.name.capital(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
color: AppColors.of(context).text.withOpacity(.75)),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
renamedTo,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing: InkWell(
|
||||
onTap: removeCallback,
|
||||
child: Icon(FeatherIcons.trash,
|
||||
color: AppColors.of(context).red.withOpacity(.75)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
105
refilc_premium/lib/ui/mobile/settings/nickname.dart
Normal file
105
refilc_premium/lib/ui/mobile/settings/nickname.dart
Normal file
@@ -0,0 +1,105 @@
|
||||
import 'package:refilc/api/providers/database_provider.dart';
|
||||
import 'package:refilc/api/providers/user_provider.dart';
|
||||
import 'package:refilc/models/user.dart';
|
||||
import 'package:refilc_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_item.dart';
|
||||
import 'package:refilc_premium/models/premium_scopes.dart';
|
||||
import 'package:refilc_premium/providers/premium_provider.dart';
|
||||
import 'package:refilc_premium/ui/mobile/premium/upsell.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:refilc_mobile_ui/screens/settings/settings_screen.i18n.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class UserMenuNickname extends StatelessWidget {
|
||||
late User u;
|
||||
|
||||
UserMenuNickname(this.u, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BottomSheetMenuItem(
|
||||
onPressed: () {
|
||||
if (!Provider.of<PremiumProvider>(context, listen: false)
|
||||
.hasScope(PremiumScopes.nickname)) {
|
||||
PremiumLockedFeatureUpsell.show(
|
||||
context: context, feature: PremiumFeature.profile);
|
||||
return;
|
||||
}
|
||||
showDialog(
|
||||
context: context, builder: (context) => UserNicknameEditor(u));
|
||||
},
|
||||
icon: const Icon(FeatherIcons.edit2),
|
||||
title: Text("edit_nickname".i18n),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class UserNicknameEditor extends StatefulWidget {
|
||||
late User u;
|
||||
|
||||
UserNicknameEditor(this.u, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<UserNicknameEditor> createState() => _UserNicknameEditorState();
|
||||
}
|
||||
|
||||
class _UserNicknameEditorState extends State<UserNicknameEditor> {
|
||||
final _userName = TextEditingController();
|
||||
late final UserProvider user;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
user = Provider.of<UserProvider>(context, listen: false);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text("change_username".i18n),
|
||||
content: TextField(
|
||||
controller: _userName,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
label: Text(widget.u.name),
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(FeatherIcons.x),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_userName.text = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
"cancel".i18n,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).maybePop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(
|
||||
"done".i18n,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onPressed: () {
|
||||
widget.u.nickname = _userName.text.trim();
|
||||
Provider.of<DatabaseProvider>(context, listen: false)
|
||||
.store
|
||||
.storeUser(widget.u);
|
||||
Provider.of<UserProvider>(context, listen: false).refresh();
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
234
refilc_premium/lib/ui/mobile/settings/profile_pic.dart
Normal file
234
refilc_premium/lib/ui/mobile/settings/profile_pic.dart
Normal file
@@ -0,0 +1,234 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:refilc/api/providers/database_provider.dart';
|
||||
import 'package:refilc/api/providers/user_provider.dart';
|
||||
import 'package:refilc/models/user.dart';
|
||||
import 'package:refilc_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_item.dart';
|
||||
import 'package:refilc_premium/models/premium_scopes.dart';
|
||||
import 'package:refilc_premium/providers/premium_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:refilc_mobile_ui/screens/settings/settings_screen.i18n.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:image_crop/image_crop.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class UserMenuProfilePic extends StatelessWidget {
|
||||
late User u;
|
||||
|
||||
UserMenuProfilePic(this.u, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!Provider.of<PremiumProvider>(context)
|
||||
.hasScope(PremiumScopes.nickname)) {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
return BottomSheetMenuItem(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context, builder: (context) => UserProfilePicEditor(u));
|
||||
},
|
||||
icon: const Icon(FeatherIcons.camera),
|
||||
title: Text("edit_profile_picture".i18n),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class UserProfilePicEditor extends StatefulWidget {
|
||||
late User u;
|
||||
|
||||
UserProfilePicEditor(this.u, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<UserProfilePicEditor> createState() => _UserProfilePicEditorState();
|
||||
}
|
||||
|
||||
class _UserProfilePicEditorState extends State<UserProfilePicEditor> {
|
||||
late final UserProvider user;
|
||||
|
||||
final cropKey = GlobalKey<CropState>();
|
||||
File? _file;
|
||||
File? _sample;
|
||||
File? _lastCropped;
|
||||
|
||||
File? image;
|
||||
Future pickImage() async {
|
||||
try {
|
||||
final image = await ImagePicker().pickImage(source: ImageSource.gallery);
|
||||
if (image == null) return;
|
||||
File imageFile = File(image.path);
|
||||
|
||||
final sample = await ImageCrop.sampleImage(
|
||||
file: imageFile,
|
||||
preferredSize: context.size!.longestSide.ceil(),
|
||||
);
|
||||
|
||||
_sample?.delete();
|
||||
_file?.delete();
|
||||
|
||||
setState(() {
|
||||
_sample = sample;
|
||||
_file = imageFile;
|
||||
});
|
||||
} on PlatformException catch (e) {
|
||||
log('Failed to pick image: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Widget cropImageWidget() {
|
||||
return SizedBox(
|
||||
height: 300,
|
||||
child: Crop.file(
|
||||
_sample!,
|
||||
key: cropKey,
|
||||
aspectRatio: 1.0,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget openImageWidget() {
|
||||
return InkWell(
|
||||
customBorder: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
),
|
||||
onTap: () => pickImage(),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(14.0),
|
||||
),
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(vertical: 32.0, horizontal: 8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"click_here".i18n,
|
||||
style: const TextStyle(
|
||||
fontSize: 22.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"select_profile_picture".i18n,
|
||||
style: const TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _cropImage() async {
|
||||
final scale = cropKey.currentState!.scale;
|
||||
final area = cropKey.currentState!.area;
|
||||
if (area == null || _file == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final sample = await ImageCrop.sampleImage(
|
||||
file: _file!,
|
||||
preferredSize: (2000 / scale).round(),
|
||||
);
|
||||
|
||||
final file = await ImageCrop.cropImage(
|
||||
file: sample,
|
||||
area: area,
|
||||
);
|
||||
|
||||
sample.delete();
|
||||
|
||||
_lastCropped?.delete();
|
||||
_lastCropped = file;
|
||||
|
||||
List<int> imageBytes = await _lastCropped!.readAsBytes();
|
||||
String base64Image = base64Encode(imageBytes);
|
||||
widget.u.picture = base64Image;
|
||||
Provider.of<DatabaseProvider>(context, listen: false)
|
||||
.store
|
||||
.storeUser(widget.u);
|
||||
Provider.of<UserProvider>(context, listen: false).refresh();
|
||||
|
||||
debugPrint('$file');
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
user = Provider.of<UserProvider>(context, listen: false);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_file?.delete();
|
||||
_sample?.delete();
|
||||
_lastCropped?.delete();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14.0))),
|
||||
contentPadding: const EdgeInsets.only(top: 10.0),
|
||||
title: Text("edit_profile_picture".i18n),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
|
||||
child: _sample == null ? openImageWidget() : cropImageWidget(),
|
||||
),
|
||||
if (widget.u.picture != "")
|
||||
TextButton(
|
||||
child: Text(
|
||||
"remove_profile_picture".i18n,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500, color: Colors.red),
|
||||
),
|
||||
onPressed: () {
|
||||
widget.u.picture = "";
|
||||
Provider.of<DatabaseProvider>(context, listen: false)
|
||||
.store
|
||||
.storeUser(widget.u);
|
||||
Provider.of<UserProvider>(context, listen: false).refresh();
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
"cancel".i18n,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).maybePop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(
|
||||
"done".i18n,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
onPressed: () async {
|
||||
await _cropImage();
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
26
refilc_premium/lib/ui/mobile/settings/share_theme.dart
Normal file
26
refilc_premium/lib/ui/mobile/settings/share_theme.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
import 'package:refilc/models/settings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class PremiumShareTheme extends StatefulWidget {
|
||||
const PremiumShareTheme({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PremiumShareTheme> createState() => _PremiumShareThemeState();
|
||||
}
|
||||
|
||||
class _PremiumShareThemeState extends State<PremiumShareTheme>
|
||||
with TickerProviderStateMixin {
|
||||
late final SettingsProvider settingsProvider;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
settingsProvider = Provider.of<SettingsProvider>(context, listen: false);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold();
|
||||
}
|
||||
}
|
||||
896
refilc_premium/lib/ui/mobile/settings/theme.dart
Normal file
896
refilc_premium/lib/ui/mobile/settings/theme.dart
Normal file
@@ -0,0 +1,896 @@
|
||||
import 'package:refilc/models/settings.dart';
|
||||
import 'package:refilc/models/shared_theme.dart';
|
||||
import 'package:refilc/theme/colors/accent.dart';
|
||||
import 'package:refilc/theme/colors/colors.dart';
|
||||
import 'package:refilc/theme/observer.dart';
|
||||
import 'package:refilc/ui/widgets/grade/grade_tile.dart';
|
||||
import 'package:refilc/ui/widgets/message/message_tile.dart';
|
||||
import 'package:refilc_kreta_api/models/grade.dart';
|
||||
import 'package:refilc_kreta_api/models/homework.dart';
|
||||
import 'package:refilc_kreta_api/models/message.dart';
|
||||
import 'package:refilc_mobile_ui/common/filter_bar.dart';
|
||||
import 'package:refilc_mobile_ui/common/panel/panel.dart';
|
||||
import 'package:refilc_mobile_ui/common/widgets/grade/new_grades.dart';
|
||||
import 'package:refilc_mobile_ui/common/widgets/homework/homework_tile.dart';
|
||||
import 'package:refilc_premium/models/premium_scopes.dart';
|
||||
import 'package:refilc_premium/providers/premium_provider.dart';
|
||||
import 'package:refilc_premium/providers/share_provider.dart';
|
||||
import 'package:refilc_premium/ui/mobile/flutter_colorpicker/colorpicker.dart';
|
||||
import 'package:refilc_premium/ui/mobile/premium/upsell.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'theme.i18n.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
class PremiumCustomAccentColorSetting extends StatefulWidget {
|
||||
const PremiumCustomAccentColorSetting({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PremiumCustomAccentColorSetting> createState() =>
|
||||
_PremiumCustomAccentColorSettingState();
|
||||
}
|
||||
|
||||
enum CustomColorMode {
|
||||
theme,
|
||||
saved,
|
||||
accent,
|
||||
background,
|
||||
highlight,
|
||||
enterId,
|
||||
}
|
||||
|
||||
class _PremiumCustomAccentColorSettingState
|
||||
extends State<PremiumCustomAccentColorSetting>
|
||||
with TickerProviderStateMixin {
|
||||
late final SettingsProvider settings;
|
||||
late final ShareProvider shareProvider;
|
||||
bool colorSelection = false;
|
||||
bool customColorMenu = false;
|
||||
CustomColorMode colorMode = CustomColorMode.theme;
|
||||
final customColorInput = TextEditingController();
|
||||
final unknownColor = Colors.black;
|
||||
|
||||
late TabController _testTabController;
|
||||
late TabController _colorsTabController;
|
||||
late AnimationController _openAnimController;
|
||||
|
||||
late final Animation<double> backgroundAnimation =
|
||||
Tween<double>(begin: 0, end: 1).animate(
|
||||
CurvedAnimation(
|
||||
parent: _openAnimController,
|
||||
curve: const Interval(0.2, 1.0, curve: Curves.easeInOut),
|
||||
),
|
||||
);
|
||||
|
||||
late final Animation<double> fullPageAnimation =
|
||||
Tween<double>(begin: 0, end: 1).animate(
|
||||
CurvedAnimation(
|
||||
parent: _openAnimController,
|
||||
curve: const Interval(0.0, 0.6, curve: Curves.easeInOut),
|
||||
),
|
||||
);
|
||||
|
||||
late final Animation<double> backContainerAnimation =
|
||||
Tween<double>(begin: 100, end: 0).animate(
|
||||
CurvedAnimation(
|
||||
parent: _openAnimController,
|
||||
curve: const Interval(0.0, 0.9, curve: Curves.easeInOut),
|
||||
),
|
||||
);
|
||||
|
||||
late final Animation<double> backContentAnimation =
|
||||
Tween<double>(begin: 100, end: 0).animate(
|
||||
CurvedAnimation(
|
||||
parent: _openAnimController,
|
||||
curve: const Interval(0.2, 1.0, curve: Curves.easeInOut),
|
||||
),
|
||||
);
|
||||
|
||||
late final Animation<double> backContentScaleAnimation =
|
||||
Tween<double>(begin: 0.8, end: 0.9).animate(
|
||||
CurvedAnimation(
|
||||
parent: _openAnimController,
|
||||
curve: const Interval(0.45, 1.0, curve: Curves.easeInOut),
|
||||
),
|
||||
);
|
||||
|
||||
late final Animation<double> pickerContainerAnimation =
|
||||
Tween<double>(begin: 0, end: 1).animate(
|
||||
CurvedAnimation(
|
||||
parent: _openAnimController,
|
||||
curve: const Interval(0.25, 0.8, curve: Curves.easeInOut),
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_colorsTabController = TabController(length: 5, vsync: this);
|
||||
_testTabController = TabController(length: 4, vsync: this);
|
||||
settings = Provider.of<SettingsProvider>(context, listen: false);
|
||||
shareProvider = Provider.of<ShareProvider>(context, listen: false);
|
||||
|
||||
_openAnimController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 750));
|
||||
_openAnimController.forward();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_openAnimController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void setTheme(ThemeMode mode, bool store) async {
|
||||
await settings.update(theme: mode, store: store);
|
||||
Provider.of<ThemeModeObserver>(context, listen: false)
|
||||
.changeTheme(mode, updateNavbarColor: false);
|
||||
}
|
||||
|
||||
dynamic getCustomColor() {
|
||||
switch (colorMode) {
|
||||
case CustomColorMode.theme:
|
||||
return accentColorMap[settings.accentColor];
|
||||
case CustomColorMode.saved:
|
||||
return [
|
||||
settings.customBackgroundColor,
|
||||
settings.customHighlightColor,
|
||||
settings.customAccentColor
|
||||
];
|
||||
case CustomColorMode.background:
|
||||
return settings.customBackgroundColor;
|
||||
case CustomColorMode.highlight:
|
||||
return settings.customHighlightColor;
|
||||
case CustomColorMode.accent:
|
||||
return settings.customAccentColor;
|
||||
case CustomColorMode.enterId:
|
||||
// do nothing here lol
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void updateCustomColor(dynamic v, bool store,
|
||||
{Color? accent, Color? background, Color? panels}) {
|
||||
if (colorMode != CustomColorMode.theme) {
|
||||
settings.update(accentColor: AccentColor.custom, store: store);
|
||||
}
|
||||
switch (colorMode) {
|
||||
case CustomColorMode.theme:
|
||||
settings.update(
|
||||
accentColor: accentColorMap.keys.firstWhere(
|
||||
(element) => accentColorMap[element] == v,
|
||||
orElse: () => AccentColor.filc),
|
||||
store: store);
|
||||
settings.update(
|
||||
customBackgroundColor: AppColors.of(context).background,
|
||||
store: store);
|
||||
settings.update(
|
||||
customHighlightColor: AppColors.of(context).highlight,
|
||||
store: store);
|
||||
settings.update(customAccentColor: v, store: store);
|
||||
break;
|
||||
case CustomColorMode.saved:
|
||||
settings.update(customBackgroundColor: v[0], store: store);
|
||||
settings.update(customHighlightColor: v[1], store: store);
|
||||
settings.update(customAccentColor: v[3], store: store);
|
||||
break;
|
||||
case CustomColorMode.background:
|
||||
settings.update(customBackgroundColor: v, store: store);
|
||||
break;
|
||||
case CustomColorMode.highlight:
|
||||
settings.update(customHighlightColor: v, store: store);
|
||||
break;
|
||||
case CustomColorMode.accent:
|
||||
settings.update(customAccentColor: v, store: store);
|
||||
break;
|
||||
case CustomColorMode.enterId:
|
||||
settings.update(customBackgroundColor: background, store: store);
|
||||
settings.update(customHighlightColor: panels, store: store);
|
||||
settings.update(customAccentColor: accent, store: store);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool hasAccess = Provider.of<PremiumProvider>(context)
|
||||
.hasScope(PremiumScopes.customColors);
|
||||
bool isBackgroundDifferent = Theme.of(context).colorScheme.background !=
|
||||
AppColors.of(context).background;
|
||||
|
||||
ThemeMode currentTheme = Theme.of(context).brightness == Brightness.light
|
||||
? ThemeMode.light
|
||||
: ThemeMode.dark;
|
||||
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
Provider.of<ThemeModeObserver>(context, listen: false)
|
||||
.changeTheme(settings.theme, updateNavbarColor: true);
|
||||
return true;
|
||||
},
|
||||
child: AnimatedBuilder(
|
||||
animation: _openAnimController,
|
||||
builder: (context, child) {
|
||||
final backgroundGradientBottomColor = isBackgroundDifferent
|
||||
? Theme.of(context).colorScheme.background
|
||||
: HSVColor.fromColor(Theme.of(context).colorScheme.background)
|
||||
.withValue(currentTheme == ThemeMode.dark
|
||||
? 0.1 * _openAnimController.value
|
||||
: 1.0 - (0.1 * _openAnimController.value))
|
||||
.withAlpha(1.0)
|
||||
.toColor();
|
||||
|
||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||
systemNavigationBarColor: backgroundGradientBottomColor,
|
||||
));
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
stops: const [0.0, 0.75],
|
||||
colors: isBackgroundDifferent
|
||||
? [
|
||||
Theme.of(context).colorScheme.background.withOpacity(1 -
|
||||
((currentTheme == ThemeMode.dark ? 0.65 : 0.45) *
|
||||
backgroundAnimation.value)),
|
||||
backgroundGradientBottomColor,
|
||||
]
|
||||
: [
|
||||
backgroundGradientBottomColor,
|
||||
backgroundGradientBottomColor
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Opacity(
|
||||
opacity: fullPageAnimation.value,
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: AppBar(
|
||||
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
leading: BackButton(color: AppColors.of(context).text),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 10.0),
|
||||
child: IconButton(
|
||||
focusColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
onPressed: () async {
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// const SnackBar(
|
||||
// duration: Duration(milliseconds: 1000),
|
||||
// content: Text(
|
||||
// "Hamarosan...",
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
SharedTheme theme =
|
||||
await shareProvider.shareCurrentTheme(context);
|
||||
Share.share(theme.id,
|
||||
subject: 'reFilc Téma / reFilc Theme');
|
||||
},
|
||||
icon: const Icon(
|
||||
FeatherIcons.share2,
|
||||
size: 22.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
title: Text(
|
||||
"theme_prev".i18n,
|
||||
style: TextStyle(color: AppColors.of(context).text),
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
Opacity(
|
||||
opacity: 1 - backContainerAnimation.value * (1 / 100),
|
||||
child: Transform.translate(
|
||||
offset: Offset(0, backContainerAnimation.value),
|
||||
child: Container(
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
stops: const [
|
||||
0.35,
|
||||
0.75
|
||||
],
|
||||
colors: [
|
||||
Theme.of(context).colorScheme.background,
|
||||
isBackgroundDifferent
|
||||
? HSVColor.fromColor(Theme.of(context)
|
||||
.colorScheme
|
||||
.background)
|
||||
.withSaturation((HSVColor.fromColor(
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.background)
|
||||
.saturation -
|
||||
0.15)
|
||||
.clamp(0.0, 1.0))
|
||||
.toColor()
|
||||
: backgroundGradientBottomColor,
|
||||
]),
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(
|
||||
vertical: 30, horizontal: 20),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 24.0),
|
||||
child: Opacity(
|
||||
opacity: 1 - backContentAnimation.value * (1 / 100),
|
||||
child: SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
child: Transform.translate(
|
||||
offset:
|
||||
Offset(0, -24 + backContentAnimation.value),
|
||||
child: Transform.scale(
|
||||
scale: backContentScaleAnimation.value,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 32.0, vertical: 6.0),
|
||||
child: FilterBar(
|
||||
items: const [
|
||||
Tab(text: "All"),
|
||||
Tab(text: "Grades"),
|
||||
Tab(text: "Messages"),
|
||||
Tab(text: "Absences"),
|
||||
],
|
||||
controller: _testTabController,
|
||||
padding: EdgeInsets.zero,
|
||||
censored: true,
|
||||
disableFading: true,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 18.0, vertical: 8.0),
|
||||
child: NewGradesSurprise(
|
||||
[
|
||||
Grade.fromJson(
|
||||
{
|
||||
"Uid": "0,Ertekeles",
|
||||
"RogzitesDatuma":
|
||||
"2022-01-01T23:00:00Z",
|
||||
"KeszitesDatuma":
|
||||
"2022-01-01T23:00:00Z",
|
||||
"LattamozasDatuma": null,
|
||||
"Tantargy": {
|
||||
"Uid": "0",
|
||||
"Nev": "reFilc szakirodalom",
|
||||
"Kategoria": {
|
||||
"Uid": "0,_",
|
||||
"Nev": "_",
|
||||
"Leiras": "Nem mondom meg"
|
||||
},
|
||||
"SortIndex": 2
|
||||
},
|
||||
"Tema":
|
||||
"Kupak csomag vásárlás vizsga",
|
||||
"Tipus": {
|
||||
"Uid": "0,_",
|
||||
"Nev": "_",
|
||||
"Leiras":
|
||||
"Évközi jegy/értékelés",
|
||||
},
|
||||
"Mod": {
|
||||
"Uid": "0,_",
|
||||
"Nev": "_",
|
||||
"Leiras": "_ feladat",
|
||||
},
|
||||
"ErtekFajta": {
|
||||
"Uid": "1,Osztalyzat",
|
||||
"Nev": "Osztalyzat",
|
||||
"Leiras":
|
||||
"Elégtelen (1) és Jeles (5) között az öt alapértelmezett érték"
|
||||
},
|
||||
"ErtekeloTanarNeve": "Premium",
|
||||
"Jelleg": "Ertekeles",
|
||||
"SzamErtek": 5,
|
||||
"SzovegesErtek": "Jeles(5)",
|
||||
"SulySzazalekErteke": 100,
|
||||
"SzovegesErtekelesRovidNev":
|
||||
null,
|
||||
"OsztalyCsoport": {"Uid": "0"},
|
||||
"SortIndex": 2
|
||||
},
|
||||
),
|
||||
],
|
||||
censored: true,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24.0, vertical: 6.0),
|
||||
child: Panel(
|
||||
child: GradeTile(
|
||||
Grade.fromJson(
|
||||
{
|
||||
"Uid": "0,Ertekeles",
|
||||
"RogzitesDatuma":
|
||||
"2022-01-01T23:00:00Z",
|
||||
"KeszitesDatuma":
|
||||
"2022-01-01T23:00:00Z",
|
||||
"LattamozasDatuma": null,
|
||||
"Tantargy": {
|
||||
"Uid": "0",
|
||||
"Nev": "reFilc szakosztály",
|
||||
"Kategoria": {
|
||||
"Uid": "0,_",
|
||||
"Nev": "_",
|
||||
"Leiras": "Nem mondom meg"
|
||||
},
|
||||
"SortIndex": 2
|
||||
},
|
||||
"Tema":
|
||||
"Kupak csomag vásárlás vizsga",
|
||||
"Tipus": {
|
||||
"Uid": "0,_",
|
||||
"Nev": "_",
|
||||
"Leiras":
|
||||
"Évközi jegy/értékelés",
|
||||
},
|
||||
"Mod": {
|
||||
"Uid": "0,_",
|
||||
"Nev": "_",
|
||||
"Leiras": "_ feladat",
|
||||
},
|
||||
"ErtekFajta": {
|
||||
"Uid": "1,Osztalyzat",
|
||||
"Nev": "Osztalyzat",
|
||||
"Leiras":
|
||||
"Elégtelen (1) és Jeles (5) között az öt alapértelmezett érték"
|
||||
},
|
||||
"ErtekeloTanarNeve": "Premium",
|
||||
"Jelleg": "Ertekeles",
|
||||
"SzamErtek": 5,
|
||||
"SzovegesErtek": "Jeles(5)",
|
||||
"SulySzazalekErteke": 100,
|
||||
"SzovegesErtekelesRovidNev":
|
||||
null,
|
||||
"OsztalyCsoport": {"Uid": "0"},
|
||||
"SortIndex": 2
|
||||
},
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
censored: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24.0, vertical: 6.0),
|
||||
child: Panel(
|
||||
child: HomeworkTile(
|
||||
Homework.fromJson(
|
||||
{
|
||||
"Uid": "0",
|
||||
"Tantargy": {
|
||||
"Uid": "0",
|
||||
"Nev":
|
||||
"reFilc premium előnyei",
|
||||
"Kategoria": {
|
||||
"Uid": "0,_",
|
||||
"Nev": "_",
|
||||
"Leiras":
|
||||
"reFilc premium előnyei",
|
||||
},
|
||||
"SortIndex": 0
|
||||
},
|
||||
"TantargyNeve":
|
||||
"reFilc premium előnyei",
|
||||
"RogzitoTanarNeve":
|
||||
"Kupak János",
|
||||
"Szoveg":
|
||||
"45 perc filctollal való rajzolás",
|
||||
"FeladasDatuma":
|
||||
"2022-01-01T23:00:00Z",
|
||||
"HataridoDatuma":
|
||||
"2022-01-01T23:00:00Z",
|
||||
"RogzitesIdopontja":
|
||||
"2022-01-01T23:00:00Z",
|
||||
"IsTanarRogzitette": true,
|
||||
"IsTanuloHaziFeladatEnabled":
|
||||
false,
|
||||
"IsMegoldva": false,
|
||||
"IsBeadhato": false,
|
||||
"OsztalyCsoport": {"Uid": "0"},
|
||||
"IsCsatolasEngedelyezes": false
|
||||
},
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
censored: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24.0, vertical: 6.0),
|
||||
child: Panel(
|
||||
child: MessageTile(
|
||||
Message.fromJson(
|
||||
{
|
||||
"azonosito": 0,
|
||||
"isElolvasva": true,
|
||||
"isToroltElem": false,
|
||||
"tipus": {
|
||||
"azonosito": 1,
|
||||
"kod": "BEERKEZETT",
|
||||
"rovidNev":
|
||||
"Beérkezett üzenet",
|
||||
"nev": "Beérkezett üzenet",
|
||||
"leiras": "Beérkezett üzenet"
|
||||
},
|
||||
"uzenet": {
|
||||
"azonosito": 0,
|
||||
"kuldesDatum":
|
||||
"2022-01-01T23:00:00",
|
||||
"feladoNev": "reFilc",
|
||||
"feladoTitulus":
|
||||
"Nagyon magas szintű személy",
|
||||
"szoveg":
|
||||
"<p>Kedves Felhasználó!</p><p><br></p><p>A prémium vásárlásakor kapott filctollal 90%-al több esély van jó jegyek szerzésére.</p>",
|
||||
"targy":
|
||||
"Filctoll használati útmutató",
|
||||
"statusz": {
|
||||
"azonosito": 2,
|
||||
"kod": "KIKULDVE",
|
||||
"rovidNev": "Kiküldve",
|
||||
"nev": "Kiküldve",
|
||||
"leiras": "Kiküldve"
|
||||
},
|
||||
"cimzettLista": [
|
||||
{
|
||||
"azonosito": 0,
|
||||
"kretaAzonosito": 0,
|
||||
"nev": "Tinta Józsi",
|
||||
"tipus": {
|
||||
"azonosito": 0,
|
||||
"kod": "TANULO",
|
||||
"rovidNev": "Tanuló",
|
||||
"nev": "Tanuló",
|
||||
"leiras": "Tanuló"
|
||||
}
|
||||
},
|
||||
],
|
||||
"csatolmanyok": [
|
||||
{
|
||||
"azonosito": 0,
|
||||
"fajlNev": "Filctoll.doc"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
),
|
||||
censored: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Wrap(
|
||||
children: [
|
||||
Opacity(
|
||||
opacity: pickerContainerAnimation.value,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: backgroundGradientBottomColor,
|
||||
offset: const Offset(0, -8),
|
||||
blurRadius: 16,
|
||||
spreadRadius: 18,
|
||||
),
|
||||
],
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
stops: const [
|
||||
0.0,
|
||||
0.175
|
||||
],
|
||||
colors: [
|
||||
backgroundGradientBottomColor,
|
||||
backgroundGradientBottomColor,
|
||||
]),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0),
|
||||
child: FilterBar(
|
||||
items: [
|
||||
ColorTab(
|
||||
color: accentColorMap[
|
||||
settings.accentColor] ??
|
||||
unknownColor,
|
||||
tab: Tab(
|
||||
text: "colorpicker_presets"
|
||||
.i18n)),
|
||||
ColorTab(
|
||||
color: unknownColor,
|
||||
tab: Tab(text: "enter_id".i18n)),
|
||||
/*ColorTab(
|
||||
color:
|
||||
settings.customAccentColor ??
|
||||
unknownColor,
|
||||
tab: Tab(
|
||||
text: "colorpicker_saved"
|
||||
.i18n)),*/
|
||||
ColorTab(
|
||||
unlocked: hasAccess,
|
||||
color: settings
|
||||
.customBackgroundColor ??
|
||||
unknownColor,
|
||||
tab: Tab(
|
||||
text: "colorpicker_background"
|
||||
.i18n)),
|
||||
ColorTab(
|
||||
unlocked: hasAccess,
|
||||
color: settings
|
||||
.customHighlightColor ??
|
||||
unknownColor,
|
||||
tab: Tab(
|
||||
text: "colorpicker_panels"
|
||||
.i18n)),
|
||||
ColorTab(
|
||||
unlocked: hasAccess,
|
||||
color:
|
||||
settings.customAccentColor ??
|
||||
unknownColor,
|
||||
tab: Tab(
|
||||
text: "colorpicker_accent"
|
||||
.i18n)),
|
||||
],
|
||||
onTap: (index) {
|
||||
if (!hasAccess) {
|
||||
index = 0;
|
||||
_colorsTabController.animateTo(0,
|
||||
duration: Duration.zero);
|
||||
|
||||
PremiumLockedFeatureUpsell.show(
|
||||
context: context,
|
||||
feature: PremiumFeature
|
||||
.customcolors);
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
setState(() {
|
||||
colorMode =
|
||||
CustomColorMode.theme;
|
||||
});
|
||||
break;
|
||||
case 1:
|
||||
setState(() {
|
||||
colorMode =
|
||||
CustomColorMode.enterId;
|
||||
});
|
||||
break;
|
||||
/*case 1:
|
||||
setState(() {
|
||||
colorMode =
|
||||
CustomColorMode.saved;
|
||||
});
|
||||
break;*/
|
||||
case 2:
|
||||
setState(() {
|
||||
colorMode =
|
||||
CustomColorMode.background;
|
||||
});
|
||||
break;
|
||||
case 3:
|
||||
setState(() {
|
||||
colorMode =
|
||||
CustomColorMode.highlight;
|
||||
});
|
||||
break;
|
||||
case 4:
|
||||
setState(() {
|
||||
colorMode =
|
||||
CustomColorMode.accent;
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
controller: _colorsTabController,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0),
|
||||
child: SafeArea(
|
||||
child: FilcColorPicker(
|
||||
colorMode: colorMode,
|
||||
pickerColor: colorMode ==
|
||||
CustomColorMode.accent
|
||||
? settings.customAccentColor ??
|
||||
unknownColor
|
||||
: colorMode ==
|
||||
CustomColorMode.background
|
||||
? settings
|
||||
.customBackgroundColor ??
|
||||
unknownColor
|
||||
: colorMode ==
|
||||
CustomColorMode.theme
|
||||
? (accentColorMap[settings
|
||||
.accentColor] ??
|
||||
AppColors.of(context)
|
||||
.text) // idk what else
|
||||
: settings
|
||||
.customHighlightColor ??
|
||||
unknownColor,
|
||||
onColorChanged: (c) {
|
||||
setState(() {
|
||||
updateCustomColor(c, false);
|
||||
});
|
||||
setTheme(settings.theme, false);
|
||||
},
|
||||
onColorChangeEnd: (c, {adaptive}) {
|
||||
setState(() {
|
||||
if (adaptive == true) {
|
||||
settings.update(
|
||||
accentColor:
|
||||
AccentColor.adaptive);
|
||||
settings.update(
|
||||
customBackgroundColor:
|
||||
AppColors.of(context)
|
||||
.background,
|
||||
store: true);
|
||||
settings.update(
|
||||
customHighlightColor:
|
||||
AppColors.of(context)
|
||||
.highlight,
|
||||
store: true);
|
||||
} else {
|
||||
updateCustomColor(c, true);
|
||||
}
|
||||
});
|
||||
setTheme(settings.theme, true);
|
||||
},
|
||||
onThemeIdProvided: (theme) {
|
||||
setState(() {
|
||||
updateCustomColor(
|
||||
null,
|
||||
true,
|
||||
accent: theme.accentColor,
|
||||
background:
|
||||
theme.backgroundColor,
|
||||
panels: theme.panelsColor,
|
||||
);
|
||||
});
|
||||
setTheme(settings.theme, true);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ColorTab extends StatelessWidget {
|
||||
const ColorTab(
|
||||
{Key? key, required this.tab, required this.color, this.unlocked = true})
|
||||
: super(key: key);
|
||||
|
||||
final Tab tab;
|
||||
final Color color;
|
||||
final bool unlocked;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Transform.translate(
|
||||
offset: const Offset(-3, 1),
|
||||
child: unlocked
|
||||
? Container(
|
||||
width: 15,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: color,
|
||||
border: Border.all(color: Colors.black, width: 2.0),
|
||||
),
|
||||
)
|
||||
: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||
child: Icon(Icons.lock,
|
||||
color: Color.fromARGB(255, 82, 82, 82), size: 18),
|
||||
),
|
||||
),
|
||||
tab
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PremiumColorPickerItem extends StatelessWidget {
|
||||
const PremiumColorPickerItem(
|
||||
{Key? key, required this.label, this.onTap, required this.color})
|
||||
: super(key: key);
|
||||
|
||||
final String label;
|
||||
final void Function()? onTap;
|
||||
final Color color;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: AppColors.of(context).text,
|
||||
fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: color, shape: BoxShape.circle, border: Border.all()),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: onTap,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
49
refilc_premium/lib/ui/mobile/settings/theme.i18n.dart
Normal file
49
refilc_premium/lib/ui/mobile/settings/theme.i18n.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
extension SettingsLocalization on String {
|
||||
static final _t = Translations.byLocale("hu_hu") +
|
||||
{
|
||||
"en_en": {
|
||||
"theme_prev": "Preview",
|
||||
"colorpicker_presets": "Presets",
|
||||
"colorpicker_background": "Background",
|
||||
"colorpicker_panels": "Panels",
|
||||
"colorpicker_accent": "Accent",
|
||||
"need_sub": "You need Kupak subscription to use modify this.",
|
||||
"advanced": "Advanced",
|
||||
"enter_id": "Enter ID",
|
||||
"theme_id": "Theme ID...",
|
||||
"theme_not_found": "Theme not found!",
|
||||
},
|
||||
"hu_hu": {
|
||||
"theme_prev": "Előnézet",
|
||||
"colorpicker_presets": "Téma",
|
||||
"colorpicker_background": "Háttér",
|
||||
"colorpicker_panels": "Panelek",
|
||||
"colorpicker_accent": "Színtónus",
|
||||
"need_sub": "A módosításhoz Kupak szintű támogatás szükséges.",
|
||||
"advanced": "Haladó",
|
||||
"enter_id": "ID megadása",
|
||||
"theme_id": "Téma azonosító...",
|
||||
"theme_not_found": "A téma nem található!",
|
||||
},
|
||||
"de_de": {
|
||||
"theme_prev": "Vorschau",
|
||||
"colorpicker_presets": "Farben",
|
||||
"colorpicker_background": "Hintergrund",
|
||||
"colorpicker_panels": "Tafeln",
|
||||
"colorpicker_accent": "Akzent",
|
||||
"need_sub":
|
||||
"Sie benötigen ein Kupak-Abonnement, um diese Funktion zu ändern.",
|
||||
"advanced": "Fortschrittlich",
|
||||
"enter_id": "Geben Sie die ID ein",
|
||||
"theme_id": "Themen-ID...",
|
||||
"theme_not_found": "Thema nicht gefunden!",
|
||||
},
|
||||
};
|
||||
|
||||
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