Merge pull request #58 from refilc/dev

Dev
This commit is contained in:
Márton Kiss 2023-09-26 20:32:21 +02:00 committed by GitHub
commit 4b93c891c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 469 additions and 126 deletions

View File

@ -53,9 +53,14 @@ class FilcAPI {
.map((json) => School.fromJson(json))
.toList();
schools.add(School(
city: "Tiszabura",
instituteCode: "supporttest-reni-tiszabura-teszt01",
name: "FILC Éles Reni tiszabura-teszt",
city: "Stockholm",
instituteCode: "refilc-test-sweden",
name: "reFilc Test SE - Leo Ekström High School",
));
schools.add(School(
city: "Madrid",
instituteCode: "refilc-test-spain",
name: "reFilc Test ES - Emilio Obrero University",
));
return schools;
} else {

View File

@ -1,6 +1,7 @@
// ignore_for_file: avoid_print, use_build_context_synchronously
import 'package:filcnaplo/utils/jwt.dart';
import 'package:filcnaplo_kreta_api/models/school.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';
@ -20,6 +21,7 @@ import 'package:filcnaplo_kreta_api/models/week.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:filcnaplo/api/nonce.dart';
import 'package:uuid/uuid.dart';
enum LoginState {
missingFields,
@ -47,6 +49,58 @@ Future loginAPI({
void Function(User)? onLogin,
void Function()? onSuccess,
}) async {
Future testLogin(School school) async {
var user = User(
username: username,
password: password,
instituteCode: instituteCode,
name: 'Teszt Lajos',
student: Student(
birth: DateTime.now(),
id: const Uuid().v4(),
name: 'Teszt Lajos',
school: school,
yearId: '1',
parents: ['Teszt András', 'Teszt Linda'],
json: {"a": "b"},
address: '1117 Budapest, Gábor Dénes utca 4.',
),
role: Role.parent,
);
if (onLogin != null) onLogin(user);
// store test user in db
await Provider.of<DatabaseProvider>(context, listen: false)
.store
.storeUser(user);
Provider.of<UserProvider>(context, listen: false).addUser(user);
Provider.of<UserProvider>(context, listen: false).setUser(user.id);
if (onSuccess != null) onSuccess();
return LoginState.success;
}
// if institute matches one of test things do test login
if (instituteCode == 'refilc-test-sweden') {
School school = School(
city: "Stockholm",
instituteCode: "refilc-test-sweden",
name: "reFilc Test SE - Leo Ekström High School",
);
await testLogin(school);
} else if (instituteCode == 'refilc-test-spain') {
School school = School(
city: "Madrid",
instituteCode: "refilc-test-spain",
name: "reFilc Test ES - Emilio Obrero University",
);
await testLogin(school);
} else {
// normal login from here
Provider.of<KretaClient>(context, listen: false).userAgent =
Provider.of<SettingsProvider>(context, listen: false).config.userAgent;
@ -129,5 +183,7 @@ Future loginAPI({
}
}
}
}
return LoginState.failed;
}

View File

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
@ -12,6 +14,7 @@ class StatusProvider extends ChangeNotifier {
StatusProvider() {
_handleNetworkChanges();
_handleDNSFailure();
Connectivity().checkConnectivity().then((value) => _networkType = value);
}
@ -24,6 +27,7 @@ class StatusProvider extends ChangeNotifier {
_networkType = event;
if (event == ConnectivityResult.none) {
if (!_stack.contains(Status.network)) {
_stack.remove(Status.apiError);
_stack.insert(0, Status.network);
notifyListeners();
}
@ -36,6 +40,31 @@ class StatusProvider extends ChangeNotifier {
});
}
void _handleDNSFailure() {
try {
InternetAddress.lookup('api.refilc.hu').then((status) {
if (status.isEmpty) {
if (!_stack.contains(Status.network)) {
_stack.remove(Status.apiError);
_stack.insert(0, Status.network);
notifyListeners();
}
} else {
if (_stack.contains(Status.network)) {
_stack.remove(Status.network);
notifyListeners();
}
}
});
} on SocketException catch (_) {
if (!_stack.contains(Status.network)) {
_stack.remove(Status.apiError);
_stack.insert(0, Status.network);
notifyListeners();
}
}
}
void triggerRequest(http.Response res) {
if (res.headers.containsKey("x-maintenance-mode") ||
res.statusCode == 503) {
@ -50,6 +79,7 @@ class StatusProvider extends ChangeNotifier {
}
}
if (_stack.contains(Status.network)) return;
if (res.body == "invalid_grant" ||
res.body.replaceAll(' ', '') == '' ||
res.statusCode == 400) {

View File

@ -33,6 +33,7 @@ const settingsDB = DatabaseStruct("settings", {
"renamed_subjects_italics": int, "renamed_teachers_enabled": int,
"renamed_teachers_italics": int,
"live_activity_color": String,
"welcome_message": String,
});
// DON'T FORGET TO UPDATE DEFAULT VALUES IN `initDB` MIGRATION OR ELSE PARENTS WILL COMPLAIN ABOUT THEIR CHILDREN MISSING
// YOU'VE BEEN WARNED!!!

View File

@ -20,7 +20,6 @@ class Ad {
});
factory Ad.fromJson(Map json) {
print(json);
return Ad(
title: json['title'] ?? 'Ad',
description: json['description'] ?? '',

View File

@ -76,6 +76,7 @@ class SettingsProvider extends ChangeNotifier {
bool _renamedTeachersEnabled;
bool _renamedTeachersItalics;
Color _liveActivityColor;
String _welcomeMessage;
SettingsProvider({
DatabaseProvider? database,
@ -120,6 +121,7 @@ class SettingsProvider extends ChangeNotifier {
required bool renameTeachersEnabled,
required bool renameTeachersItalics,
required Color liveActivityColor,
required String welcomeMessage,
}) : _database = database,
_language = language,
_startPage = startPage,
@ -161,7 +163,8 @@ class SettingsProvider extends ChangeNotifier {
_renamedSubjectsItalics = renameSubjectsItalics,
_renamedTeachersEnabled = renameTeachersEnabled,
_renamedTeachersItalics = renameTeachersItalics,
_liveActivityColor = liveActivityColor;
_liveActivityColor = liveActivityColor,
_welcomeMessage = welcomeMessage;
factory SettingsProvider.fromMap(Map map,
{required DatabaseProvider database}) {
@ -223,6 +226,7 @@ class SettingsProvider extends ChangeNotifier {
renameTeachersEnabled: map["renamed_teachers_enabled"] == 1,
renameTeachersItalics: map["renamed_teachers_italics"] == 1,
liveActivityColor: Color(map["live_activity_color"]),
welcomeMessage: map["welcome_message"],
);
}
@ -272,6 +276,7 @@ class SettingsProvider extends ChangeNotifier {
"renamed_teachers_enabled": _renamedTeachersEnabled ? 1 : 0,
"renamed_teachers_italics": _renamedTeachersItalics ? 1 : 0,
"live_activity_color": _liveActivityColor.value,
"welcome_message": _welcomeMessage,
};
}
@ -325,6 +330,7 @@ class SettingsProvider extends ChangeNotifier {
renameTeachersEnabled: false,
renameTeachersItalics: false,
liveActivityColor: const Color(0xFF676767),
welcomeMessage: '',
);
}
@ -373,6 +379,7 @@ class SettingsProvider extends ChangeNotifier {
bool get renamedTeachersEnabled => _renamedTeachersEnabled;
bool get renamedTeachersItalics => _renamedTeachersItalics;
Color get liveActivityColor => _liveActivityColor;
String get welcomeMessage => _welcomeMessage;
Future<void> update({
bool store = true,
@ -417,6 +424,7 @@ class SettingsProvider extends ChangeNotifier {
bool? renamedTeachersEnabled,
bool? renamedTeachersItalics,
Color? liveActivityColor,
String? welcomeMessage,
}) async {
if (language != null && language != _language) _language = language;
if (startPage != null && startPage != _startPage) _startPage = startPage;
@ -535,6 +543,9 @@ class SettingsProvider extends ChangeNotifier {
if (liveActivityColor != null && liveActivityColor != _liveActivityColor) {
_liveActivityColor = liveActivityColor;
}
if (welcomeMessage != null && welcomeMessage != _welcomeMessage) {
_welcomeMessage = welcomeMessage;
}
if (store) await _database?.store.storeSettings(this);
notifyListeners();
}

View File

@ -93,4 +93,13 @@ class User {
"refresh_user_data": "false",
};
}
static Map<String, Object?> logoutBody({
required String refreshToken,
}) {
return {
"refresh_token": refreshToken,
"client_id": KretaAPI.clientId,
};
}
}

View File

@ -3,7 +3,7 @@ description: "Nem hivatalos e-napló alkalmazás az e-Kréta rendszerhez"
homepage: https://refilc.hu
publish_to: "none"
version: 4.2.3+223
version: 4.2.4+224
environment:
sdk: ">=2.17.0 <3.0.0"

View File

@ -176,13 +176,16 @@ class _SidebarState extends State<Sidebar> {
String? userId = user.id;
if (userId == null) return;
// Delete User
// revoke refresh token
await Provider.of<KretaClient>(context, listen: false).logout();
// delete user from app
user.removeUser(userId);
await Provider.of<DatabaseProvider>(context, listen: false)
.store
.removeUser(userId);
// If no other Users left, go back to LoginScreen
// if no other users left, go back to login screen
if (user.getUsers().isNotEmpty) {
user.setUser(user.getUsers().first.id);
restore().then((_) => user.setUser(user.getUsers().first.id));

View File

@ -5,7 +5,7 @@ extension SettingsLocalization on String {
{
"en_en": {
"personal_details": "Personal Details",
"open_dkt": "Open DKT",
"open_dkt": "Open DCS",
"edit_nickname": "Edit Nickname",
"edit_profile_picture": "Edit Profile Picture",
"remove_profile_picture": "Remove Profile Picture",
@ -39,7 +39,8 @@ extension SettingsLocalization on String {
"done": "Done",
"reset": "Reset",
"open": "Open",
"data_collected": "Data collected: Platform (eg. Android), App version (eg. 3.0.0), Unique Install Identifier",
"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",
@ -98,7 +99,8 @@ extension SettingsLocalization on String {
"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ó",
"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",
@ -123,7 +125,7 @@ extension SettingsLocalization on String {
},
"de_de": {
"personal_details": "Persönliche Angaben",
"open_dkt": "Öffnen DKT",
"open_dkt": "Öffnen RDZ",
"edit_nickname": "Spitznamen bearbeiten",
"edit_profile_picture": "Profilbild bearbeiten",
"remove_profile_picture": "Profilbild entfernen",
@ -157,7 +159,8 @@ extension SettingsLocalization on String {
"done": "Fertig",
"reset": "Zurücksetzen",
"open": "Öffnen",
"data_collected": "Erhobene Daten: Plattform (z.B. Android), App version (z.B. 3.0.0), Eindeutige Installationskennung",
"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",

View File

@ -3,6 +3,7 @@ import 'package:intl/intl.dart';
class KretaAPI {
// IDP API
static const login = BaseKreta.kretaIdp + KretaApiEndpoints.token;
static const logout = BaseKreta.kretaIdp + KretaApiEndpoints.revoke;
static const nonce = BaseKreta.kretaIdp + KretaApiEndpoints.nonce;
static const clientId = "kreta-ellenorzo-mobile-android";
@ -86,6 +87,7 @@ class BaseKreta {
class KretaApiEndpoints {
static const token = "/connect/token";
static const revoke = "/connect/revocation";
static const nonce = "/nonce";
static const notes = "/ellenorzo/V3/Sajat/Feljegyzesek";
static const events = "/ellenorzo/V3/Sajat/FaliujsagElemek";

View File

@ -26,6 +26,8 @@ class KretaClient {
late final UserProvider _user;
late final StatusProvider _status;
bool _loginRefreshing = false;
KretaClient({
this.accessToken,
required SettingsProvider settings,
@ -164,6 +166,9 @@ class KretaClient {
}
Future<void> refreshLogin() async {
if (_loginRefreshing) return;
_loginRefreshing = true;
User? loginUser = _user.user;
if (loginUser == null) return;
@ -182,13 +187,15 @@ class KretaClient {
print("DEBUG: refreshLogin: ${loginUser.id} ${loginUser.name}");
}
Map? loginRes = await postAPI(KretaAPI.login,
Map? loginRes = await postAPI(
KretaAPI.login,
headers: headers,
body: User.loginBody(
username: loginUser.username,
password: loginUser.password,
instituteCode: loginUser.instituteCode,
));
),
);
if (loginRes != null) {
if (loginRes.containsKey("access_token")) {
@ -215,5 +222,25 @@ class KretaClient {
}
}
}
_loginRefreshing = false;
}
Future<void> logout() async {
User? loginUser = _user.user;
if (loginUser == null) return;
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded",
};
await postAPI(
KretaAPI.logout,
headers: headers,
body: User.logoutBody(
refreshToken: refreshToken!,
),
json: false,
);
}
}

View File

@ -5,7 +5,7 @@ import 'package:filcnaplo_kreta_api/client/api.dart';
import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_kreta_api/models/lesson.dart';
import 'package:filcnaplo_kreta_api/models/week.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
class TimetableProvider with ChangeNotifier {
Map<Week, List<Lesson>> lessons = {};
@ -69,14 +69,14 @@ class TimetableProvider with ChangeNotifier {
.getAPI(KretaAPI.timetable(iss, start: week.start, end: week.end));
if (lessonsJson == null) {
if (kDebugMode) print('Cannot fetch Lessons for User ${user.id}');
return;
// throw "Cannot fetch Lessons for User ${user.id}";
} else {
List<Lesson> lessonsList =
lessonsJson.map((e) => Lesson.fromJson(e)).toList();
if (lessons.isEmpty) return;
lessons[week] = lessonsList;
await store();

View File

@ -13,9 +13,6 @@ class AdTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
print('geic');
print(ad);
return Padding(
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8.0),
child: PanelButton(
@ -38,12 +35,15 @@ class AdTile extends StatelessWidget {
],
),
leading: ad.logoUrl != null
? Image.network(
? ClipRRect(
borderRadius: BorderRadius.circular(50.0),
child: Image.network(
ad.logoUrl.toString(),
errorBuilder: (context, error, stackTrace) {
ad.logoUrl = null;
return const SizedBox();
},
),
)
: null,
trailing: const Icon(FeatherIcons.externalLink),

View File

@ -30,6 +30,7 @@ import 'package:provider/provider.dart';
import 'home_page.i18n.dart';
import 'package:filcnaplo/ui/filter/widgets.dart';
import 'package:filcnaplo/ui/filter/sort.dart';
import 'package:i18n_extension/i18n_extension.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@ -94,6 +95,13 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
void setGreeting() {
DateTime now = DateTime.now();
List<String> nameParts = user.displayName?.split(" ") ?? ["?"];
if (!settings.presentationMode) {
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
} else {
firstName = "János";
}
if (now.isBefore(DateTime(now.year, DateTime.august, 31)) &&
now.isAfter(DateTime(now.year, DateTime.june, 14))) {
greeting = "goodrest";
@ -104,16 +112,16 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
Future.delayed(const Duration(seconds: 1))
.then((value) => mounted ? _confettiController?.play() : null);
}
} else if (now.month == user.student?.birth.month &&
now.day == user.student?.birth.day) {
greeting = "happybirthday";
// } else if (now.month == user.student?.birth.month &&
// now.day == user.student?.birth.day) {
// greeting = "happybirthday";
if (NavigationScreen.of(context)?.init("confetti") ?? false) {
_confettiController =
ConfettiController(duration: const Duration(seconds: 3));
Future.delayed(const Duration(seconds: 1))
.then((value) => mounted ? _confettiController?.play() : null);
}
// if (NavigationScreen.of(context)?.init("confetti") ?? false) {
// _confettiController =
// ConfettiController(duration: const Duration(seconds: 3));
// Future.delayed(const Duration(seconds: 1))
// .then((value) => mounted ? _confettiController?.play() : null);
// }
} else if (now.isAfter(DateTime(now.year, DateTime.may, 28)) &&
now.isBefore(DateTime(now.year, DateTime.may, 30))) {
greeting = "refilcopen";
@ -130,6 +138,12 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
greeting = "merryxmas";
} else if (now.month == DateTime.january && now.day == 1) {
greeting = "happynewyear";
} else if (settings.welcomeMessage.replaceAll(' ', '') != '') {
greeting = settings.welcomeMessage;
greeting = localizeFill(
settings.welcomeMessage,
[firstName],
);
} else if (now.hour >= 18) {
greeting = "goodevening";
} else if (now.hour >= 10) {
@ -155,13 +169,6 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
setGreeting();
List<String> nameParts = user.displayName?.split(" ") ?? ["?"];
if (!settings.presentationMode) {
firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];
} else {
firstName = "János";
}
return Scaffold(
body: Stack(
children: [

View File

@ -13,7 +13,8 @@ extension Localization on String {
"welcome": "Welcome, %s!",
"missing_fields": "Missing Fields!",
"invalid_grant":
"Invalid Username/Password! (Try adding spaces after Username)",
// "Invalid Username/Password! (Try adding spaces after Username)",
"Invalid Username/Password!",
"error": "Failed to log in.",
"schools_error": "Failed to get schools."
},
@ -27,7 +28,8 @@ extension Localization on String {
"welcome": "Üdv, %s!",
"missing_fields": "Hiányzó adatok!",
"invalid_grant":
"Helytelen Felhasználónév/Jelszó! (Próbálj szóközöket írni a Felhasználónév után)",
// "Helytelen Felhasználónév/Jelszó! (Próbálj szóközöket írni a Felhasználónév után)",
"Helytelen Felhasználónév/Jelszó!",
"error": "Sikertelen bejelentkezés.",
"schools_error": "Nem sikerült lekérni az iskolákat."
},

View File

@ -43,6 +43,7 @@ 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';
import 'package:filcnaplo_premium/ui/mobile/settings/modify_teacher_names.dart';
import 'package:filcnaplo_premium/ui/mobile/settings/welcome_message.dart';
class SettingsScreen extends StatefulWidget {
const SettingsScreen({Key? key}) : super(key: key);
@ -206,7 +207,7 @@ class _SettingsScreenState extends State<SettingsScreen>
opacity: 1 - _hideContainersController.value,
child: Column(
children: [
const SizedBox(height: 32.0),
const SizedBox(height: 45.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -454,6 +455,7 @@ class _SettingsScreenState extends State<SettingsScreen>
Material(
type: MaterialType.transparency,
child: MenuNotifications(settings: settings)),
WelcomeMessagePanelButton(settings, user),
],
),
),

View File

@ -73,6 +73,9 @@ extension SettingsLocalization on String {
"devsettings": "Developer Settings",
"devmode": "Developer Mode",
"copy_jwt": "Copy JWT",
"welcome_msg": "Welcome Message",
"default": "Default",
"edit_welcome_msg": "Edit welcome message",
},
"hu_hu": {
"personal_details": "Személyes információk",
@ -144,6 +147,9 @@ extension SettingsLocalization on String {
"devsettings": "Fejlesztői Beállítások",
"devmode": "Fejlesztői mód",
"copy_jwt": "JWT másolása",
"welcome_msg": "Üdvözlő üzenet",
"default": "Alapértelmezett",
"edit_welcome_msg": "Üdvözlő üzenet szerkesztése",
},
"de_de": {
"personal_details": "Persönliche Angaben",
@ -214,6 +220,9 @@ extension SettingsLocalization on String {
"devsettings": "Entwickleroptionen",
"devmode": "Entwicklermodus",
"copy_jwt": "JWT kopieren",
"welcome_msg": "Willkommensnachricht",
"default": "Standard",
"edit_welcome_msg": "Begrüßungsnachricht bearbeiten",
},
};

View File

@ -8,6 +8,7 @@ import 'package:filcnaplo/ui/widgets/message/message_tile.dart';
import 'package:filcnaplo_kreta_api/models/grade.dart';
import 'package:filcnaplo_kreta_api/models/homework.dart';
import 'package:filcnaplo_kreta_api/models/message.dart';
import 'package:filcnaplo_mobile_ui/common/action_button.dart';
import 'package:filcnaplo_mobile_ui/common/filter_bar.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel.dart';
import 'package:filcnaplo_mobile_ui/common/widgets/grade/new_grades.dart';
@ -268,10 +269,32 @@ class _PremiumCustomAccentColorSettingState
// ),
// ),
// );
SharedTheme theme =
await shareProvider.shareCurrentTheme(context);
Share.share(theme.id,
subject: 'reFilc Téma / reFilc Theme');
showDialog(
context: context,
builder: (context) => WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: Text("attention".i18n),
content: Text("share_disclaimer".i18n),
actions: [
ActionButton(
label: "understand".i18n,
onTap: () async {
Navigator.of(context).pop();
SharedTheme theme = await shareProvider
.shareCurrentTheme(context);
Share.share(
theme.id,
subject: 'share_subj_theme'.i18n,
);
},
),
],
),
),
);
},
icon: const Icon(
FeatherIcons.share2,

View File

@ -14,6 +14,10 @@ extension SettingsLocalization on String {
"enter_id": "Enter ID",
"theme_id": "Theme ID...",
"theme_not_found": "Theme not found!",
"attention": "Attention!",
"share_disclaimer":
"By sharing the theme, you agree that the nickname you set and all settings of the theme will be shared publicly.",
"understand": "I understand",
},
"hu_hu": {
"theme_prev": "Előnézet",
@ -26,6 +30,10 @@ extension SettingsLocalization on String {
"enter_id": "ID megadása",
"theme_id": "Téma azonosító...",
"theme_not_found": "A téma nem található!",
"attention": "Figyelem!",
"share_disclaimer":
"A téma megosztásával elfogadod, hogy az általad beállított becenév és a téma minden beállítása nyilvánosan megosztásra kerüljön.",
"understand": "Értem",
},
"de_de": {
"theme_prev": "Vorschau",
@ -39,6 +47,10 @@ extension SettingsLocalization on String {
"enter_id": "Geben Sie die ID ein",
"theme_id": "Themen-ID...",
"theme_not_found": "Thema nicht gefunden!",
"attention": "Achtung!",
"share_disclaimer":
"Durch das Teilen des Themes erklären Sie sich damit einverstanden, dass der von Ihnen festgelegte Spitzname und alle Einstellungen des Themes öffentlich geteilt werden.",
"understand": "Ich verstehe",
},
};

View File

@ -0,0 +1,142 @@
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo_mobile_ui/common/panel/panel_button.dart';
import 'package:filcnaplo_premium/models/premium_scopes.dart';
import 'package:filcnaplo_premium/providers/premium_provider.dart';
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/settings_screen.i18n.dart';
import 'package:provider/provider.dart';
import 'package:i18n_extension/i18n_extension.dart';
// ignore: must_be_immutable
class WelcomeMessagePanelButton extends StatelessWidget {
late SettingsProvider settingsProvider;
late UserProvider user;
WelcomeMessagePanelButton(this.settingsProvider, this.user, {Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
String finalName = ((user.nickname ?? '') != ''
? user.nickname
: (user.displayName ?? '') != ''
? user.displayName
: 'János') ??
'János';
return PanelButton(
onPressed: () {
if (!Provider.of<PremiumProvider>(context, listen: false)
.hasScope(PremiumScopes.all)) {
PremiumLockedFeatureUpsell.show(
context: context, feature: PremiumFeature.profile);
return;
}
showDialog(
context: context,
builder: (context) => WelcomeMessageEditor(settingsProvider));
},
title: Text("welcome_msg".i18n),
leading: const Icon(FeatherIcons.smile),
trailing: SizedBox(
width: 100,
child: Expanded(
child: Text(
settingsProvider.welcomeMessage.replaceAll(' ', '') != ''
? localizeFill(
settingsProvider.welcomeMessage,
[finalName],
)
: 'default'.i18n,
style: const TextStyle(fontSize: 14.0),
textAlign: TextAlign.end,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
),
),
);
}
}
// ignore: must_be_immutable
class WelcomeMessageEditor extends StatefulWidget {
late SettingsProvider settingsProvider;
WelcomeMessageEditor(this.settingsProvider, {Key? key}) : super(key: key);
@override
State<WelcomeMessageEditor> createState() => _WelcomeMessageEditorState();
}
class _WelcomeMessageEditorState extends State<WelcomeMessageEditor> {
final _welcomeMsg = TextEditingController();
@override
void initState() {
super.initState();
_welcomeMsg.text =
widget.settingsProvider.welcomeMessage.replaceAll('%s', '%name%');
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text("edit_welcome_msg".i18n),
content: TextField(
controller: _welcomeMsg,
autofocus: true,
decoration: InputDecoration(
border: const OutlineInputBorder(),
label: Text('welcome_msg'.i18n),
suffixIcon: IconButton(
icon: const Icon(FeatherIcons.x),
onPressed: () {
setState(() {
_welcomeMsg.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: () {
// var trimmed = _welcomeMsg.text.trim();
// var defLen = trimmed.length;
// var replacedLen = trimmed.replaceAll('%s', '').length;
// if (defLen - 2 > replacedLen) {
// print('fuck yourself rn');
// }
var finalText = _welcomeMsg.text
.trim()
.replaceAll('%s', '')
.replaceFirst('%name%', '%s');
widget.settingsProvider
.update(welcomeMessage: finalText, store: true);
Navigator.of(context).pop(true);
},
),
],
);
}
}