Merge branch 'dev' into master

This commit is contained in:
BalazsManus 2025-02-15 11:29:14 +01:00
commit fc402eef16
56 changed files with 1417 additions and 2589 deletions

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "naplo-plus"]
path = refilc_plus
url = git@github.com:refilc/naplo-plus.git
url = https://git.qwit.cloud/refilc/student-plus.git

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,28 @@
<component name="libraryTable">
<library name="Dart SDK">
<CLASSES>
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/async" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/cli" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/collection" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/convert" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/core" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/developer" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/ffi" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/html" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/indexed_db" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/io" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/isolate" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/js" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/js_util" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/math" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/mirrors" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/svg" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/typed_data" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/web_audio" />
<root url="file:///opt/flutter/bin/cache/dart-sdk/lib/web_gl" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/async" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/cli" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/collection" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/concurrent" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/convert" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/core" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/developer" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/ffi" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/html" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/indexed_db" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/io" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/isolate" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/js" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/js_interop" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/js_interop_unsafe" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/js_util" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/math" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/mirrors" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/svg" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/typed_data" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/web_audio" />
<root url="file://$USER_HOME$/3D Objects/dart-sdk/lib/web_gl" />
</CLASSES>
<JAVADOC />
<SOURCES />

1
.idea/misc.xml generated
View File

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Android API 33, extension level 3 Platform" project-jdk-type="Android SDK" />
</project>

13
.idea/naplo.iml generated
View File

@ -60,8 +60,21 @@
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/build" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/.pub" />
<excludeFolder url="file://$MODULE_DIR$/filcnaplo/linux/flutter/ephemeral/.plugin_symlinks/flutter_acrylic/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/refilc/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/refilc/.pub" />
<excludeFolder url="file://$MODULE_DIR$/refilc/build" />
<excludeFolder url="file://$MODULE_DIR$/refilc_kreta_api/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/refilc_kreta_api/.pub" />
<excludeFolder url="file://$MODULE_DIR$/refilc_kreta_api/build" />
<excludeFolder url="file://$MODULE_DIR$/refilc_mobile_ui/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/refilc_mobile_ui/.pub" />
<excludeFolder url="file://$MODULE_DIR$/refilc_mobile_ui/build" />
<excludeFolder url="file://$MODULE_DIR$/refilc_plus/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/refilc_plus/.pub" />
<excludeFolder url="file://$MODULE_DIR$/refilc_plus/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
</component>
</module>

5
.idea/vcs.xml generated
View File

@ -2,9 +2,6 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/filcnaplo_desktop_ui" vcs="Git" />
<mapping directory="$PROJECT_DIR$/filcnaplo_kreta_api" vcs="Git" />
<mapping directory="$PROJECT_DIR$/filcnaplo_mobile_ui" vcs="Git" />
<mapping directory="$PROJECT_DIR$/filcnaplo_premium" vcs="Git" />
<mapping directory="$PROJECT_DIR$/refilc_plus" vcs="Git" />
</component>
</project>

View File

@ -120,7 +120,8 @@ android {
release {
signingConfig signingConfigs.release
shrinkResources false
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

View File

@ -12,3 +12,5 @@
-dontwarn org.joda.convert.FromString
-dontwarn org.joda.convert.ToString
#-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 69 B

View File

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<foreground>
<inset
android:drawable="@drawable/ic_launcher_foreground"
android:inset="16%" />
</foreground>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 642 B

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -6,7 +6,7 @@
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#03112D</item>
<item name="android:windowSplashScreenBackground">#7CA021</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -6,7 +6,7 @@
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#03112D</item>
<item name="android:windowSplashScreenBackground">#7CA021</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#03112D</color>
<color name="ic_launcher_background">#7CA021</color>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

@ -38,7 +38,7 @@
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="1700" height="1700"/>
<image name="LaunchImage" width="2700" height="2700"/>
<image name="LaunchBackground" width="1" height="1"/>
</resources>
</document>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.transistorsoft.refilcnotification</string>
@ -134,5 +134,5 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</dict>
</plist>

View File

@ -21,46 +21,48 @@ class FilcAPI {
static const baseUrl = "https://api.refilcapp.hu";
// Public API
static const schoolList = "$baseUrl/v3/public/school-list";
static const news = "0.0.0.0/v4/public/news";
static const supporters = "$baseUrl/v3/public/supporters";
static const schoolList = "https://api.refilcapp.hu/v3/public/school-list";
static const news = "https://staticrf-api.pages.dev/news/index.json";
static const supporters = "0.0.0.0";
// Private API
static const ads = "0.0.0.0/v3/private/ads";
static const ads = "0.0.0.0";
static const config = "$baseUrl/v3/private/config";
static const reportApi = "$baseUrl/v3/private/crash-report";
static const rfPlus = "0.0.0.0/v3/rf-plus";
static const plusAuthLogin = "0.0.0.0/auth/login";
static const plusAuthCallback = "0.0.0.0/auth/callback";
static const plusActivation = "0.0.0.0/activate";
static const plusScopes = "0.0.0.0/scopes";
static const rfPlus = "0.0.0.0";
static const plusAuthLogin = "0.0.0.0";
static const plusAuthCallback = "0.0.0.0";
static const plusActivation = "0.0.0.0";
static const plusScopes = "0.0.0.0/";
// Updates
static const repo = "refilc/naplo";
static const releases = "https://api.github.com/repos/$repo/releases";
// Share API
static const themeShare = "$baseUrl/v3/shared/theme/add";
static const themeGet = "$baseUrl/v3/shared/theme/get";
static const themeShare = "https://api.refilcapp.hu/v3/shared/theme/add";
static const themeGet = "https://api.refilcapp.hu/v3/shared/theme/get";
static const allThemes = "$themeGet/all";
static const themeByID = "$themeGet/";
static const gradeColorsShare = "$baseUrl/v3/shared/grade-colors/add";
static const gradeColorsGet = "$baseUrl/v3/shared/grade-colors/get";
static const gradeColorsShare = "https://api.refilcapp.hu/v3/shared/grade-colors/add";
static const gradeColorsGet = "https://api.refilcapp.hu/v3/shared/grade-colors/get";
static const allGradeColors = "$gradeColorsGet/all";
static const gradeColorsByID = "$gradeColorsGet/";
// Payment API
static const payment = "0.0.0.0/v4/payment";
static const stripeSheet = "0.0.0.0/stripe-sheet";
static const payment = "0.0.0.0";
static const stripeSheet = "0.0.0.0";
// Cloud Sync
// cloud sync? for what reason
static const cloudSyncApi = "0.0.0.0/v4/me/cloud-sync";
static const cloudSyncApi = "0.0.0.0";
static Future<bool> checkConnectivity() async =>
(await Connectivity().checkConnectivity())[0] != ConnectivityResult.none;
// nem tudom nem vazar-e senkit se, de mar ertelmetlen ez
static Future<List<School>?> getSchools() async {
try {
http.Response res = await http.get(Uri.parse(schoolList));
@ -70,16 +72,6 @@ class FilcAPI {
.cast<Map>()
.map((json) => School.fromJson(json))
.toList();
schools.add(School(
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 {
throw "HTTP ${res.statusCode}: ${res.body}";
@ -102,9 +94,7 @@ class FilcAPI {
"rf-platform-version": settings.analyticsEnabled
? Platform.operatingSystemVersion
: "unknown",
"rf-app-version": settings.analyticsEnabled
? const String.fromEnvironment("APPVER", defaultValue: "?")
: "unknown",
"rf-app-version": const String.fromEnvironment("APPVER", defaultValue: "?"),
"rf-uinid": settings.xFilcId,
};
@ -135,7 +125,8 @@ class FilcAPI {
http.Response res = await http.get(Uri.parse(news));
if (res.statusCode == 200) {
return (jsonDecode(res.body) as List)
String utf8Body = utf8.decode(res.bodyBytes);
return (jsonDecode(utf8Body) as List)
.cast<Map>()
.map((e) => News.fromJson(e))
.toList();
@ -397,27 +388,6 @@ class FilcAPI {
// cloud sync
static Future<Map?> cloudSync(Map<String, String> data, String token) async {
try {
var client = http.Client();
http.Response res = await client.post(
Uri.parse(cloudSyncApi),
body: data,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $token',
},
);
if (res.statusCode != 200) {
throw "HTTP ${res.statusCode}: ${res.body}";
}
return jsonDecode(res.body);
} on Exception catch (error, stacktrace) {
log("ERROR: FilcAPI.cloudSync: $error $stacktrace");
}
return null;
}
}

View File

@ -18,7 +18,7 @@ class AdProvider extends ChangeNotifier {
}
Future<void> fetch() async {
_ads = await FilcAPI.getAds() ?? [];
_ads = [];
_ads.sort((a, b) => -a.date.compareTo(b.date));
// check for new ads

View File

@ -470,7 +470,7 @@ class SettingsProvider extends ChangeNotifier {
notificationsMessagesEnabled: true,
notificationsLessonsEnabled: true,
notificationsBitfield: 255,
developerMode: false,
developerMode: true,
notificationPollInterval: 1,
vibrate: VibrationStrength.medium,
abWeeks: false,
@ -478,7 +478,7 @@ class SettingsProvider extends ChangeNotifier {
updateChannel: UpdateChannel.stable,
config: Config.fromJson({}),
xFilcId: const Uuid().v4(),
analyticsEnabled: true,
analyticsEnabled: false,
graphClassAvg: false,
goodStudent: false,
presentationMode: false,
@ -506,7 +506,7 @@ class SettingsProvider extends ChangeNotifier {
currentThemeId: '',
currentThemeDisplayName: '',
currentThemeCreator: 'reFilc',
showBreaks: true,
showBreaks: false,
pinSetGeneral: '',
pinSetPersonalize: '',
pinSetNotify: '',

View File

@ -29,11 +29,11 @@ dependencies:
sqflite: ^2.2.0+2
intl: ^0.19.0
provider: ^6.1.1
http: ^1.1.2
http: ^1.2.0
uuid: ^4.2.1
html: ^0.15.0
open_filex: ^4.3.4
path_provider: ^2.0.2
path_provider: ^2.1.3
permission_handler: ^11.0.1
share_plus: ^10.0.3
connectivity_plus: ^6.0.3
@ -53,7 +53,7 @@ dependencies:
dropdown_button2: ^2.3.9
home_widget: ^0.7.0+1
flutter_expandable_fab: ^2.0.0
url_launcher: ^6.1.6
url_launcher: ^6.2.5
flutter_svg: ^2.0.10+1
image_picker: ^1.0.7
animations: ^2.0.1
@ -175,13 +175,13 @@ flutter:
flutter_launcher_icons:
image_path: assets/icons/ic_android.png
android: true
adaptive_icon_background: "#03112D"
adaptive_icon_foreground: assets/icons/ic_android.png
adaptive_icon_background: "#7CA021"
adaptive_icon_foreground: assets/icons/ic_launcher_foreground.png
ios: false
remove_alpha_ios: false
flutter_native_splash:
color: "#03112D"
color: "#7CA021"
image: assets/icons/ic_splash.png
android_12:
image: assets/icons/ic_splash.png

View File

@ -16,6 +16,7 @@ import 'package:refilc_kreta_api/providers/note_provider.dart';
import 'package:refilc_kreta_api/providers/timetable_provider.dart';
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/database_provider.dart';
// import 'package:refilc/utils/format.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc/models/user.dart';
@ -23,20 +24,25 @@ import 'package:refilc/theme/colors/colors.dart';
import 'package:refilc_kreta_api/client/client.dart';
import 'package:refilc_mobile_ui/common/action_button.dart';
import 'package:refilc_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu.dart';
// import 'package:refilc_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu_item.dart';
import 'package:refilc_mobile_ui/common/panel/panel.dart';
import 'package:refilc_mobile_ui/common/panel/panel_button.dart';
import 'package:refilc_mobile_ui/common/profile_image/profile_image.dart';
import 'package:refilc_mobile_ui/common/soon_alert/soon_alert.dart';
// import 'package:refilc_mobile_ui/common/soon_alert/soon_alert.dart';
import 'package:refilc_mobile_ui/common/splitted_panel/splitted_panel.dart';
import 'package:refilc_mobile_ui/common/system_chrome.dart';
// import 'package:refilc_mobile_ui/common/system_chrome.dart';
import 'package:refilc_mobile_ui/common/widgets/update/updates_view.dart';
import 'package:refilc_mobile_ui/screens/news/news_screen.dart';
// import 'package:refilc_mobile_ui/screens/notes/notes_screen.dart';
import 'package:refilc_mobile_ui/screens/settings/accounts/account_tile.dart';
import 'package:refilc_mobile_ui/screens/settings/accounts/account_view.dart';
// import 'package:refilc_mobile_ui/screens/settings/debug/subject_icon_gallery.dart';
// import 'package:refilc_mobile_ui/screens/settings/modify_subject_names.dart';
import 'package:refilc_mobile_ui/screens/settings/notifications_screen.dart';
@ -46,11 +52,14 @@ import 'package:refilc_mobile_ui/screens/settings/submenu/code_scanner.dart';
import 'package:refilc_mobile_ui/screens/settings/submenu/extras_screen.dart';
import 'package:refilc_mobile_ui/screens/settings/submenu/personalize_screen.dart';
import 'package:flutter/foundation.dart';
// import 'package:refilc_plus/models/premium_scopes.dart';
import 'package:refilc_plus/providers/plus_provider.dart';
// import 'package:refilc_plus/ui/mobile/plus/upsell.dart';
// import 'package:refilc_plus/ui/mobile/settings/app_icon_screen.dart';
import 'package:flutter/cupertino.dart';
// import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart' as tabs;
@ -64,6 +73,7 @@ import 'settings_screen.i18n.dart';
import 'package:flutter/services.dart';
import 'package:refilc_mobile_ui/screens/settings/user/nickname.dart';
import 'package:refilc_mobile_ui/screens/settings/user/profile_pic.dart';
// import 'package:refilc_plus/ui/mobile/settings/modify_teacher_names.dart';
// import 'package:refilc_plus/ui/mobile/settings/welcome_message.dart';
// import 'package:refilc_mobile_ui/screens/error_screen.dart';
@ -97,7 +107,8 @@ class SettingsScreenState extends State<SettingsScreen>
late AnimationController _hideContainersController;
Future<void> restore() => Future.wait([
Future<void> restore() =>
Future.wait([
Provider.of<GradeProvider>(context, listen: false).restore(),
Provider.of<TimetableProvider>(context, listen: false).restoreUser(),
Provider.of<ExamProvider>(context, listen: false).restore(),
@ -142,7 +153,8 @@ class SettingsScreenState extends State<SettingsScreen>
name: _firstName,
role: account.role,
profilePictureString: account.picture,
backgroundColor: Theme.of(context)
backgroundColor: Theme
.of(context)
.colorScheme
.tertiary, //!settings.presentationMode
//? ColorUtils.stringToColor(account.name)
@ -156,7 +168,8 @@ class SettingsScreenState extends State<SettingsScreen>
if (err != null) {
showDialog(
context: context,
builder: (_) => AlertDialog(
builder: (_) =>
AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: Text('oopsie'.i18n),
@ -170,16 +183,26 @@ class SettingsScreenState extends State<SettingsScreen>
// delete user
user.removeUser(userId);
await Provider.of<DatabaseProvider>(context,
await Provider
.of<DatabaseProvider>(context,
listen: false)
.store
.removeUser(userId);
// if no users, show login, else login with back button
if (user.getUsers().isNotEmpty) {
user.setUser(user.getUsers().first.id);
if (user
.getUsers()
.isNotEmpty) {
user.setUser(user
.getUsers()
.first
.id);
restore().then(
(_) => user.setUser(user.getUsers().first.id));
(_) =>
user.setUser(user
.getUsers()
.first
.id));
Navigator.of(context).pop();
Navigator.of(context)
@ -190,7 +213,8 @@ class SettingsScreenState extends State<SettingsScreen>
} else {
Navigator.of(context).pop();
Navigator.of(context)
.pushNamedAndRemoveUntil("login", (_) => false);
.pushNamedAndRemoveUntil(
"login", (_) => false);
}
})
],
@ -236,14 +260,17 @@ class SettingsScreenState extends State<SettingsScreen>
]);
}
void _openDKT(User u) => tabs.launchUrl(
void _openDKT(User u) =>
tabs.launchUrl(
Uri.parse(
"https://dkttanulo.e-kreta.hu/sso?id_token=${kretaClient.idToken}"),
customTabsOptions: tabs.CustomTabsOptions(
showTitle: true,
colorSchemes: tabs.CustomTabsColorSchemes(
defaultPrams: tabs.CustomTabsColorSchemeParams(
toolbarColor: Theme.of(context).scaffoldBackgroundColor,
toolbarColor: Theme
.of(context)
.scaffoldBackgroundColor,
),
),
),
@ -309,7 +336,8 @@ class SettingsScreenState extends State<SettingsScreen>
return AnimatedBuilder(
animation: _hideContainersController,
builder: (context, child) => Opacity(
builder: (context, child) =>
Opacity(
opacity: 1 - _hideContainersController.value,
child: Column(
children: [
@ -358,7 +386,10 @@ class SettingsScreenState extends State<SettingsScreen>
Navigator.of(context).pop();
},
icon: Icon(FeatherIcons.x,
color: AppColors.of(context).text.withOpacity(0.8)),
color: AppColors
.of(context)
.text
.withOpacity(0.8)),
),
const SizedBox(
width: 5.0,
@ -379,7 +410,8 @@ class SettingsScreenState extends State<SettingsScreen>
role: user.role,
profilePictureString: user.picture,
gradeStreak: (user.gradeStreak ?? 0) > 1,
backgroundColor: Theme.of(context)
backgroundColor: Theme
.of(context)
.colorScheme
.tertiary, //!settings.presentationMode
//? ColorUtils.stringToColor(user.displayName ?? "?")
@ -403,7 +435,9 @@ class SettingsScreenState extends State<SettingsScreen>
style: TextStyle(
fontSize: 22.0,
fontWeight: FontWeight.w600,
color: AppColors.of(context).text),
color: AppColors
.of(context)
.text),
),
),
),
@ -424,17 +458,14 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
FeatherIcons.info,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12.0), bottom: Radius.circular(4.0)),
),
// cloud-sync
const MenuCloudSyncSettings(
borderRadius: BorderRadius.vertical(
top: Radius.circular(4.0),
bottom: Radius.circular(4.0),
),
top: Radius.circular(12.0),
bottom: Radius.circular(4.0)),
),
// open dcs (digital collaboration space)
PanelButton(
@ -443,10 +474,14 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
FeatherIcons.grid,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(4.0), bottom: Radius.circular(4.0)),
top: Radius.circular(4.0),
bottom: Radius.circular(4.0)),
),
// edit user
PanelButton(
@ -456,10 +491,14 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
FeatherIcons.edit3,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(4.0), bottom: Radius.circular(4.0)),
top: Radius.circular(4.0),
bottom: Radius.circular(4.0)),
),
// switch account
PanelButton(
@ -476,10 +515,14 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
FeatherIcons.users,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(4.0), bottom: Radius.circular(4.0)),
top: Radius.circular(4.0),
bottom: Radius.circular(4.0)),
),
// log user out
PanelButton(
@ -489,15 +532,25 @@ class SettingsScreenState extends State<SettingsScreen>
// delete user
user.removeUser(userId);
await Provider.of<DatabaseProvider>(context, listen: false)
await Provider
.of<DatabaseProvider>(context, listen: false)
.store
.removeUser(userId);
// if no users, show login
if (user.getUsers().isNotEmpty) {
user.setUser(user.getUsers().first.id);
if (user
.getUsers()
.isNotEmpty) {
user.setUser(user
.getUsers()
.first
.id);
restore()
.then((_) => user.setUser(user.getUsers().first.id));
.then((_) =>
user.setUser(user
.getUsers()
.first
.id));
} else {
Navigator.of(context)
.pushNamedAndRemoveUntil("login", (_) => false);
@ -506,11 +559,14 @@ class SettingsScreenState extends State<SettingsScreen>
title: Text("log_out".i18n),
leading: Icon(
FeatherIcons.logOut,
color: AppColors.of(context).red,
color: AppColors
.of(context)
.red,
size: 22.0,
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(4.0), bottom: Radius.circular(12.0)),
top: Radius.circular(4.0),
bottom: Radius.circular(12.0)),
),
// SplittedMenuOption(
// padding: const EdgeInsets.all(8.0),
@ -614,7 +670,10 @@ class SettingsScreenState extends State<SettingsScreen>
updateProvider.releases.first.tag,
style: TextStyle(
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.secondary,
color: Theme
.of(context)
.colorScheme
.secondary,
),
),
),
@ -665,7 +724,8 @@ class SettingsScreenState extends State<SettingsScreen>
if (v) {
showDialog(
context: context,
builder: (context) => WillPopScope(
builder: (context) =>
WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
shape: RoundedRectangleBorder(
@ -679,8 +739,10 @@ class SettingsScreenState extends State<SettingsScreen>
label: "understand".i18n,
onTap: () {
Navigator.of(context).pop();
settings.update(goodStudent: v);
Provider.of<GradeProvider>(context,
settings.update(
goodStudent: v);
Provider.of<GradeProvider>(
context,
listen: false)
.convertBySettings();
})
@ -690,12 +752,16 @@ class SettingsScreenState extends State<SettingsScreen>
);
} else {
settings.update(goodStudent: v);
Provider.of<GradeProvider>(context, listen: false)
Provider.of<GradeProvider>(
context, listen: false)
.convertBySettings();
}
},
value: settings.goodStudent,
activeColor: Theme.of(context).colorScheme.secondary,
activeColor: Theme
.of(context)
.colorScheme
.secondary,
),
),
],
@ -718,7 +784,10 @@ class SettingsScreenState extends State<SettingsScreen>
onChanged: (v) =>
settings.update(presentationMode: v),
value: settings.presentationMode,
activeColor: Theme.of(context).colorScheme.secondary,
activeColor: Theme
.of(context)
.colorScheme
.secondary,
),
),
@ -743,7 +812,8 @@ class SettingsScreenState extends State<SettingsScreen>
],
),
// uwu mode
SplittedPanel(
// since it is not working i removed it
/*SplittedPanel(
cardPadding: const EdgeInsets.all(4.0),
padding: EdgeInsets.zero,
children: [
@ -764,7 +834,7 @@ class SettingsScreenState extends State<SettingsScreen>
),
),
],
),
),*/
],
),
@ -781,14 +851,20 @@ class SettingsScreenState extends State<SettingsScreen>
title: Text(
"grade_streak".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
fontWeight: FontWeight.w500,
),
),
subtitle: Text(
"grade_streak_subtitle".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(0.75),
color: AppColors
.of(context)
.text
.withOpacity(0.75),
),
),
leading: Image.asset(
@ -802,7 +878,10 @@ class SettingsScreenState extends State<SettingsScreen>
trailing: Text(
"${user.gradeStreak}",
style: TextStyle(
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
fontWeight: FontWeight.w500,
fontSize: 18.0,
),
@ -930,7 +1009,10 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
FeatherIcons.sun,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
trailing: Text(
themeModeText,
@ -949,12 +1031,12 @@ class SettingsScreenState extends State<SettingsScreen>
padding: EdgeInsets.only(top: 8.0),
cardPadding: EdgeInsets.all(4.0),
children: [
MenuNotifications(
/*MenuNotifications(
borderRadius: BorderRadius.vertical(
top: Radius.circular(12.0),
bottom: Radius.circular(12.0),
),
),
),*/
],
),
@ -1019,28 +1101,14 @@ class SettingsScreenState extends State<SettingsScreen>
title: Text("other".i18n),
cardPadding: const EdgeInsets.all(4.0),
children: [
PanelButton(
leading: Icon(
Icons.qr_code,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
),
title: Text("qr_scanner".i18n),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const CodeScannerScreen(),
),
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12.0),
bottom: Radius.circular(4.0),
),
),
PanelButton(
leading: Icon(
FeatherIcons.mail,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
title: Text("news".i18n),
onPressed: () => _openNews(context),
@ -1053,11 +1121,15 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
FeatherIcons.map,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
title: Text("stickermap".i18n),
onPressed: () => launchUrl(
Uri.parse("https://stickermap.refilc.hu"),
onPressed: () =>
launchUrl(
Uri.parse("https://map.qwit.cloud"),
mode: LaunchMode.inAppBrowserView,
),
borderRadius: const BorderRadius.vertical(
@ -1104,12 +1176,12 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
FeatherIcons.lock,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
title: Text("privacy".i18n),
// onPressed: () => launchUrl(
// Uri.parse("https://refilc.hu/privacy-policy"),
// mode: LaunchMode.inAppWebView),
onPressed: () => _openPrivacy(context),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12.0),
@ -1120,17 +1192,21 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
FeatherIcons.atSign,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
title: const Text("Discord"),
onPressed: () => launchUrl(Uri.parse("https://dc.refilc.hu"),
onPressed: () =>
launchUrl(Uri.parse("https://discord.gg/6DvjyPAw2T"),
mode: LaunchMode.externalApplication),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(4.0),
bottom: Radius.circular(4.0),
),
),
PanelButton(
/*PanelButton(
leading: Icon(
FeatherIcons.globe,
size: 22.0,
@ -1143,16 +1219,21 @@ class SettingsScreenState extends State<SettingsScreen>
top: Radius.circular(4.0),
bottom: Radius.circular(4.0),
),
),
),*/
PanelButton(
leading: Icon(
FeatherIcons.github,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
title: const Text("Github"),
onPressed: () => launchUrl(
Uri.parse("https://github.com/refilc"),
title: const Text("Gitea"),
onPressed: () =>
launchUrl(
Uri.parse(
"https://git.qwit.cloud/refilc/student-legacy"),
mode: LaunchMode.externalApplication),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(4.0),
@ -1163,7 +1244,10 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
FeatherIcons.award,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
title: Text("licenses".i18n),
onPressed: () => showLicensePage(context: context),
@ -1178,9 +1262,14 @@ class SettingsScreenState extends State<SettingsScreen>
margin: const EdgeInsets.all(10.0),
textStyle: TextStyle(
fontWeight: FontWeight.w500,
color: AppColors.of(context).text),
color: AppColors
.of(context)
.text),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
color: Theme
.of(context)
.colorScheme
.surface,
borderRadius: BorderRadius.circular(12.0),
boxShadow: [
BoxShadow(
@ -1204,24 +1293,35 @@ class SettingsScreenState extends State<SettingsScreen>
FeatherIcons.barChart2,
size: 22.0,
color: settings.analyticsEnabled
? AppColors.of(context).text.withOpacity(0.95)
: AppColors.of(context).text.withOpacity(.25),
? AppColors
.of(context)
.text
.withOpacity(0.95)
: AppColors
.of(context)
.text
.withOpacity(.25),
),
title: Text(
"Analytics".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
color: AppColors.of(context).text.withOpacity(
color: AppColors
.of(context)
.text
.withOpacity(
settings.analyticsEnabled ? 1.0 : .5),
),
),
subtitle: Text(
"Anonymous Usage Analytics".i18n,
style: TextStyle(
color: AppColors.of(context)
color: AppColors
.of(context)
.text
.withOpacity(settings.analyticsEnabled ? .5 : .2),
.withOpacity(
settings.analyticsEnabled ? .5 : .2),
),
),
onChanged: (v) {
@ -1236,7 +1336,10 @@ class SettingsScreenState extends State<SettingsScreen>
settings.update(analyticsEnabled: v);
},
value: settings.analyticsEnabled,
activeColor: Theme.of(context).colorScheme.secondary,
activeColor: Theme
.of(context)
.colorScheme
.secondary,
),
),
),
@ -1244,10 +1347,14 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
Icons.feedback_outlined,
size: 22.0,
color: AppColors.of(context).text.withOpacity(0.95),
color: AppColors
.of(context)
.text
.withOpacity(0.95),
),
title: Text("feedback".i18n),
onPressed: () => {
onPressed: () =>
{
Shake.setScreenshotIncluded(false),
Shake.show(ShakeScreen.newTicket),
Shake.setScreenshotIncluded(true),
@ -1268,7 +1375,8 @@ class SettingsScreenState extends State<SettingsScreen>
PanelButton(
title: const Text('loginToGoogle'),
onPressed: () async {
ThirdPartyProvider tpp = Provider.of<ThirdPartyProvider>(
ThirdPartyProvider tpp = Provider.of<
ThirdPartyProvider>(
context,
listen: false);
@ -1291,7 +1399,7 @@ class SettingsScreenState extends State<SettingsScreen>
],
),
// developer options
if (settings.developerMode)
if (true)
SplittedPanel(
title: Text("devsettings".i18n),
cardPadding: const EdgeInsets.all(4.0),
@ -1306,10 +1414,15 @@ class SettingsScreenState extends State<SettingsScreen>
top: Radius.circular(12.0),
bottom: Radius.circular(4.0))),
title: Text("devmode".i18n,
style: const TextStyle(fontWeight: FontWeight.w500)),
onChanged: (v) => settings.update(developerMode: false),
style: const TextStyle(
fontWeight: FontWeight.w500)),
onChanged: (v) =>
settings.update(developerMode: false),
value: settings.developerMode,
activeColor: Theme.of(context).colorScheme.secondary,
activeColor: Theme
.of(context)
.colorScheme
.secondary,
),
),
PanelButton(
@ -1320,17 +1433,22 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
Icons.tune_outlined,
size: 22.0,
color: AppColors.of(context).text.withOpacity(.95),
color: AppColors
.of(context)
.text
.withOpacity(.95),
),
title: Text("exp_settings".i18n),
onPressed: () => Clipboard.setData(ClipboardData(
onPressed: () =>
Clipboard.setData(ClipboardData(
text: json.encode(settings.toMap()),
)),
),
PanelButton(
borderRadius: BorderRadius.vertical(
top: const Radius.circular(4.0),
bottom: Provider.of<PlusProvider>(context, listen: false)
bottom: Provider
.of<PlusProvider>(context, listen: false)
.hasPremium
? const Radius.circular(4.0)
: const Radius.circular(12.0),
@ -1338,14 +1456,20 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
FeatherIcons.copy,
size: 22.0,
color: AppColors.of(context).text.withOpacity(.95),
color: AppColors
.of(context)
.text
.withOpacity(.95),
),
title: Text("copy_jwt".i18n),
onPressed: () => Clipboard.setData(ClipboardData(
text: Provider.of<KretaClient>(context, listen: false)
onPressed: () =>
Clipboard.setData(ClipboardData(
text: Provider
.of<KretaClient>(context, listen: false)
.accessToken!)),
),
if (Provider.of<PlusProvider>(context, listen: false)
if (Provider
.of<PlusProvider>(context, listen: false)
.hasPremium)
PanelButton(
borderRadius: const BorderRadius.vertical(
@ -1355,7 +1479,10 @@ class SettingsScreenState extends State<SettingsScreen>
leading: Icon(
FeatherIcons.key,
size: 22.0,
color: AppColors.of(context).text.withOpacity(.95),
color: AppColors
.of(context)
.text
.withOpacity(.95),
),
title: const Text("Remove Premium"),
onPressed: () {
@ -1363,7 +1490,8 @@ class SettingsScreenState extends State<SettingsScreen>
.activate(removePremium: true);
settings.update(
accentColor: AccentColor.filc, store: true);
Provider.of<ThemeModeObserver>(context, listen: false)
Provider.of<ThemeModeObserver>(
context, listen: false)
.changeTheme(settings.theme);
},
),
@ -1378,34 +1506,27 @@ class SettingsScreenState extends State<SettingsScreen>
child: FutureBuilder<Map>(
future: futureRelease,
builder: (context, release) {
if (release.hasData) {
return DefaultTextStyle(
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(
fontWeight: FontWeight.w600,
color: AppColors.of(context)
.text
.withOpacity(0.65)),
child: Text("v${release.data!['version']}, módosítva a Filc csapat által"),
);
String versionText;
if (release.hasData && release.data != null) {
versionText = "v${release
.data!['version']}, modosítva a Filc csapat által";
} else {
String envAppVer = const String.fromEnvironment(
"APPVER",
defaultValue: "?");
versionText =
"reFilc, modosítva a Filc csapat által";
}
return DefaultTextStyle(
style: Theme.of(context)
style: Theme
.of(context)
.textTheme
.titleMedium!
.copyWith(
fontWeight: FontWeight.w600,
color: AppColors.of(context)
color: AppColors
.of(context)
.text
.withOpacity(0.65)),
child: Text("v$envAppVer"),
child: Text(versionText),
);
}
},
),
onTap: () {
@ -1432,16 +1553,20 @@ class SettingsScreenState extends State<SettingsScreen>
),
],
),
),
)
,
);
}
void _openNews(BuildContext context) =>
Navigator.of(context, rootNavigator: true)
.push(CupertinoPageRoute(builder: (context) => const NewsScreen()));
void _openUpdates(BuildContext context) =>
UpdateView.show(updateProvider.releases.first, context: context);
void _openPrivacy(BuildContext context) => PrivacyView.show(context);
// void _openNotes(BuildContext context, Map<String, bool> doneItems) async =>
// Navigator.of(context, rootNavigator: true).push(CupertinoPageRoute(
// builder: (context) => NotesScreen(

@ -1 +1 @@
Subproject commit 478edbefc025f89270c345bd3b3e8ab3325a30f9
Subproject commit d760ad7d19108b087e1c0f0b199a198bfd36aea1