diff --git a/refilc/.gitignore b/refilc/.gitignore index f991fd7..4257c18 100644 --- a/refilc/.gitignore +++ b/refilc/.gitignore @@ -46,4 +46,32 @@ app.*.map.json .symlinks/ Pods Podfile.lock -UserInterfaceState.xcuserstate \ No newline at end of file +UserInterfaceState.xcuserstate +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/.last_build_id +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* diff --git a/refilc/build-ipa.sh b/refilc/build-ipa.sh old mode 100644 new mode 100755 diff --git a/refilc/ios/Runner.xcodeproj/project.pbxproj b/refilc/ios/Runner.xcodeproj/project.pbxproj index e4a26c0..2cc04b9 100644 --- a/refilc/ios/Runner.xcodeproj/project.pbxproj +++ b/refilc/ios/Runner.xcodeproj/project.pbxproj @@ -517,8 +517,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 250; - DEVELOPMENT_TEAM = UT7MSP4GWZ; + CURRENT_PROJECT_VERSION = 255; + DEVELOPMENT_TEAM = 4DKAF249F3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = reFilc; @@ -528,7 +528,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 5.0.0; - PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo; + PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -549,8 +549,8 @@ CODE_SIGN_ENTITLEMENTS = livecard/livecard.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 250; - DEVELOPMENT_TEAM = UT7MSP4GWZ; + CURRENT_PROJECT_VERSION = 255; + DEVELOPMENT_TEAM = 4DKAF249F3; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = livecard/Info.plist; @@ -566,7 +566,7 @@ MARKETING_VERSION = 5.0.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo.livecardpro; + PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo.livecardpro; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -591,8 +591,8 @@ CODE_SIGN_ENTITLEMENTS = livecard/livecard.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 250; - DEVELOPMENT_TEAM = UT7MSP4GWZ; + CURRENT_PROJECT_VERSION = 255; + DEVELOPMENT_TEAM = 4DKAF249F3; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = livecard/Info.plist; @@ -607,7 +607,7 @@ ); MARKETING_VERSION = 5.0.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo.livecardpro; + PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo.livecardpro; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -631,8 +631,8 @@ CODE_SIGN_ENTITLEMENTS = livecard/livecard.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 250; - DEVELOPMENT_TEAM = UT7MSP4GWZ; + CURRENT_PROJECT_VERSION = 255; + DEVELOPMENT_TEAM = 4DKAF249F3; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = livecard/Info.plist; @@ -647,7 +647,7 @@ ); MARKETING_VERSION = 5.0.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo.livecardpro; + PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo.livecardpro; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -775,8 +775,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 250; - DEVELOPMENT_TEAM = UT7MSP4GWZ; + CURRENT_PROJECT_VERSION = 255; + DEVELOPMENT_TEAM = 4DKAF249F3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = reFilc; @@ -786,7 +786,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 5.0.0; - PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo; + PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -803,8 +803,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 250; - DEVELOPMENT_TEAM = UT7MSP4GWZ; + CURRENT_PROJECT_VERSION = 255; + DEVELOPMENT_TEAM = 4DKAF249F3; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = reFilc; @@ -814,7 +814,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 5.0.0; - PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo; + PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/refilc/ios/Runner.xcodeproj/xcuserdata/tmarccci.xcuserdatad/xcschemes/xcschememanagement.plist b/refilc/ios/Runner.xcodeproj/xcuserdata/tmarccci.xcuserdatad/xcschemes/xcschememanagement.plist index 1c0c029..1970da8 100644 --- a/refilc/ios/Runner.xcodeproj/xcuserdata/tmarccci.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/refilc/ios/Runner.xcodeproj/xcuserdata/tmarccci.xcuserdatad/xcschemes/xcschememanagement.plist @@ -12,7 +12,7 @@ livecard.xcscheme_^#shared#^_ orderHint - 78 + 83 diff --git a/refilc/ios/Runner.xcworkspace/xcuserdata/tmarccci.xcuserdatad/UserInterfaceState.xcuserstate b/refilc/ios/Runner.xcworkspace/xcuserdata/tmarccci.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 45ab200..0000000 Binary files a/refilc/ios/Runner.xcworkspace/xcuserdata/tmarccci.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/refilc/ios/Runner/Runner.entitlements b/refilc/ios/Runner/Runner.entitlements index 8e1d462..127fede 100644 --- a/refilc/ios/Runner/Runner.entitlements +++ b/refilc/ios/Runner/Runner.entitlements @@ -5,8 +5,6 @@ aps-environment development com.apple.security.application-groups - - group.refilc2.livecard - + diff --git a/refilc/ios/livecard/livecard.entitlements b/refilc/ios/livecard/livecard.entitlements index 8e1d462..127fede 100644 --- a/refilc/ios/livecard/livecard.entitlements +++ b/refilc/ios/livecard/livecard.entitlements @@ -5,8 +5,6 @@ aps-environment development com.apple.security.application-groups - - group.refilc2.livecard - + diff --git a/refilc/lib/api/providers/self_note_provider.dart b/refilc/lib/api/providers/self_note_provider.dart index b19816c..4be70a5 100644 --- a/refilc/lib/api/providers/self_note_provider.dart +++ b/refilc/lib/api/providers/self_note_provider.dart @@ -7,23 +7,33 @@ import 'package:provider/provider.dart'; class SelfNoteProvider with ChangeNotifier { late List _notes; + late List _todoItems; late BuildContext _context; + List get notes => _notes; + List get todos => _todoItems; SelfNoteProvider({ List initialNotes = const [], + List initialTodoItems = const [], required BuildContext context, }) { _notes = List.castFrom(initialNotes); + _todoItems = List.castFrom(initialTodoItems); _context = context; if (_notes.isEmpty) restore(); + if (_todoItems.isEmpty) restoreTodo(); } // restore self notes from db Future restore() async { String? userId = Provider.of(_context, listen: false).id; + // await Provider.of(_context, listen: false) + // .userStore + // .storeSelfNotes([], userId: userId!); + // load self notes from db if (userId != null) { var dbNotes = await Provider.of(_context, listen: false) @@ -34,6 +44,24 @@ class SelfNoteProvider with ChangeNotifier { } } + // restore todo items from db + Future restoreTodo() async { + String? userId = Provider.of(_context, listen: false).id; + + // await Provider.of(_context, listen: false) + // .userStore + // .storeSelfNotes([], userId: userId!); + + // load self notes from db + if (userId != null) { + var dbTodo = await Provider.of(_context, listen: false) + .userQuery + .getTodoItems(userId: userId); + _todoItems = dbTodo; + notifyListeners(); + } + } + // fetches fresh data from api (not needed, cuz no api for that) // Future fetch() async { // } @@ -50,4 +78,17 @@ class SelfNoteProvider with ChangeNotifier { _notes = notes; notifyListeners(); } + + // store todo items in db + Future storeTodo(List todos) async { + User? user = Provider.of(_context, listen: false).user; + if (user == null) throw "Cannot store Self Notes for User null"; + String userId = user.id; + + await Provider.of(_context, listen: false) + .userStore + .storeSelfTodoItems(todos, userId: userId); + _todoItems = todos; + notifyListeners(); + } } diff --git a/refilc/lib/database/init.dart b/refilc/lib/database/init.dart index d06a600..0d2822a 100644 --- a/refilc/lib/database/init.dart +++ b/refilc/lib/database/init.dart @@ -52,6 +52,9 @@ const settingsDB = DatabaseStruct("settings", { "nav_shadow": int, "new_colors": int, "uwu_mode": int, + // quick settings + "q_timetable_lesson_num": int, "q_timetable_sub_tiles": int, + "q_subjects_sub_tiles": int, }); // DON'T FORGET TO UPDATE DEFAULT VALUES IN `initDB` MIGRATION OR ELSE PARENTS WILL COMPLAIN ABOUT THEIR CHILDREN MISSING // YOU'VE BEEN WARNED!!! @@ -81,7 +84,7 @@ const userDataDB = DatabaseStruct("user_data", { "goal_befores": String, "goal_pin_dates": String, // todo and notes - "todo_items": String, "self_notes": String, + "todo_items": String, "self_notes": String, "self_todo": String, // v5 shit "roundings": String, "grade_rarities": String, @@ -149,7 +152,7 @@ Future initDB(DatabaseProvider database) async { "goal_befores": "{}", "goal_pin_dates": "{}", // todo and notes - "todo_items": "{}", "self_notes": "[]", + "todo_items": "{}", "self_notes": "[]", "self_todo": "[]", // v5 shit "roundings": "{}", "grade_rarities": "{}", diff --git a/refilc/lib/database/query.dart b/refilc/lib/database/query.dart index e899e6d..741a253 100644 --- a/refilc/lib/database/query.dart +++ b/refilc/lib/database/query.dart @@ -317,6 +317,18 @@ class UserDatabaseQuery { return selfNotes; } + Future> getTodoItems({required String userId}) async { + List userData = + await db.query("user_data", where: "id = ?", whereArgs: [userId]); + if (userData.isEmpty) return []; + String? todoItemsJson = userData.elementAt(0)["self_todo"] as String?; + if (todoItemsJson == null) return []; + List todoItems = (jsonDecode(todoItemsJson) as List) + .map((e) => TodoItem.fromJson(e)) + .toList(); + return todoItems; + } + // v5 Future> getRoundings({required String userId}) async { List userData = diff --git a/refilc/lib/database/store.dart b/refilc/lib/database/store.dart index 6132780..46929c0 100644 --- a/refilc/lib/database/store.dart +++ b/refilc/lib/database/store.dart @@ -196,6 +196,13 @@ class UserDatabaseStore { where: "id = ?", whereArgs: [userId]); } + Future storeSelfTodoItems(List todoItems, + {required String userId}) async { + String todoItemsJson = jsonEncode(todoItems.map((e) => e.json).toList()); + await db.update("user_data", {"self_todo": todoItemsJson}, + where: "id = ?", whereArgs: [userId]); + } + // v5 Future storeRoundings(Map roundings, {required String userId}) async { diff --git a/refilc/lib/models/self_note.dart b/refilc/lib/models/self_note.dart index 609f6ff..cbaf42b 100644 --- a/refilc/lib/models/self_note.dart +++ b/refilc/lib/models/self_note.dart @@ -1,13 +1,18 @@ +enum NoteType { text, image } + class SelfNote { String id; String? title; String content; + NoteType noteType; + Map? json; SelfNote({ required this.id, this.title, required this.content, + required this.noteType, this.json, }); @@ -16,6 +21,7 @@ class SelfNote { id: json['id'], title: json['title'], content: json['content'], + noteType: json['note_type'] == 'image' ? NoteType.image : NoteType.text, json: json, ); } @@ -24,5 +30,40 @@ class SelfNote { 'id': id, 'title': title, 'content': content, + 'note_type': noteType == NoteType.image ? 'image' : 'text', + }; +} + +class TodoItem { + String id; + String title; + String content; + bool done; + + Map? json; + + TodoItem({ + required this.id, + required this.title, + required this.content, + required this.done, + this.json, + }); + + factory TodoItem.fromJson(Map json) { + return TodoItem( + id: json['id'], + title: json['title'], + content: json['content'], + done: json['done'], + json: json, + ); + } + + get toJson => { + 'id': id, + 'title': title, + 'content': content, + 'done': done, }; } diff --git a/refilc/lib/models/settings.dart b/refilc/lib/models/settings.dart index bb1433a..72d23f1 100644 --- a/refilc/lib/models/settings.dart +++ b/refilc/lib/models/settings.dart @@ -105,6 +105,10 @@ class SettingsProvider extends ChangeNotifier { bool _navShadow; bool _newColors; bool _uwuMode; + // quick settings + bool _qTimetableLessonNum; + bool _qTimetableSubTiles; + bool _qSubjectsSubTiles; SettingsProvider({ DatabaseProvider? database, @@ -172,6 +176,9 @@ class SettingsProvider extends ChangeNotifier { required bool navShadow, required bool newColors, required bool uwuMode, + required bool qTimetableLessonNum, + required bool qTimetableSubTiles, + required bool qSubjectsSubTiles, }) : _database = database, _language = language, _startPage = startPage, @@ -236,7 +243,10 @@ class SettingsProvider extends ChangeNotifier { _calendarId = calendarId, _navShadow = navShadow, _newColors = newColors, - _uwuMode = uwuMode; + _uwuMode = uwuMode, + _qTimetableLessonNum = qTimetableLessonNum, + _qTimetableSubTiles = qTimetableSubTiles, + _qSubjectsSubTiles = qSubjectsSubTiles; factory SettingsProvider.fromMap(Map map, {required DatabaseProvider database}) { @@ -321,6 +331,9 @@ class SettingsProvider extends ChangeNotifier { navShadow: map['nav_shadow'] == 1, newColors: map['new_colors'] == 1, uwuMode: map['uwu_mode'] == 1, + qTimetableLessonNum: map['q_timetable_lesson_num'] == 1, + qTimetableSubTiles: map['q_timetable_sub_tiles'] == 1, + qSubjectsSubTiles: map['q_subjects_sub_tiles'] == 1, ); } @@ -393,6 +406,9 @@ class SettingsProvider extends ChangeNotifier { "nav_shadow": _navShadow ? 1 : 0, "new_colors": _newColors ? 1 : 0, "uwu_mode": _uwuMode ? 1 : 0, + "q_timetable_lesson_num": _qTimetableLessonNum ? 1 : 0, + "q_timetable_sub_tiles": _qTimetableSubTiles ? 1 : 0, + "q_subjects_sub_tiles": _qSubjectsSubTiles ? 1 : 0, }; } @@ -469,6 +485,9 @@ class SettingsProvider extends ChangeNotifier { navShadow: true, newColors: true, uwuMode: false, + qTimetableLessonNum: true, + qTimetableSubTiles: true, + qSubjectsSubTiles: true, ); } @@ -536,6 +555,9 @@ class SettingsProvider extends ChangeNotifier { bool get navShadow => _navShadow; bool get newColors => _newColors; bool get uwuMode => _uwuMode; + bool get qTimetableLessonNum => _qTimetableLessonNum; + bool get qTimetableSubTiles => _qTimetableSubTiles; + bool get qSubjectsSubTiles => _qSubjectsSubTiles; Future update({ bool store = true, @@ -599,6 +621,9 @@ class SettingsProvider extends ChangeNotifier { bool? navShadow, bool? newColors, bool? uwuMode, + bool? qTimetableLessonNum, + bool? qTimetableSubTiles, + bool? qSubjectsSubTiles, }) async { if (language != null && language != _language) _language = language; if (startPage != null && startPage != _startPage) _startPage = startPage; @@ -669,7 +694,7 @@ class SettingsProvider extends ChangeNotifier { if (bellDelay != null && bellDelay != _bellDelay) _bellDelay = bellDelay; if (bellDelayEnabled != null && bellDelayEnabled != _bellDelayEnabled) { _bellDelayEnabled = bellDelayEnabled; - if(Platform.isIOS){ + if (Platform.isIOS) { LiveCardProvider.hasActivitySettingsChanged = true; } } @@ -781,6 +806,17 @@ class SettingsProvider extends ChangeNotifier { if (uwuMode != null && uwuMode != _uwuMode) { _uwuMode = uwuMode; } + if (qTimetableLessonNum != null && + qTimetableLessonNum != _qTimetableLessonNum) { + _qTimetableLessonNum = qTimetableLessonNum; + } + if (qTimetableSubTiles != null && + qTimetableSubTiles != _qTimetableSubTiles) { + _qTimetableSubTiles = qTimetableSubTiles; + } + if (qSubjectsSubTiles != null && qSubjectsSubTiles != _qSubjectsSubTiles) { + _qSubjectsSubTiles = qSubjectsSubTiles; + } // store or not if (store) await _database?.store.storeSettings(this); notifyListeners(); diff --git a/refilc/lib/theme/colors/accent.dart b/refilc/lib/theme/colors/accent.dart index 152046a..df27bc0 100644 --- a/refilc/lib/theme/colors/accent.dart +++ b/refilc/lib/theme/colors/accent.dart @@ -31,3 +31,66 @@ Map accentColorMap = { AccentColor.adaptive: const Color(0xFF3D7BF4), AccentColor.custom: const Color(0xFF3D7BF4), }; + +// new v5 things +Map lightPrimary = { + AccentColor.filc: const Color(0xFF050B15), +}; +Map lightSecondary = { + AccentColor.filc: const Color(0xFF3F444F), +}; +Map lightTeritary = { + AccentColor.filc: const Color(0xFF1C469A), +}; +Map lightIcon = { + AccentColor.filc: const Color(0xFF0A2456), +}; +Map lightAccent = { + AccentColor.filc: const Color(0xFF487DE6), +}; +Map lightBgDarkened = { + AccentColor.filc: const Color(0xFFB9C8E5), +}; +Map lightBtnSecStrk = { + AccentColor.filc: const Color(0xFFCEDBF5), +}; +Map lightBg = { + AccentColor.filc: const Color(0xFFDAE4F7), +}; +Map lightCard = { + AccentColor.filc: const Color(0xFFEDF3FF), +}; +Map lightBtnSec = { + AccentColor.filc: const Color(0xFFFBFCFF), +}; + +Map darkPrimary = { + AccentColor.filc: const Color(0xFFEBF1FD), +}; +Map darkSecondary = { + AccentColor.filc: const Color(0xFFCFD8E9), +}; +Map darkTeritary = { + AccentColor.filc: const Color(0xFFAEC8FC), +}; +Map darkIcon = { + AccentColor.filc: const Color(0xFFBAD1FF), +}; +Map darkAccent = { + AccentColor.filc: const Color(0xFF487DE6), +}; +Map darkBgDarkened = { + AccentColor.filc: const Color(0xFF010205), +}; +Map darkBtnSecStrk = { + AccentColor.filc: const Color(0xFF1C2230), +}; +Map darkBg = { + AccentColor.filc: const Color(0xFF070A0E), +}; +Map darkCard = { + AccentColor.filc: const Color(0xFF0F131B), +}; +Map darkBtnSec = { + AccentColor.filc: const Color(0xFF131822), +}; diff --git a/refilc/lib/theme/colors/new_colors.dart b/refilc/lib/theme/colors/new_colors.dart new file mode 100644 index 0000000..aa7f080 --- /dev/null +++ b/refilc/lib/theme/colors/new_colors.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; + +class NewColors extends ThemeExtension { + const NewColors({ + required this.accent, + required this.primary, + required this.secondary, + required this.teritary, + required this.icon, + required this.darkenBg, + required this.btnSecStrk, + required this.background, + required this.card, + required this.btnSec, + }); + + final Color? accent; + final Color? primary; + final Color? secondary; + final Color? teritary; + final Color? icon; + final Color? darkenBg; + final Color? btnSecStrk; + final Color? background; + final Color? card; + final Color? btnSec; + + @override + NewColors copyWith({ + Color? accent, + Color? primary, + Color? secondary, + Color? teritary, + Color? icon, + Color? darkenBg, + Color? btnSecStrk, + Color? background, + Color? card, + Color? btnSec, + }) { + return NewColors( + accent: accent ?? this.accent, + primary: primary ?? this.primary, + secondary: secondary ?? this.secondary, + teritary: teritary ?? this.teritary, + icon: icon ?? this.icon, + darkenBg: darkenBg ?? this.darkenBg, + btnSecStrk: btnSecStrk ?? this.btnSecStrk, + background: background ?? this.background, + card: card ?? this.card, + btnSec: btnSec ?? this.btnSec, + ); + } + + @override + NewColors lerp(NewColors? other, double t) { + if (other is! NewColors) { + return this; + } + return NewColors( + accent: Color.lerp(accent, other.accent, t), + primary: Color.lerp(primary, other.primary, t), + secondary: Color.lerp(secondary, other.secondary, t), + teritary: Color.lerp(teritary, other.teritary, t), + icon: Color.lerp(icon, other.icon, t), + darkenBg: Color.lerp(darkenBg, other.darkenBg, t), + btnSecStrk: Color.lerp(btnSecStrk, other.btnSecStrk, t), + background: Color.lerp(background, other.background, t), + card: Color.lerp(card, other.card, t), + btnSec: Color.lerp(btnSec, other.btnSec, t), + ); + } +} diff --git a/refilc/lib/theme/theme.dart b/refilc/lib/theme/theme.dart index 1acae12..e1a9215 100644 --- a/refilc/lib/theme/theme.dart +++ b/refilc/lib/theme/theme.dart @@ -87,6 +87,20 @@ class AppTheme { amount: 0.4); // white mode: same tertiary as secondary return ThemeData( + // extensions: [ + // NewColors( + // accent: lightAccent[accentColor]!, + // primary: lightPrimary[accentColor]!, + // secondary: lightSecondary[accentColor]!, + // teritary: lightTeritary[accentColor]!, + // icon: lightIcon[accentColor]!, + // darkenBg: lightBgDarkened[accentColor]!, + // btnSecStrk: lightBtnSecStrk[accentColor]!, + // background: lightBg[accentColor]!, + // card: lightCard[accentColor]!, + // btnSec: lightBtnSec[accentColor]!, + // ), + // ], brightness: Brightness.light, useMaterial3: true, fontFamily: _defaultFontFamily, @@ -198,6 +212,20 @@ class AppTheme { amount: 0.1); // dark mode: tertiary is way darker than secondary return ThemeData( + // extensions: [ + // NewColors( + // accent: darkAccent[accentColor]!, + // primary: darkPrimary[accentColor]!, + // secondary: darkSecondary[accentColor]!, + // teritary: darkTeritary[accentColor]!, + // icon: darkIcon[accentColor]!, + // darkenBg: darkBgDarkened[accentColor]!, + // btnSecStrk: darkBtnSecStrk[accentColor]!, + // background: darkBg[accentColor]!, + // card: darkCard[accentColor]!, + // btnSec: darkBtnSec[accentColor]!, + // ), + // ], brightness: Brightness.dark, useMaterial3: true, fontFamily: _defaultFontFamily, diff --git a/refilc/lib/utils/reverse_search.dart b/refilc/lib/utils/reverse_search.dart index 35e3425..9380806 100644 --- a/refilc/lib/utils/reverse_search.dart +++ b/refilc/lib/utils/reverse_search.dart @@ -1,8 +1,11 @@ import 'dart:developer'; import 'package:refilc_kreta_api/models/absence.dart'; +import 'package:refilc_kreta_api/models/exam.dart'; import 'package:refilc_kreta_api/models/lesson.dart'; +import 'package:refilc_kreta_api/models/subject.dart'; import 'package:refilc_kreta_api/models/week.dart'; +import 'package:refilc_kreta_api/providers/grade_provider.dart'; import 'package:refilc_kreta_api/providers/timetable_provider.dart'; import 'package:flutter/cupertino.dart'; import 'package:provider/provider.dart'; @@ -38,7 +41,54 @@ class ReverseSearch { } } + static Future getLessonByExam( + Exam exam, BuildContext context) async { + final timetableProvider = + Provider.of(context, listen: false); + + List lessons = []; + final week = Week.fromDate(exam.writeDate); + try { + await timetableProvider.fetch(week: week); + } catch (e) { + log("[ERROR] getLessonByAbsence: $e"); + } + lessons = timetableProvider.getWeek(week) ?? []; + + // Find absence lesson in timetable + Lesson lesson = lessons.firstWhere( + (l) => + _sameDate(l.date, exam.writeDate) && l.subject.id == exam.subject.id, + orElse: () => Lesson.fromJson({'isEmpty': true}), + ); + + if (lesson.isEmpty) { + return null; + } else { + return lesson; + } + } + // difference.inDays is not reliable static bool _sameDate(DateTime a, DateTime b) => (a.year == b.year && a.month == b.month && a.day == b.day); + + static Future getSubjectByLesson( + Lesson lesson, BuildContext context) async { + final gradeProvider = Provider.of(context, listen: false); + + try { + await gradeProvider.fetch(); + } catch (e) { + log("[ERROR] getSubjectByLesson: $e"); + } + + try { + return gradeProvider.grades.map((e) => e.subject).firstWhere( + (s) => s.id == lesson.subject.id, + ); + } catch (e) { + return null; + } + } } diff --git a/refilc/pubspec.yaml b/refilc/pubspec.yaml index 39cc8c6..904b70f 100644 --- a/refilc/pubspec.yaml +++ b/refilc/pubspec.yaml @@ -3,7 +3,7 @@ description: "Egy nem hivatalos e-KRÉTA kliens, diákoktól diákoknak." homepage: https://refilc.hu publish_to: "none" -version: 5.0.0+253 +version: 5.0.0+258 environment: sdk: ">=2.17.0 <=3.3.2" diff --git a/refilc_mobile_ui/lib/common/bottom_sheet_menu/rounded_bottom_sheet.dart b/refilc_mobile_ui/lib/common/bottom_sheet_menu/rounded_bottom_sheet.dart index 7bb4977..9a64aa2 100644 --- a/refilc_mobile_ui/lib/common/bottom_sheet_menu/rounded_bottom_sheet.dart +++ b/refilc_mobile_ui/lib/common/bottom_sheet_menu/rounded_bottom_sheet.dart @@ -8,19 +8,21 @@ class RoundedBottomSheet extends StatelessWidget { this.borderRadius = 16.0, this.shrink = true, this.showHandle = true, + this.backgroundColor, }); final Widget? child; final double borderRadius; final bool shrink; final bool showHandle; + final Color? backgroundColor; @override Widget build(BuildContext context) { return AnimatedContainer( duration: const Duration(milliseconds: 500), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: backgroundColor ?? Theme.of(context).colorScheme.background, borderRadius: BorderRadius.only( topLeft: Radius.circular(borderRadius), topRight: Radius.circular(borderRadius), @@ -52,6 +54,7 @@ Future showRoundedModalBottomSheet( required Widget child, bool rootNavigator = true, bool showHandle = true, + Color? backgroundColor, }) async { return await showModalBottomSheet( useSafeArea: false, @@ -62,6 +65,7 @@ Future showRoundedModalBottomSheet( useRootNavigator: rootNavigator, isScrollControlled: true, builder: (context) => RoundedBottomSheet( + backgroundColor: backgroundColor, showHandle: showHandle, child: child, ), diff --git a/refilc_mobile_ui/lib/common/widgets/exam/exam_viewable.dart b/refilc_mobile_ui/lib/common/widgets/exam/exam_viewable.dart index 0366af7..60a67ab 100644 --- a/refilc_mobile_ui/lib/common/widgets/exam/exam_viewable.dart +++ b/refilc_mobile_ui/lib/common/widgets/exam/exam_viewable.dart @@ -1,8 +1,19 @@ +// ignore_for_file: use_build_context_synchronously + +import 'package:flutter_svg/svg.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; +import 'package:refilc/helpers/subject.dart'; +import 'package:refilc/models/settings.dart'; +import 'package:refilc/theme/colors/colors.dart'; +import 'package:refilc/theme/colors/utils.dart'; +import 'package:refilc/utils/format.dart'; +import 'package:refilc/utils/reverse_search.dart'; import 'package:refilc_kreta_api/models/exam.dart'; -import 'package:refilc_mobile_ui/common/viewable.dart'; -import 'package:refilc_mobile_ui/common/widgets/card_handle.dart'; +import 'package:refilc_kreta_api/models/lesson.dart'; +import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart'; +import 'package:refilc_mobile_ui/common/round_border_icon.dart'; import 'package:refilc_mobile_ui/common/widgets/exam/exam_tile.dart'; -import 'package:refilc_mobile_ui/common/widgets/exam/exam_view.dart'; import 'package:flutter/material.dart'; class ExamViewable extends StatelessWidget { @@ -15,13 +26,335 @@ class ExamViewable extends StatelessWidget { @override Widget build(BuildContext context) { - return Viewable( - tile: ExamTile( + return GestureDetector( + onTap: () => ExamPopup.show(context: context, exam: exam), + child: ExamTile( exam, showSubject: showSubject, padding: tilePadding, ), - view: CardHandle(child: ExamView(exam)), + ); + // return Viewable( + // tile: ExamTile( + // exam, + // showSubject: showSubject, + // padding: tilePadding, + // ), + // view: CardHandle(child: ExamView(exam)), + // ); + } +} + +class ExamPopup extends StatelessWidget { + const ExamPopup({ + super.key, + required this.exam, + required this.outsideContext, + required this.lesson, + }); + + final Exam exam; + final BuildContext outsideContext; + final Lesson? lesson; + + static void show({ + required BuildContext context, + required Exam exam, + }) async => + showRoundedModalBottomSheet( + context, + child: ExamPopup( + exam: exam, + outsideContext: context, + lesson: (await ReverseSearch.getLessonByExam(exam, context)), + ), + showHandle: false, + ); + + // IconData _getIcon() => _featureLevels[feature] == PremiumFeatureLevel.cap + // ? FilcIcons.kupak + // : _featureLevels[feature] == PremiumFeatureLevel.ink + // ? FilcIcons.tinta + // : FilcIcons.tinta; + // Color _getColor(BuildContext context) => + // _featureLevels[feature] == PremiumFeatureLevel.gold + // ? const Color(0xFFC89B08) + // : Theme.of(context).brightness == Brightness.light + // ? const Color(0xff691A9B) + // : const Color(0xffA66FC8); + // String? _getAsset() => _featureAssets[feature]; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context).scaffoldBackgroundColor, + borderRadius: const BorderRadius.vertical( + top: Radius.circular(12.0), + ), + ), + child: Stack( + children: [ + Stack( + children: [ + SvgPicture.asset( + "assets/svg/mesh_bg.svg", + // ignore: deprecated_member_use + color: ColorsUtils() + .fade(context, Theme.of(context).colorScheme.secondary, + darkenAmount: 0.1, lightenAmount: 0.1) + .withOpacity(0.33), + width: MediaQuery.of(context).size.width, + ), + Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.vertical( + top: Radius.circular(12.0), + ), + gradient: LinearGradient( + colors: [ + Theme.of(context).scaffoldBackgroundColor, + Theme.of(context) + .scaffoldBackgroundColor + .withOpacity(0.1), + Theme.of(context) + .scaffoldBackgroundColor + .withOpacity(0.1), + Theme.of(context).scaffoldBackgroundColor, + ], + stops: const [0.1, 0.5, 0.7, 1.0], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + width: MediaQuery.of(context).size.width, + height: 175.0, + ), + ], + ), + SizedBox( + width: MediaQuery.of(context).size.width, + child: Padding( + padding: const EdgeInsets.all(18.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: ColorsUtils() + .fade( + context, Theme.of(context).colorScheme.secondary, + darkenAmount: 0.1, lightenAmount: 0.1) + .withOpacity(0.33), + borderRadius: BorderRadius.circular( + 2.0, + ), + ), + ), + const SizedBox( + height: 38.0, + ), + Container( + decoration: BoxDecoration( + color: Theme.of(context).scaffoldBackgroundColor, + borderRadius: BorderRadius.circular(50.0), + ), + child: RoundBorderIcon( + color: ColorsUtils() + .darken( + Theme.of(context).colorScheme.secondary, + amount: 0.1, + ) + .withOpacity(0.9), + width: 1.5, + padding: 10.0, + icon: Icon( + SubjectIcon.resolveVariant( + context: context, subject: exam.subject), + size: 32.0, + color: ColorsUtils() + .darken( + Theme.of(context).colorScheme.secondary, + amount: 0.1, + ) + .withOpacity(0.8), + ), + ), + ), + const SizedBox( + height: 55.0, + ), + Container( + width: double.infinity, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: const BorderRadius.vertical( + top: Radius.circular(12.0), + bottom: Radius.circular(6.0)), + ), + padding: const EdgeInsets.all(14.0), + child: Row( + children: [ + const RoundBorderIcon( + padding: 8.0, + icon: Icon( + Icons.edit_document, + size: 20.0, + ), + ), + const SizedBox( + width: 10.0, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.7, + child: Text( + exam.description.capital(), + style: TextStyle( + color: AppColors.of(context) + .text + .withOpacity(0.9), + fontSize: 16.0, + fontWeight: FontWeight.w600, + ), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ), + Text( + (exam.mode?.description ?? 'Ismeretlen') + .capital(), + style: TextStyle( + color: AppColors.of(context).text, + fontSize: 14.0, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ], + ), + ), + if (lesson != null) + const SizedBox( + height: 6.0, + ), + if (lesson != null) + Container( + width: double.infinity, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: const BorderRadius.vertical( + top: Radius.circular(6.0), + bottom: Radius.circular(12.0)), + ), + padding: const EdgeInsets.all(14.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Icon( + SubjectIcon.resolveVariant( + context: context, subject: exam.subject), + size: 20.0, + ), + const SizedBox( + width: 10.0, + ), + Text( + (((lesson?.subject.isRenamed ?? false) && + Provider.of( + context, + listen: false) + .renamedSubjectsEnabled) + ? lesson?.subject.renamedTo + : (lesson?.subject.name ?? '') + .capital()) ?? + 'Ismeretlen', + style: TextStyle( + color: AppColors.of(context) + .text + .withOpacity(0.9), + fontSize: 16.0, + fontWeight: FontWeight.w600, + fontStyle: ((lesson?.subject.isRenamed ?? + false) && + Provider.of(context, + listen: false) + .renamedSubjectsItalics) + ? FontStyle.italic + : null, + ), + ), + ], + ), + Text( + '${DateFormat('H:mm').format(lesson!.start)} - ${DateFormat('H:mm').format(lesson!.end)}', + style: TextStyle( + color: + AppColors.of(context).text.withOpacity(0.85), + fontSize: 14.0, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + + // const SizedBox( + // height: 24.0, + // ), + // GestureDetector( + // onTap: () async { + // ReverseSearch.getSubjectByLesson(lesson, context) + // .then((subject) { + // if (subject != null) { + // GradesPage.jump(outsideContext, subject: subject); + // } else { + // ScaffoldMessenger.of(context) + // .showSnackBar(CustomSnackBar( + // content: Text("Cannot find subject".i18n, + // style: const TextStyle(color: Colors.white)), + // backgroundColor: AppColors.of(context).red, + // context: context, + // )); + // } + // }); + // }, + // child: Container( + // width: double.infinity, + // decoration: BoxDecoration( + // color: Theme.of(context).colorScheme.background, + // borderRadius: BorderRadius.circular(12.0), + // ), + // padding: const EdgeInsets.all(16.0), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // Text( + // 'view_subject'.i18n, + // style: TextStyle( + // color: + // AppColors.of(context).text.withOpacity(0.9), + // fontSize: 18.0, + // fontWeight: FontWeight.w500, + // ), + // ), + // ], + // ), + // ), + // ), + ], + ), + ), + ), + ], + ), ); } } diff --git a/refilc_mobile_ui/lib/common/widgets/grade/grade_subject_tile.dart b/refilc_mobile_ui/lib/common/widgets/grade/grade_subject_tile.dart index 17e624c..894ac41 100644 --- a/refilc_mobile_ui/lib/common/widgets/grade/grade_subject_tile.dart +++ b/refilc_mobile_ui/lib/common/widgets/grade/grade_subject_tile.dart @@ -9,12 +9,14 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class GradeSubjectTile extends StatelessWidget { - const GradeSubjectTile(this.subject, - {super.key, - this.average = 0.0, - this.groupAverage = 0.0, - this.onTap, - this.averageBefore = 0.0}); + const GradeSubjectTile( + this.subject, { + super.key, + this.average = 0.0, + this.groupAverage = 0.0, + this.onTap, + this.averageBefore = 0.0, + }); final GradeSubject subject; final void Function()? onTap; diff --git a/refilc_mobile_ui/lib/common/widgets/lesson/lesson_viewable.dart b/refilc_mobile_ui/lib/common/widgets/lesson/lesson_viewable.dart index 1cca668..507c65e 100644 --- a/refilc_mobile_ui/lib/common/widgets/lesson/lesson_viewable.dart +++ b/refilc_mobile_ui/lib/common/widgets/lesson/lesson_viewable.dart @@ -1,32 +1,32 @@ -import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:intl/intl.dart'; import 'package:provider/provider.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/theme/colors/utils.dart'; +import 'package:refilc/utils/format.dart'; import 'package:refilc_kreta_api/models/lesson.dart'; import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart'; -import 'package:refilc_mobile_ui/common/panel/panel_button.dart'; import 'package:refilc_mobile_ui/common/round_border_icon.dart'; -import 'package:refilc_mobile_ui/common/viewable.dart'; -import 'package:refilc_mobile_ui/common/widgets/card_handle.dart'; import 'package:refilc/ui/widgets/lesson/lesson_tile.dart'; -import 'package:refilc_mobile_ui/common/widgets/lesson/lesson_view.dart'; import 'package:flutter/material.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 'lesson_view.i18n.dart'; class LessonViewable extends StatefulWidget { - const LessonViewable(this.lesson, - {super.key, this.swapDesc = false, required this.customDesc}); + const LessonViewable( + this.lesson, { + super.key, + this.swapDesc = false, + required this.customDesc, + this.showSubTiles = true, + }); final Lesson lesson; final bool swapDesc; final String customDesc; + final bool showSubTiles; @override State createState() => LessonViewableState(); @@ -51,14 +51,28 @@ class LessonViewableState extends State { Lesson lsn = widget.lesson; lsn.description = widget.customDesc; - final tile = LessonTile(lsn, swapDesc: widget.swapDesc); + final tile = LessonTile( + lsn, + swapDesc: widget.swapDesc, + showSubTiles: widget.showSubTiles, + ); if (lsn.subject.id == '' || tile.lesson.isEmpty) return tile; - return LessonTile( - lsn, - swapDesc: widget.swapDesc, - onTap: () => TimetableLessonPopup.show(context: context, lesson: lsn), + return GestureDetector( + onTap: () => TimetableLessonPopup.show( + context: context, + lesson: lsn, + ), + child: LessonTile( + lsn, + swapDesc: widget.swapDesc, + showSubTiles: widget.showSubTiles, + // onTap: () => TimetableLessonPopup.show( + // context: context, + // lesson: lsn, + // ), + ), ); // return Viewable( @@ -232,9 +246,14 @@ class LessonViewableState extends State { } class TimetableLessonPopup extends StatelessWidget { - const TimetableLessonPopup({super.key, required this.lesson}); + const TimetableLessonPopup({ + super.key, + required this.lesson, + required this.outsideContext, + }); final Lesson lesson; + final BuildContext outsideContext; static void show({ required BuildContext context, @@ -244,6 +263,7 @@ class TimetableLessonPopup extends StatelessWidget { context, child: TimetableLessonPopup( lesson: lesson, + outsideContext: context, ), showHandle: false, ); @@ -272,13 +292,42 @@ class TimetableLessonPopup extends StatelessWidget { ), child: Stack( children: [ - SvgPicture.asset( - "assets/svg/mesh_bg.svg", - // ignore: deprecated_member_use - color: ColorsUtils().fade( - context, Theme.of(context).scaffoldBackgroundColor, - darkenAmount: 0.1, lightenAmount: 0.1), - width: MediaQuery.of(context).size.width, + Stack( + children: [ + SvgPicture.asset( + "assets/svg/mesh_bg.svg", + // ignore: deprecated_member_use + color: ColorsUtils() + .fade(context, Theme.of(context).colorScheme.secondary, + darkenAmount: 0.1, lightenAmount: 0.1) + .withOpacity(0.33), + width: MediaQuery.of(context).size.width, + ), + Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.vertical( + top: Radius.circular(12.0), + ), + gradient: LinearGradient( + colors: [ + Theme.of(context).scaffoldBackgroundColor, + Theme.of(context) + .scaffoldBackgroundColor + .withOpacity(0.1), + Theme.of(context) + .scaffoldBackgroundColor + .withOpacity(0.1), + Theme.of(context).scaffoldBackgroundColor, + ], + stops: const [0.1, 0.5, 0.7, 1.0], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + width: MediaQuery.of(context).size.width, + height: 175.0, + ), + ], ), SizedBox( width: MediaQuery.of(context).size.width, @@ -291,9 +340,11 @@ class TimetableLessonPopup extends StatelessWidget { width: 40, height: 4, decoration: BoxDecoration( - color: ColorsUtils().fade( - context, Theme.of(context).scaffoldBackgroundColor, - darkenAmount: 0.2, lightenAmount: 0.2), + color: ColorsUtils() + .fade( + context, Theme.of(context).colorScheme.secondary, + darkenAmount: 0.1, lightenAmount: 0.1) + .withOpacity(0.33), borderRadius: BorderRadius.circular( 2.0, ), @@ -302,10 +353,31 @@ class TimetableLessonPopup extends StatelessWidget { const SizedBox( height: 38.0, ), - RoundBorderIcon( - icon: Icon( - SubjectIcon.resolveVariant( - context: context, subject: lesson.subject), + Container( + decoration: BoxDecoration( + color: Theme.of(context).scaffoldBackgroundColor, + borderRadius: BorderRadius.circular(50.0), + ), + child: RoundBorderIcon( + color: ColorsUtils() + .darken( + Theme.of(context).colorScheme.secondary, + amount: 0.1, + ) + .withOpacity(0.9), + width: 1.5, + padding: 10.0, + icon: Icon( + SubjectIcon.resolveVariant( + context: context, subject: lesson.subject), + size: 32.0, + color: ColorsUtils() + .darken( + Theme.of(context).colorScheme.secondary, + amount: 0.1, + ) + .withOpacity(0.8), + ), ), ), const SizedBox( @@ -315,39 +387,92 @@ class TimetableLessonPopup extends StatelessWidget { width: double.infinity, decoration: BoxDecoration( color: Theme.of(context).colorScheme.background, - borderRadius: const BorderRadius.vertical( - top: Radius.circular(12.0), - bottom: Radius.circular(6.0), + borderRadius: BorderRadius.vertical( + top: const Radius.circular(12.0), + bottom: (lesson.description.replaceAll(' ', '') != '') + ? const Radius.circular(6.0) + : const Radius.circular(12.0), ), ), padding: const EdgeInsets.all(14.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - '6:09 - 4:20', - style: TextStyle( - color: AppColors.of(context).text.withOpacity(0.85), - fontSize: 14.0, - fontWeight: FontWeight.w500, - ), + Row( + children: [ + Text( + '${DateFormat('H:mm').format(lesson.start)} - ${DateFormat('H:mm').format(lesson.end)}', + style: TextStyle( + color: AppColors.of(context) + .text + .withOpacity(0.85), + fontSize: 14.0, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox( + width: 8.0, + ), + Container( + width: lesson.room.length > 20 ? 111 : null, + padding: const EdgeInsets.symmetric( + horizontal: 5.5, vertical: 3.0), + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .tertiary + .withOpacity(.15), + borderRadius: BorderRadius.circular(10.0), + ), + child: Text( + lesson.room, + overflow: TextOverflow.ellipsis, + style: TextStyle( + height: 1.1, + fontSize: 12.0, + fontWeight: FontWeight.w600, + color: Theme.of(context) + .colorScheme + .secondary + .withOpacity(.9), + ), + ), + ), + ], ), const SizedBox( height: 12.0, ), Text( - lesson.name, + lesson.subject.isRenamed && + Provider.of(context, + listen: false) + .renamedSubjectsEnabled + ? lesson.subject.renamedTo! + : lesson.subject.name.capital(), style: TextStyle( color: AppColors.of(context).text, fontSize: 20.0, fontWeight: FontWeight.w700, + fontStyle: Provider.of(context, + listen: false) + .renamedSubjectsItalics && + lesson.subject.isRenamed + ? FontStyle.italic + : null, ), ), const SizedBox( height: 8.0, ), Text( - lesson.teacher.name, + (lesson.teacher.isRenamed && + Provider.of(context, + listen: false) + .renamedTeachersEnabled) + ? (lesson.teacher.renamedTo ?? + lesson.teacher.name) + : lesson.teacher.name, style: TextStyle( color: AppColors.of(context).text.withOpacity(0.9), fontSize: 14.0, @@ -357,64 +482,79 @@ class TimetableLessonPopup extends StatelessWidget { ], ), ), - const SizedBox( - height: 6.0, - ), - Container( - width: double.infinity, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: const BorderRadius.vertical( - top: Radius.circular(6.0), - bottom: Radius.circular(12.0), - ), + if (lesson.description.replaceAll(' ', '') != '') + const SizedBox( + height: 6.0, ), - padding: const EdgeInsets.all(14.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - lesson.description, - style: TextStyle( - color: AppColors.of(context).text.withOpacity(0.9), - fontSize: 14.0, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - const SizedBox( - height: 24.0, - ), - GestureDetector( - onTap: () { - Navigator.of(context, rootNavigator: true) - .pushReplacementNamed('/'); - }, - child: Container( + if (lesson.description.replaceAll(' ', '') != '') + Container( width: double.infinity, decoration: BoxDecoration( color: Theme.of(context).colorScheme.background, - borderRadius: BorderRadius.circular(12.0), + borderRadius: const BorderRadius.vertical( + top: Radius.circular(6.0), + bottom: Radius.circular(12.0), + ), ), - padding: const EdgeInsets.all(16.0), + padding: const EdgeInsets.all(14.0), child: Column( - crossAxisAlignment: CrossAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'view_subject'.i18n, + lesson.description, style: TextStyle( color: AppColors.of(context).text.withOpacity(0.9), - fontSize: 18.0, - fontWeight: FontWeight.w500, + fontSize: 14.0, + fontWeight: FontWeight.w600, ), ), ], ), ), - ), + // const SizedBox( + // height: 24.0, + // ), + // GestureDetector( + // onTap: () async { + // ReverseSearch.getSubjectByLesson(lesson, context) + // .then((subject) { + // if (subject != null) { + // GradesPage.jump(outsideContext, subject: subject); + // } else { + // ScaffoldMessenger.of(context) + // .showSnackBar(CustomSnackBar( + // content: Text("Cannot find subject".i18n, + // style: const TextStyle(color: Colors.white)), + // backgroundColor: AppColors.of(context).red, + // context: context, + // )); + // } + // }); + // }, + // child: Container( + // width: double.infinity, + // decoration: BoxDecoration( + // color: Theme.of(context).colorScheme.background, + // borderRadius: BorderRadius.circular(12.0), + // ), + // padding: const EdgeInsets.all(16.0), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // Text( + // 'view_subject'.i18n, + // style: TextStyle( + // color: + // AppColors.of(context).text.withOpacity(0.9), + // fontSize: 18.0, + // fontWeight: FontWeight.w500, + // ), + // ), + // ], + // ), + // ), + // ), ], ), ), diff --git a/refilc_mobile_ui/lib/pages/grades/grades_page.dart b/refilc_mobile_ui/lib/pages/grades/grades_page.dart index 88f1030..0a50cde 100644 --- a/refilc_mobile_ui/lib/pages/grades/grades_page.dart +++ b/refilc_mobile_ui/lib/pages/grades/grades_page.dart @@ -19,6 +19,7 @@ import 'package:refilc_kreta_api/models/subject.dart'; import 'package:refilc_kreta_api/models/group_average.dart'; import 'package:refilc_kreta_api/providers/homework_provider.dart'; import 'package:refilc_mobile_ui/common/average_display.dart'; +import 'package:refilc_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu.dart'; import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart'; import 'package:refilc_mobile_ui/common/empty.dart'; import 'package:refilc_mobile_ui/common/panel/panel.dart'; @@ -32,6 +33,8 @@ import 'package:refilc_mobile_ui/pages/grades/fail_warning.dart'; import 'package:refilc_mobile_ui/pages/grades/grades_count.dart'; import 'package:refilc_mobile_ui/pages/grades/graph.dart'; import 'package:refilc_mobile_ui/pages/grades/grade_subject_view.dart'; +import 'package:refilc_mobile_ui/screens/navigation/navigation_route_handler.dart'; +import 'package:refilc_mobile_ui/screens/navigation/navigation_screen.dart'; import 'package:refilc_plus/models/premium_scopes.dart'; import 'package:refilc_plus/providers/plus_provider.dart'; import 'package:flutter/material.dart'; @@ -48,6 +51,19 @@ import 'grades_page.i18n.dart'; class GradesPage extends StatefulWidget { const GradesPage({super.key}); + static void jump(BuildContext context, {GradeSubject? subject}) { + // Go to timetable page with arguments + NavigationScreen.of(context) + ?.customRoute(navigationPageRoute((context) => const GradesPage())); + + NavigationScreen.of(context)?.setPage("grades"); + + // Show initial Lesson + if (subject != null) { + GradeSubjectView(subject, groupAverage: 0.0).push(context, root: true); + } + } + @override GradesPageState createState() => GradesPageState(); } @@ -147,7 +163,8 @@ class GradesPageState extends State { Exam? nearestExam = allExams.firstWhereOrNull((e) => e.subject.id == subject.id && e.writeDate.isAfter(DateTime.now())); - bool hasUnder = hasHomework || nearestExam != null; + bool hasUnder = (hasHomework || nearestExam != null) && + Provider.of(context).qSubjectsSubTiles; return Padding( padding: i > 1 ? const EdgeInsets.only(top: 9.0) : EdgeInsets.zero, @@ -201,7 +218,8 @@ class GradesPageState extends State { const SizedBox( height: 6.0, ), - if (hasHomework) + if (hasHomework && + Provider.of(context).qSubjectsSubTiles) Container( decoration: BoxDecoration( boxShadow: [ @@ -249,7 +267,14 @@ class GradesPageState extends State { ), ), ), - if (nearestExam != null) + if (hasHomework && + nearestExam != null && + Provider.of(context).qSubjectsSubTiles) + const SizedBox( + height: 6.0, + ), + if (nearestExam != null && + Provider.of(context).qSubjectsSubTiles) Container( decoration: BoxDecoration( boxShadow: [ @@ -519,19 +544,10 @@ class GradesPageState extends State { child: IconButton( splashRadius: 24.0, onPressed: () { - if (!Provider.of(context, listen: false) - .hasScope(PremiumScopes.totalGradeCalculator)) { - PlusLockedFeaturePopup.show( - context: context, - feature: PremiumFeature.gradeCalculation); - return; - } - - // SoonAlert.show(context: context); - gradeCalcTotal(context); + showQuickSettings(context); }, icon: Icon( - FeatherIcons.plus, + FeatherIcons.moreHorizontal, color: AppColors.of(context).text, ), ), @@ -627,4 +643,69 @@ class GradesPageState extends State { } }); } + + void showQuickSettings(BuildContext context) { + showRoundedModalBottomSheet( + context, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + child: BottomSheetMenu(items: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: Theme.of(context).colorScheme.background), + child: ListTile( + title: Row( + children: [ + const Icon(FeatherIcons.plusCircle), + const SizedBox( + width: 10.0, + ), + Text('grade_calc'.i18n), + ], + ), + onTap: () { + if (!Provider.of(context, listen: false) + .hasScope(PremiumScopes.totalGradeCalculator)) { + PlusLockedFeaturePopup.show( + context: context, feature: PremiumFeature.gradeCalculation); + return; + } + + // SoonAlert.show(context: context); + gradeCalcTotal(context); + + Navigator.of(context, rootNavigator: true).pop(); + }, + ), + ), + const SizedBox( + height: 10.0, + ), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: Theme.of(context).colorScheme.background), + child: SwitchListTile( + title: Row( + children: [ + const Icon(Icons.edit_document), + const SizedBox( + width: 10.0, + ), + Text('show_exams_homework'.i18n), + ], + ), + value: Provider.of(context, listen: false) + .qSubjectsSubTiles, + onChanged: (v) { + Provider.of(context, listen: false) + .update(qSubjectsSubTiles: v); + + Navigator.of(context, rootNavigator: true).pop(); + }, + ), + ), + ]), + ); + } } diff --git a/refilc_mobile_ui/lib/pages/grades/grades_page.i18n.dart b/refilc_mobile_ui/lib/pages/grades/grades_page.i18n.dart index 1985e34..59eee56 100644 --- a/refilc_mobile_ui/lib/pages/grades/grades_page.i18n.dart +++ b/refilc_mobile_ui/lib/pages/grades/grades_page.i18n.dart @@ -27,6 +27,8 @@ extension Localization on String { "exams": "Exams", "timetable": "Timetable", "grades": "Grades", + "show_exams_homework": "Exams and Homework", + "grade_calc": "Grade Calculator", }, "hu_hu": { "Grades": "Tantárgyak", @@ -51,6 +53,8 @@ extension Localization on String { "exams": "Számonkérések", "timetable": "Órarend", "grades": "Jegyek", + "show_exams_homework": "Dolgozatok és házik", + "grade_calc": "Jegy kalkulátor", }, "de_de": { "Grades": "Fächer", @@ -75,6 +79,8 @@ extension Localization on String { "exams": "Prüfungen", "timetable": "Stundenplan", "grades": "Noten", + "show_exams_homework": "Referate und Hausaufgaben", + "grade_calc": "Noten-Rechner", }, }; diff --git a/refilc_mobile_ui/lib/pages/grades/graph.dart b/refilc_mobile_ui/lib/pages/grades/graph.dart index 8f22f65..ed8e2e8 100644 --- a/refilc_mobile_ui/lib/pages/grades/graph.dart +++ b/refilc_mobile_ui/lib/pages/grades/graph.dart @@ -161,6 +161,11 @@ class GradeGraphState extends State { final x = halfYearGrade.writeDate.month + (halfYearGrade.writeDate.day / 31) + ((halfYearGrade.writeDate.year - data.last.writeDate.year) * 12); + + List dataBeforeMidYr = data + .where((e) => e.writeDate.isBefore(halfYearGrade.writeDate)) + .toList(); + if (x <= maxX) { extraLinesV.add( VerticalLine( @@ -170,7 +175,9 @@ class GradeGraphState extends State { label: VerticalLineLabel( labelResolver: (_) => " ${"mid".i18n} ​", // <- zwsp for padding show: true, - alignment: Alignment.topLeft, + alignment: dataBeforeMidYr.length < 2 + ? Alignment.topRight + : Alignment.topLeft, style: TextStyle( backgroundColor: Theme.of(context).colorScheme.background, color: AppColors.of(context).text, @@ -218,11 +225,12 @@ class GradeGraphState extends State { horizontalLines: extraLinesH), lineBarsData: [ LineChartBarData( - preventCurveOverShooting: true, + preventCurveOverShooting: false, spots: subjectSpots, isCurved: true, colors: averageColors.reversed.toList(), - barWidth: 8, + barWidth: 6, + curveSmoothness: 0.2, isStrokeCapRound: true, dotData: FlDotData(show: false), belowBarData: BarAreaData( @@ -339,11 +347,13 @@ class GradeGraphState extends State { ghostData.isNotEmpty ? ghostData : data; tData.sort((a, b) => a.writeDate.compareTo(b.writeDate)); - return tData.first.writeDate - .add(const Duration(days: 120)) - .isBefore(tData.last.writeDate) - ? 2.0 - : 1.0; + return ghostData.isNotEmpty + ? 3.0 + : tData.first.writeDate + .add(const Duration(days: 120)) + .isBefore(tData.last.writeDate) + ? 2.0 + : 2.5; }(), checkToShowTitle: (double minValue, double maxValue, diff --git a/refilc_mobile_ui/lib/pages/home/home_page.dart b/refilc_mobile_ui/lib/pages/home/home_page.dart index 8939275..8a7218e 100644 --- a/refilc_mobile_ui/lib/pages/home/home_page.dart +++ b/refilc_mobile_ui/lib/pages/home/home_page.dart @@ -214,7 +214,7 @@ class HomePageState extends State with TickerProviderStateMixin { // TODO: REMOVE IN PRODUCTION BUILD!!! // print(_liveCard.currentState); - _liveCard.currentState = LiveCardState.duringLesson; + // _liveCard.currentState = LiveCardState.duringLesson; return Scaffold( body: Stack( diff --git a/refilc_mobile_ui/lib/pages/home/live_card/live_card.dart b/refilc_mobile_ui/lib/pages/home/live_card/live_card.dart index 3cf6f4b..922070a 100644 --- a/refilc_mobile_ui/lib/pages/home/live_card/live_card.dart +++ b/refilc_mobile_ui/lib/pages/home/live_card/live_card.dart @@ -6,17 +6,9 @@ import 'package:refilc/helpers/subject.dart'; import 'package:refilc/models/settings.dart'; import 'package:refilc/theme/colors/colors.dart'; import 'package:refilc/ui/widgets/lesson/lesson_tile.dart'; -import 'package:refilc_kreta_api/models/category.dart'; -import 'package:refilc_kreta_api/models/lesson.dart'; -import 'package:refilc_kreta_api/models/subject.dart'; -import 'package:refilc_kreta_api/models/teacher.dart'; import 'package:refilc_mobile_ui/common/panel/panel.dart'; import 'package:refilc_mobile_ui/common/progress_bar.dart'; import 'package:refilc_mobile_ui/common/round_border_icon.dart'; -// import 'package:refilc_kreta_api/models/category.dart'; -// import 'package:refilc_kreta_api/models/lesson.dart'; -// import 'package:refilc_kreta_api/models/subject.dart'; -// import 'package:refilc_kreta_api/models/teacher.dart'; import 'package:refilc_mobile_ui/common/splitted_panel/splitted_panel.dart'; import 'package:refilc_mobile_ui/pages/home/live_card/heads_up_countdown.dart'; import 'package:refilc_mobile_ui/pages/home/live_card/segmented_countdown.dart'; @@ -71,19 +63,19 @@ class LiveCardStateA extends State { // test // TODO: REMOVE IN PRODUCTION BUILD!!! - liveCard.currentState = LiveCardState.duringLesson; + /*liveCard.currentState = LiveCardState.duringLesson; liveCard.currentLesson = Lesson( - date: DateTime.now().add(Duration( + date: DateTime.now().add(const Duration( minutes: 30, )), subject: GradeSubject( category: Category(id: 'asd'), id: 'asd', name: 'Matematika'), lessonIndex: '1', teacher: Teacher(id: 'id', name: 'name'), - start: DateTime.now().subtract(Duration( + start: DateTime.now().subtract(const Duration( minutes: 30, )), - end: DateTime.now().add(Duration( + end: DateTime.now().add(const Duration( minutes: 15, )), homeworkId: 'homeworkId', @@ -92,9 +84,9 @@ class LiveCardStateA extends State { room: 'ABC69', groupName: 'groupName', name: 'name', - ); + );*/ - liveCard.nextLesson = liveCard.currentLesson; + // liveCard.nextLesson = liveCard.currentLesson; // final dt = DateTime(2024, 3, 22, 17, 12, 1, 1, 1); diff --git a/refilc_mobile_ui/lib/pages/notes/notes_page.dart b/refilc_mobile_ui/lib/pages/notes/notes_page.dart index 61ff85c..f777747 100644 --- a/refilc_mobile_ui/lib/pages/notes/notes_page.dart +++ b/refilc_mobile_ui/lib/pages/notes/notes_page.dart @@ -1,5 +1,6 @@ // ignore_for_file: no_leading_underscores_for_local_identifiers, use_build_context_synchronously +import 'dart:convert'; import 'dart:math'; import 'package:flutter/cupertino.dart'; @@ -7,6 +8,8 @@ import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:refilc/api/providers/database_provider.dart'; import 'package:refilc/api/providers/self_note_provider.dart'; import 'package:refilc/api/providers/update_provider.dart'; +import 'package:refilc/models/self_note.dart'; +import 'package:refilc/models/settings.dart'; import 'package:refilc/utils/format.dart'; import 'package:refilc_kreta_api/models/absence.dart'; import 'package:refilc_kreta_api/models/homework.dart'; @@ -14,6 +17,8 @@ import 'package:refilc_kreta_api/models/subject.dart'; import 'package:refilc/api/providers/user_provider.dart'; import 'package:refilc/theme/colors/colors.dart'; import 'package:refilc_kreta_api/providers/homework_provider.dart'; +import 'package:refilc_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu.dart'; +import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart'; import 'package:refilc_mobile_ui/common/empty.dart'; import 'package:refilc_mobile_ui/common/panel/panel.dart'; import 'package:refilc_mobile_ui/common/profile_image/profile_button.dart'; @@ -23,12 +28,14 @@ import 'package:refilc_mobile_ui/common/widgets/tick_tile.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:refilc_mobile_ui/pages/notes/submenu/add_note_screen.dart'; +import 'package:refilc_mobile_ui/pages/notes/submenu/create_image_note.dart'; import 'package:refilc_mobile_ui/pages/notes/submenu/note_view_screen.dart'; import 'package:refilc_mobile_ui/pages/notes/submenu/self_note_tile.dart'; import 'package:refilc_plus/models/premium_scopes.dart'; import 'package:refilc_plus/providers/plus_provider.dart'; import 'package:refilc_plus/ui/mobile/plus/premium_inline.dart'; import 'package:refilc_plus/ui/mobile/plus/upsell.dart'; +import 'package:uuid/uuid.dart'; import 'notes_page.i18n.dart'; enum AbsenceFilter { absences, delays, misses } @@ -59,9 +66,14 @@ class NotesPageState extends State with TickerProviderStateMixin { Map doneItems = {}; List noteTiles = []; + List todoItems = []; + + final TextEditingController _taskName = TextEditingController(); + final TextEditingController _taskContent = TextEditingController(); void generateTiles() async { doneItems = await databaseProvider.userQuery.toDoItems(userId: user.id!); + todoItems = await databaseProvider.userQuery.getTodoItems(userId: user.id!); List tiles = []; @@ -76,7 +88,7 @@ class NotesPageState extends State with TickerProviderStateMixin { List toDoTiles = []; if (hw.isNotEmpty && - !Provider.of(context, listen: false) + Provider.of(context, listen: false) .hasScope(PremiumScopes.unlimitedSelfNotes)) { toDoTiles.addAll(hw.map((e) => TickTile( padding: EdgeInsets.zero, @@ -96,6 +108,21 @@ class NotesPageState extends State with TickerProviderStateMixin { ))); } + if (selfNoteProvider.todos.isNotEmpty) { + toDoTiles.addAll(selfNoteProvider.todos.map((e) => TickTile( + padding: EdgeInsets.zero, + title: e.title, + description: e.content, + isTicked: e.done, + onTap: (p0) async { + todoItems.firstWhere((element) => element.id == e.id).done = p0; + + await databaseProvider.userStore + .storeSelfTodoItems(todoItems, userId: user.id!); + }, + ))); + } + if (toDoTiles.isNotEmpty) { tiles.add(const SizedBox( height: 10.0, @@ -114,13 +141,42 @@ class NotesPageState extends State with TickerProviderStateMixin { if (selfNoteProvider.notes.isNotEmpty) { selfNoteTiles.addAll(selfNoteProvider.notes.reversed.map( - (e) => SelfNoteTile( - title: e.title ?? e.content.split(' ')[0], - content: e.content, - onTap: () => Navigator.of(context, rootNavigator: true).push( - CupertinoPageRoute( - builder: (context) => NoteViewScreen(note: e))), - ), + (e) => e.noteType == NoteType.text + ? SelfNoteTile( + title: e.title ?? e.content.split(' ')[0], + content: e.content, + onTap: () => Navigator.of(context, rootNavigator: true).push( + CupertinoPageRoute( + builder: (context) => NoteViewScreen(note: e))), + ) + : GestureDetector( + onTap: () => Navigator.of(context, rootNavigator: true).push( + CupertinoPageRoute( + builder: (context) => NoteViewScreen(note: e))), + child: Container( + height: MediaQuery.of(context).size.width / 2.42, + width: MediaQuery.of(context).size.width / 2.42, + decoration: BoxDecoration( + boxShadow: [ + if (Provider.of(context, listen: false) + .shadowEffect) + BoxShadow( + offset: const Offset(0, 21), + blurRadius: 23.0, + color: Theme.of(context).shadowColor, + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(16.0), + child: Image.memory( + const Base64Decoder().convert(e.content), + fit: BoxFit.cover, + gaplessPlayback: true, + ), + ), + ), + ), )); } @@ -235,9 +291,7 @@ class NotesPageState extends State with TickerProviderStateMixin { feature: PremiumFeature.selfNotes); } - Navigator.of(context, rootNavigator: true).push( - CupertinoPageRoute( - builder: (context) => const AddNoteScreen())); + showCreationModal(context); }, child: Icon( FeatherIcons.plus, @@ -285,6 +339,8 @@ class NotesPageState extends State with TickerProviderStateMixin { .fetch( from: DateTime.now().subtract(const Duration(days: 30))); Provider.of(context, listen: false).restore(); + Provider.of(context, listen: false) + .restoreTodo(); generateTiles(); @@ -312,4 +368,229 @@ class NotesPageState extends State with TickerProviderStateMixin { ), ); } + + void showCreationModal(BuildContext context) { + // _sheetController = _scaffoldKey.currentState?.showBottomSheet( + // (context) => RoundedBottomSheet( + // borderRadius: 14.0, + // child: BottomSheetMenu(items: [ + // SwitchListTile( + // title: Text('show_lesson_num'.i18n), + // value: + // Provider.of(context).qTimetableLessonNum, + // onChanged: (v) { + // Provider.of(context, listen: false) + // .update(qTimetableLessonNum: v); + // }) + // ])), + // backgroundColor: const Color(0x00000000), + // elevation: 12.0, + // ); + + // _sheetController!.closed.then((value) { + // // Show fab and grades + // if (mounted) {} + // }); + showRoundedModalBottomSheet( + context, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + child: BottomSheetMenu(items: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: Theme.of(context).colorScheme.background), + child: ListTile( + title: Row( + children: [ + const Icon(Icons.sticky_note_2_outlined), + const SizedBox( + width: 10.0, + ), + Text('new_note'.i18n), + ], + ), + onTap: () => Navigator.of(context, rootNavigator: true).push( + CupertinoPageRoute( + builder: (context) => const AddNoteScreen())), + ), + ), + const SizedBox( + height: 10.0, + ), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: Theme.of(context).colorScheme.background), + child: ListTile( + title: Row( + children: [ + const Icon(Icons.photo_library_outlined), + const SizedBox( + width: 10.0, + ), + Text('new_image'.i18n), + ], + ), + onTap: () { + showDialog( + context: context, + builder: (context) => ImageNoteEditor(user.user!)); + }, + ), + ), + const SizedBox( + height: 10.0, + ), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: Theme.of(context).colorScheme.background), + child: ListTile( + title: Row( + children: [ + const Icon(Icons.task_outlined), + const SizedBox( + width: 10.0, + ), + Text('new_task'.i18n), + ], + ), + onTap: () { + if (!Provider.of(context, listen: false) + .hasScope(PremiumScopes.unlimitedSelfNotes)) { + PlusLockedFeaturePopup.show( + context: context, feature: PremiumFeature.selfNotes); + + return; + } + + showTaskCreation(context); + }, + ), + ), + ]), + ); + } + + void showTaskCreation(context) { + showDialog( + context: context, + builder: (context) => AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(14.0))), + contentPadding: const EdgeInsets.only(top: 10.0), + title: Text("new_task".i18n), + content: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 10.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: _taskName, + 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: "task_name".i18n, + suffixIcon: IconButton( + icon: const Icon( + FeatherIcons.x, + color: Colors.grey, + ), + onPressed: () { + setState(() { + _taskName.text = ""; + }); + }, + ), + ), + ), + const SizedBox( + height: 10.0, + ), + TextField( + controller: _taskContent, + 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: "task_content".i18n, + suffixIcon: IconButton( + icon: const Icon( + FeatherIcons.x, + color: Colors.grey, + ), + onPressed: () { + setState(() { + _taskContent.text = ""; + }); + }, + ), + ), + ), + ], + ), + ), + actions: [ + TextButton( + child: Text( + "cancel".i18n, + style: const TextStyle(fontWeight: FontWeight.w500), + ), + onPressed: () { + Navigator.of(context).maybePop(); + }, + ), + TextButton( + child: Text( + "next".i18n, + style: const TextStyle(fontWeight: FontWeight.w500), + ), + onPressed: () async { + todoItems.add(TodoItem.fromJson({ + 'id': const Uuid().v4(), + 'title': _taskName.text.replaceAll(' ', '') != "" + ? _taskName.text + : 'no_title'.i18n, + 'content': _taskContent.text, + 'done': false, + })); + + await databaseProvider.userStore + .storeSelfTodoItems(todoItems, userId: user.id!); + + setState(() { + _taskName.text = ""; + _taskContent.text = ""; + }); + + Provider.of(context, listen: false).restore(); + Provider.of(context, listen: false) + .restoreTodo(); + + generateTiles(); + + Navigator.of(context).pop(true); + }, + ), + ], + ), + ); + } } diff --git a/refilc_mobile_ui/lib/pages/notes/notes_page.i18n.dart b/refilc_mobile_ui/lib/pages/notes/notes_page.i18n.dart index 7924e35..28bc703 100644 --- a/refilc_mobile_ui/lib/pages/notes/notes_page.i18n.dart +++ b/refilc_mobile_ui/lib/pages/notes/notes_page.i18n.dart @@ -13,6 +13,11 @@ extension ScreensLocalization on String { "hint": "Note content...", "hint_t": "Note title...", "your_notes": "Your Notes", + "new_image": "New Image", + "no_title": "No title", + "task_content": "Task content...", + "task_name": "Task title...", + "new_task": "New Task", }, "hu_hu": { "notes": "Füzet", @@ -24,6 +29,11 @@ extension ScreensLocalization on String { "hint": "Jegyzet tartalma...", "hint_t": "Jegyzet címe...", "your_notes": "Jegyzeteid", + "new_image": "Új kép", + "no_title": "Nincs cím", + "task_content": "Feladat tartalma...", + "task_name": "Feladat címe...", + "new_task": "Új feladat", }, "de_de": { "notes": "Broschüre", @@ -35,6 +45,11 @@ extension ScreensLocalization on String { "hint": "Inhalt beachten...", "hint_t": "Titel notieren...", "your_notes": "Deine Noten", + "new_image": "Neues Bild", + "no_title": "Kein Titel", + "task_content": "Aufgabeninhalt...", + "task_name": "Aufgabentitel...", + "new_task": "Neue Aufgabe", }, }; diff --git a/refilc_mobile_ui/lib/pages/notes/submenu/add_note_screen.dart b/refilc_mobile_ui/lib/pages/notes/submenu/add_note_screen.dart index 4cbe5cf..30c9bb3 100644 --- a/refilc_mobile_ui/lib/pages/notes/submenu/add_note_screen.dart +++ b/refilc_mobile_ui/lib/pages/notes/submenu/add_note_screen.dart @@ -153,7 +153,8 @@ class AddNoteScreenState extends State { 'title': _titleController.text.replaceAll(' ', '') == '' ? null : _titleController.text, - 'content': _contentController.text + 'content': _contentController.text, + 'note_type': 'text', })); } else { var i = @@ -165,6 +166,7 @@ class AddNoteScreenState extends State { ? null : _titleController.text, 'content': _contentController.text, + 'note_type': 'text', }); } diff --git a/refilc_mobile_ui/lib/pages/notes/submenu/create_image_note.dart b/refilc_mobile_ui/lib/pages/notes/submenu/create_image_note.dart new file mode 100644 index 0000000..9da532b --- /dev/null +++ b/refilc_mobile_ui/lib/pages/notes/submenu/create_image_note.dart @@ -0,0 +1,215 @@ +// ignore_for_file: use_build_context_synchronously + +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:image_crop/image_crop.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:provider/provider.dart'; +import 'package:refilc/api/providers/database_provider.dart'; +import 'package:refilc/api/providers/self_note_provider.dart'; +import 'package:refilc/models/self_note.dart'; +import 'package:refilc/models/user.dart'; +import 'package:uuid/uuid.dart'; +import 'notes_screen.i18n.dart'; + +// ignore: must_be_immutable +class ImageNoteEditor extends StatefulWidget { + late User u; + + ImageNoteEditor(this.u, {super.key}); + + @override + State createState() => _ImageNoteEditorState(); +} + +class _ImageNoteEditorState extends State { + final cropKey = GlobalKey(); + 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_image".i18n, + style: const TextStyle( + fontSize: 14.0, + fontWeight: FontWeight.w500, + ), + ) + ], + ), + ), + ); + } + + Future _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 imageBytes = await _lastCropped!.readAsBytes(); + String base64Image = base64Encode(imageBytes); + + List selfNotes = + await Provider.of(context, listen: false) + .userQuery + .getSelfNotes(userId: widget.u.id); + + selfNotes.add(SelfNote.fromJson({ + 'id': const Uuid().v4(), + 'content': base64Image, + 'note_type': 'image' + })); + + await Provider.of(context, listen: false) + .userStore + .storeSelfNotes(selfNotes, userId: widget.u.id); + + Provider.of(context, listen: false).restore(); + Provider.of(context, listen: false).restoreTodo(); + + debugPrint('$file'); + } + + @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("new_image".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(context, listen: false) + // .store + // .storeUser(widget.u); + // Provider.of(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( + "next".i18n, + style: const TextStyle(fontWeight: FontWeight.w500), + ), + onPressed: () async { + await _cropImage(); + Navigator.of(context).pop(true); + }, + ), + ], + ); + } +} diff --git a/refilc_mobile_ui/lib/pages/notes/submenu/note_view_screen.dart b/refilc_mobile_ui/lib/pages/notes/submenu/note_view_screen.dart index 5ea5ddd..6c53209 100644 --- a/refilc_mobile_ui/lib/pages/notes/submenu/note_view_screen.dart +++ b/refilc_mobile_ui/lib/pages/notes/submenu/note_view_screen.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:refilc/api/providers/self_note_provider.dart'; import 'package:refilc/models/self_note.dart'; @@ -8,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:provider/provider.dart'; import 'package:markdown/markdown.dart' as md; +import 'notes_screen.i18n.dart'; class NoteViewScreen extends StatefulWidget { const NoteViewScreen({super.key, required this.note}); @@ -30,7 +33,9 @@ class NoteViewScreenState extends State { surfaceTintColor: Theme.of(context).scaffoldBackgroundColor, leading: BackButton(color: AppColors.of(context).text), title: Text( - widget.note.title ?? '${widget.note.content.split(' ')[0]}...', + widget.note.noteType == NoteType.text + ? (widget.note.title ?? '${widget.note.content.split(' ')[0]}...') + : 'image_note'.i18n, style: TextStyle( color: AppColors.of(context).text, fontSize: 26.0, @@ -38,52 +43,55 @@ class NoteViewScreenState extends State { ), ), actions: [ - ClipRRect( - borderRadius: BorderRadius.circular(10.1), - child: GestureDetector( - onTap: () { - // handle tap - Navigator.of(context, rootNavigator: true).push( - CupertinoPageRoute( - builder: (context) => - AddNoteScreen(initialNote: widget.note))); - }, - child: Container( - color: Theme.of(context).colorScheme.secondary.withOpacity(0.2), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Stack( - children: [ - IconTheme( - data: IconThemeData( - color: Theme.of(context).colorScheme.secondary, + if (widget.note.noteType == NoteType.text) + ClipRRect( + borderRadius: BorderRadius.circular(10.1), + child: GestureDetector( + onTap: () { + // handle tap + Navigator.of(context, rootNavigator: true).push( + CupertinoPageRoute( + builder: (context) => + AddNoteScreen(initialNote: widget.note))); + }, + child: Container( + color: + Theme.of(context).colorScheme.secondary.withOpacity(0.2), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Stack( + children: [ + IconTheme( + data: IconThemeData( + color: Theme.of(context).colorScheme.secondary, + ), + child: const Icon( + FeatherIcons.edit, + size: 20.0, + ), ), - child: const Icon( - FeatherIcons.edit, - size: 20.0, + IconTheme( + data: IconThemeData( + color: + Theme.of(context).brightness == Brightness.light + ? Colors.black.withOpacity(.5) + : Colors.white.withOpacity(.3), + ), + child: const Icon( + FeatherIcons.edit, + size: 20.0, + ), ), - ), - IconTheme( - data: IconThemeData( - color: - Theme.of(context).brightness == Brightness.light - ? Colors.black.withOpacity(.5) - : Colors.white.withOpacity(.3), - ), - child: const Icon( - FeatherIcons.edit, - size: 20.0, - ), - ), - ], + ], + ), ), ), ), ), - ), - const SizedBox( - width: 10, - ), + if (widget.note.noteType == NoteType.text) + const SizedBox( + width: 10, + ), ClipRRect( borderRadius: BorderRadius.circular(10.1), child: GestureDetector( @@ -140,21 +148,30 @@ class NoteViewScreenState extends State { child: Column( children: [ Expanded( - child: MarkdownBody( - data: widget.note.content, - extensionSet: md.ExtensionSet( - md.ExtensionSet.gitHubFlavored.blockSyntaxes, - [ - md.EmojiSyntax(), - ...md.ExtensionSet.gitHubFlavored.inlineSyntaxes - ], - ), - styleSheet: MarkdownStyleSheet( - p: const TextStyle( - fontSize: 15.0, - ), - ), - ), + child: widget.note.noteType == NoteType.text + ? MarkdownBody( + data: widget.note.content, + extensionSet: md.ExtensionSet( + md.ExtensionSet.gitHubFlavored.blockSyntaxes, + [ + md.EmojiSyntax(), + ...md.ExtensionSet.gitHubFlavored.inlineSyntaxes + ], + ), + styleSheet: MarkdownStyleSheet( + p: const TextStyle( + fontSize: 15.0, + ), + ), + ) + : ClipRRect( + borderRadius: BorderRadius.circular(10.0), + child: Image.memory( + const Base64Decoder().convert(widget.note.content), + fit: BoxFit.contain, + gaplessPlayback: true, + ), + ), ), // Expanded( // child: Text( diff --git a/refilc_mobile_ui/lib/pages/notes/submenu/notes_screen.dart b/refilc_mobile_ui/lib/pages/notes/submenu/notes_screen.dart index 8f2795a..df9b01b 100644 --- a/refilc_mobile_ui/lib/pages/notes/submenu/notes_screen.dart +++ b/refilc_mobile_ui/lib/pages/notes/submenu/notes_screen.dart @@ -268,6 +268,7 @@ class NotesScreenState extends State { Provider.of(context, listen: false) .fetch(from: DateTime.now().subtract(const Duration(days: 30))); Provider.of(context, listen: false).restore(); + Provider.of(context, listen: false).restoreTodo(); return Future(() => null); }, diff --git a/refilc_mobile_ui/lib/pages/notes/submenu/notes_screen.i18n.dart b/refilc_mobile_ui/lib/pages/notes/submenu/notes_screen.i18n.dart index 02c848b..77d6128 100644 --- a/refilc_mobile_ui/lib/pages/notes/submenu/notes_screen.i18n.dart +++ b/refilc_mobile_ui/lib/pages/notes/submenu/notes_screen.i18n.dart @@ -13,6 +13,12 @@ extension SettingsLocalization on String { "hint": "Note content...", "hint_t": "Note title...", "your_notes": "Your Notes", + "next": "Next", + "cancel": "Cancel", + "click_here": "Click here", + "select_image": "to select an image", + "new_image": "New Image", + "image_note": "Image", }, "hu_hu": { "notes": "Füzet", @@ -24,6 +30,12 @@ extension SettingsLocalization on String { "hint": "Jegyzet tartalma...", "hint_t": "Jegyzet címe...", "your_notes": "Jegyzeteid", + "next": "Tovább", + "cancel": "Mégse", + "click_here": "Kattints ide", + "select_image": "kép kiválasztásához", + "new_image": "Új kép", + "image_note": "Kép", }, "de_de": { "notes": "Broschüre", @@ -35,6 +47,12 @@ extension SettingsLocalization on String { "hint": "Inhalt beachten...", "hint_t": "Titel notieren...", "your_notes": "Deine Noten", + "next": "Weiter", + "cancel": "Abbrechen", + "click_here": "Klicken Sie hier", + "select_image": "um ein Bild auszuwählen", + "new_image": "Neues Bild", + "image_note": "Bild", }, }; diff --git a/refilc_mobile_ui/lib/pages/timetable/timetable_page.dart b/refilc_mobile_ui/lib/pages/timetable/timetable_page.dart index f4bb86b..03b983a 100644 --- a/refilc_mobile_ui/lib/pages/timetable/timetable_page.dart +++ b/refilc_mobile_ui/lib/pages/timetable/timetable_page.dart @@ -12,6 +12,8 @@ import 'package:refilc_kreta_api/providers/timetable_provider.dart'; import 'package:refilc/api/providers/user_provider.dart'; import 'package:refilc/theme/colors/colors.dart'; import 'package:refilc_kreta_api/models/lesson.dart'; +import 'package:refilc_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu.dart'; +import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart'; import 'package:refilc_mobile_ui/common/dot.dart'; import 'package:refilc_mobile_ui/common/empty.dart'; import 'package:refilc_mobile_ui/common/profile_image/profile_button.dart'; @@ -67,6 +69,8 @@ class TimetablePage extends StatefulWidget { class TimetablePageState extends State with TickerProviderStateMixin, WidgetsBindingObserver { + final GlobalKey _scaffoldKey = GlobalKey(); + late UserProvider user; late TimetableProvider timetableProvider; late UpdateProvider updateProvider; @@ -213,6 +217,7 @@ class TimetablePageState extends State firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0]; return Scaffold( + key: _scaffoldKey, body: Padding( padding: const EdgeInsets.only(top: 9.0), child: RefreshIndicator( @@ -233,8 +238,53 @@ class TimetablePageState extends State snap: false, surfaceTintColor: Theme.of(context).scaffoldBackgroundColor, actions: [ + // Padding( + // padding: const EdgeInsets.only(top: 8.0, bottom: 8.0), + // child: IconButton( + // splashRadius: 24.0, + // // tested timetable sync + // // onPressed: () async { + // // ThirdPartyProvider tpp = + // // Provider.of(context, + // // listen: false); + + // // await tpp.pushTimetable(context, _controller); + // // }, + // onPressed: () { + // // If timetable empty, show empty + // if (_tabController.length == 0) { + // ScaffoldMessenger.of(context).showSnackBar(SnackBar( + // content: Text("empty_timetable".i18n), + // duration: const Duration(seconds: 2), + // )); + // return; + // } + + // Navigator.of(context, rootNavigator: true) + // .push(PageRouteBuilder( + // pageBuilder: + // (context, animation, secondaryAnimation) => + // FSTimetable( + // controller: _controller, + // ), + // )) + // .then((_) { + // SystemChrome.setPreferredOrientations( + // [DeviceOrientation.portraitUp]); + // setSystemChrome(context); + // }); + // }, + // icon: Icon(FeatherIcons.trello, + // color: AppColors.of(context).text), + // ), + // ), + Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.only( + right: 5.0, + bottom: 8.0, + top: 8.0, + ), child: IconButton( splashRadius: 24.0, // tested timetable sync @@ -246,30 +296,9 @@ class TimetablePageState extends State // await tpp.pushTimetable(context, _controller); // }, onPressed: () { - // If timetable empty, show empty - if (_tabController.length == 0) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text("empty_timetable".i18n), - duration: const Duration(seconds: 2), - )); - return; - } - - Navigator.of(context, rootNavigator: true) - .push(PageRouteBuilder( - pageBuilder: - (context, animation, secondaryAnimation) => - FSTimetable( - controller: _controller, - ), - )) - .then((_) { - SystemChrome.setPreferredOrientations( - [DeviceOrientation.portraitUp]); - setSystemChrome(context); - }); + showQuickSettings(context); }, - icon: Icon(FeatherIcons.trello, + icon: Icon(FeatherIcons.moreHorizontal, color: AppColors.of(context).text), ), ), @@ -685,6 +714,9 @@ class TimetablePageState extends State customLessonDesc[ lesson.id] ?? lesson.description, + showSubTiles: + settingsProvider + .qTimetableSubTiles, ), ), ), @@ -817,6 +849,152 @@ class TimetablePageState extends State ), ); } + + void showQuickSettings(BuildContext context) { + // _sheetController = _scaffoldKey.currentState?.showBottomSheet( + // (context) => RoundedBottomSheet( + // borderRadius: 14.0, + // child: BottomSheetMenu(items: [ + // SwitchListTile( + // title: Text('show_lesson_num'.i18n), + // value: + // Provider.of(context).qTimetableLessonNum, + // onChanged: (v) { + // Provider.of(context, listen: false) + // .update(qTimetableLessonNum: v); + // }) + // ])), + // backgroundColor: const Color(0x00000000), + // elevation: 12.0, + // ); + + // _sheetController!.closed.then((value) { + // // Show fab and grades + // if (mounted) {} + // }); + showRoundedModalBottomSheet( + context, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + child: BottomSheetMenu(items: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: Theme.of(context).colorScheme.background), + child: ListTile( + contentPadding: const EdgeInsets.only(left: 16.0, right: 10.0), + title: Row( + children: [ + const Icon(FeatherIcons.trello), + const SizedBox( + width: 10.0, + ), + Text('full_screen_timetable'.i18n), + ], + ), + onTap: () { + if (_tabController.length == 0) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text("empty_timetable".i18n), + duration: const Duration(seconds: 2), + )); + return; + } + + Navigator.of(context, rootNavigator: true).pop(); + + Navigator.of(context, rootNavigator: true) + .push(PageRouteBuilder( + pageBuilder: (context, animation, secondaryAnimation) => + FSTimetable( + controller: _controller, + ), + )) + .then((_) { + SystemChrome.setPreferredOrientations( + [DeviceOrientation.portraitUp]); + setSystemChrome(context); + }); + }, + ), + ), + const SizedBox( + height: 10.0, + ), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: Theme.of(context).colorScheme.background), + child: SwitchListTile( + contentPadding: const EdgeInsets.only(left: 16.0, right: 10.0), + title: Row( + children: [ + const Icon(Icons.local_cafe_rounded), + const SizedBox( + width: 10.0, + ), + Text('show_breaks'.i18n), + ], + ), + value: Provider.of(context, listen: false) + .showBreaks, + onChanged: (v) { + Provider.of(context, listen: false) + .update(showBreaks: v); + + Navigator.of(context, rootNavigator: true).pop(); + }, + ), + ), + // SwitchListTile( + // title: Row( + // children: [ + // const Icon(FeatherIcons.clock), + // const SizedBox( + // width: 10.0, + // ), + // Text('show_lesson_num'.i18n), + // ], + // ), + // value: Provider.of(context, listen: false) + // .qTimetableLessonNum, + // onChanged: (v) { + // Provider.of(context, listen: false) + // .update(qTimetableLessonNum: v); + + // Navigator.of(context, rootNavigator: true).pop(); + // }, + // ), + const SizedBox( + height: 10.0, + ), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: Theme.of(context).colorScheme.background), + child: SwitchListTile( + contentPadding: const EdgeInsets.only(left: 16.0, right: 10.0), + title: Row( + children: [ + const Icon(Icons.edit_document), + const SizedBox( + width: 10.0, + ), + Text('show_exams_homework'.i18n), + ], + ), + value: Provider.of(context, listen: false) + .qTimetableSubTiles, + onChanged: (v) { + Provider.of(context, listen: false) + .update(qTimetableSubTiles: v); + + Navigator.of(context, rootNavigator: true).pop(); + }, + ), + ), + ]), + ); + } } // difference.inDays is not reliable diff --git a/refilc_mobile_ui/lib/pages/timetable/timetable_page.i18n.dart b/refilc_mobile_ui/lib/pages/timetable/timetable_page.i18n.dart index c2d9f31..843090d 100644 --- a/refilc_mobile_ui/lib/pages/timetable/timetable_page.i18n.dart +++ b/refilc_mobile_ui/lib/pages/timetable/timetable_page.i18n.dart @@ -10,6 +10,9 @@ extension Localization on String { "error": "Failed to fetch timetable!", "empty_timetable": "Timetable is empty!", "break": "Break", + "full_screen_timetable": "Full Screen Timetable", + "show_breaks": "Show Breaks", + "show_exams_homework": "Exams and Homework", }, "hu_hu": { "timetable": "Órarend", @@ -18,6 +21,9 @@ extension Localization on String { "error": "Nem sikerült lekérni az órarendet!", "empty_timetable": "Az órarend üres!", "break": "Szünet", + "full_screen_timetable": "Teljes képernyős órarend", + "show_breaks": "Szünetek megjelenítése", + "show_exams_homework": "Dolgozatok és házik", }, "de_de": { "timetable": "Zeitplan", @@ -26,6 +32,9 @@ extension Localization on String { "error": "Der Fahrplan konnte nicht abgerufen werden!", "empty_timetable": "Der Zeitplan ist blank!", "break": "Pause", + "full_screen_timetable": "Vollbildfahrplan", + "show_breaks": "Pausen anzeigen", + "show_exams_homework": "Referate und Hausaufgaben", }, }; diff --git a/refilc_mobile_ui/lib/plus/components/plan_card.dart b/refilc_mobile_ui/lib/plus/components/plan_card.dart index d1a9567..27b0c9b 100644 --- a/refilc_mobile_ui/lib/plus/components/plan_card.dart +++ b/refilc_mobile_ui/lib/plus/components/plan_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:refilc/models/settings.dart'; import 'package:refilc_plus/providers/plus_provider.dart'; import 'package:refilc_plus/ui/mobile/plus/activation_view/activation_view.dart'; import 'package:refilc_mobile_ui/plus/plus_screen.i18n.dart'; @@ -50,6 +51,19 @@ class PlusPlanCard extends StatelessWidget { return; } + if (Provider.of(context).xFilcId == "none") { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text( + "Be kell kapcsolnod a Névtelen Analitikát a beállítások főoldalán, mielőtt reFilc+ előfizetést vásárolnál!", + style: + TextStyle(color: Colors.black, fontWeight: FontWeight.bold), + ), + backgroundColor: Colors.white, + )); + + return; + } + if (Provider.of(context, listen: false).hasPremium) { if (!active) { launchUrl( diff --git a/refilc_mobile_ui/lib/screens/login/login_screen.dart b/refilc_mobile_ui/lib/screens/login/login_screen.dart index a9f2dbd..572f3dc 100644 --- a/refilc_mobile_ui/lib/screens/login/login_screen.dart +++ b/refilc_mobile_ui/lib/screens/login/login_screen.dart @@ -3,10 +3,8 @@ import 'package:refilc/api/client.dart'; import 'package:refilc/api/login.dart'; import 'package:refilc/theme/colors/colors.dart'; -import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart'; import 'package:refilc_mobile_ui/common/custom_snack_bar.dart'; import 'package:refilc_mobile_ui/common/system_chrome.dart'; -import 'package:refilc_mobile_ui/common/widgets/absence/absence_display.dart'; import 'package:refilc_mobile_ui/screens/login/login_button.dart'; import 'package:refilc_mobile_ui/screens/login/login_input.dart'; import 'package:refilc_mobile_ui/screens/login/school_input/school_input.dart'; @@ -14,8 +12,6 @@ import 'package:refilc_mobile_ui/screens/settings/privacy_view.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'login_screen.i18n.dart'; -import 'package:carousel_slider/carousel_slider.dart'; -import 'package:flutter_svg/flutter_svg.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key, this.back = false}); @@ -79,450 +75,223 @@ class LoginScreenState extends State { @override Widget build(BuildContext context) { - precacheImage(const AssetImage('assets/images/showcase1.png'), context); - precacheImage(const AssetImage('assets/images/showcase2.png'), context); - precacheImage(const AssetImage('assets/images/showcase3.png'), context); - precacheImage(const AssetImage('assets/images/showcase4.png'), context); - bool selected = false; - return Scaffold( body: Container( - decoration: const BoxDecoration(color: Color(0xFFDAE4F7)), + decoration: BoxDecoration(color: AppColors.of(context).loginBackground), child: SingleChildScrollView( physics: const ClampingScrollPhysics(), controller: _scrollController, child: Container( - decoration: const BoxDecoration(color: Color(0xFFDAE4F7)), + decoration: + BoxDecoration(color: AppColors.of(context).loginBackground), width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, child: SafeArea( child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - // app icon - Padding( - padding: const EdgeInsets.only(left: 24, top: 20), - child: Row( - children: [ - Image.asset( - 'assets/icons/ic_rounded.png', - width: 30.0, - ), - const SizedBox(width: 8), - const Text( - 'reFilc', - style: TextStyle( - color: Color(0xFF050B15), - fontSize: 18.0, - fontWeight: FontWeight.bold, - fontFamily: 'Montserrat'), - ), - Material( - type: MaterialType.transparency, - child: showBack - ? BackButton(color: AppColors.of(context).text) - : const SizedBox(height: 48.0), - ), - ], - )), - Stack( - alignment: Alignment.bottomCenter, - children: [ - Column( - //login buttons and ui starts here - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - const SizedBox(height: 21), - CarouselSlider( - options: CarouselOptions( - height: MediaQuery.of(context).size.height, - viewportFraction: 1, - autoPlay: true, - autoPlayInterval: const Duration(seconds: 6), - pauseAutoPlayOnTouch: true), - items: [1, 2, 3, 4].map((i) { - return Builder( - builder: (BuildContext context) { - return Column( - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: - const EdgeInsets.only(left: 24), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisAlignment: - MainAxisAlignment.start, - children: [ - Text( - "welcome_title_$i".i18n, - style: const TextStyle( - color: Color(0xFF050B15), - fontSize: 19, - fontFamily: 'Montserrat', - fontWeight: FontWeight.w700, - height: 1.3), - ), - const SizedBox( - height: 14.375), //meth - Padding( - padding: const EdgeInsets.only( - right: 20), - child: Text( - "welcome_text_$i".i18n, - style: const TextStyle( - color: Color(0xFF050B15), - fontFamily: 'FigTree', - fontWeight: - FontWeight.w500, - fontSize: 17, - height: 1.3), - ), - ), - ], - )), - const SizedBox(height: 15.625), - Padding( - padding: const EdgeInsets.only( - left: 16, right: 16), - child: Image.asset( - 'assets/images/showcase$i.png')) - ], - ); - }, - ); - }).toList(), - ), - ], + Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 16.0, top: 12.0), + child: ClipOval( + child: Material( + type: MaterialType.transparency, + child: showBack + ? BackButton( + color: AppColors.of(context).loginPrimary) + : const SizedBox(height: 48.0), ), - Container( - height: 300, - width: double.infinity, - decoration: const BoxDecoration( - gradient: LinearGradient( - colors: [Color(0x00DAE4F7), Color(0xFFDAE4F7)], - stops: [0, 0.12], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: Padding( - padding: EdgeInsets.only(top: 50, bottom: MediaQuery.of(context).viewInsets.bottom), - child: Column( - children: [ - SizedBox( - height: 48, - width: double.infinity, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16), - child: FilledButton( - style: ButtonStyle( - shape: MaterialStateProperty.all< - RoundedRectangleBorder>( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(12)), - ))), - onPressed: () { - showModalBottomSheet( - backgroundColor: Colors.transparent, - context: context, - builder: (BuildContext context) { - return Container( - height: MediaQuery.of(context) - .size - .height * - 0.5 + MediaQuery.of(context).viewInsets.bottom, - decoration: const BoxDecoration( - color: Color(0xFFDAE4F7), - borderRadius: BorderRadius.only( - topRight: - Radius.circular(24.0), - topLeft: - Radius.circular(24.0), - ), - ), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.center, - mainAxisAlignment: - MainAxisAlignment.start, - children: [ - Padding( - padding: - const EdgeInsets.only( - top: 18), - child: Container( - decoration: - const BoxDecoration( - color: - Color(0xFFB9C8E5), - borderRadius: - BorderRadius.only( - topRight: - Radius.circular( - 2.0), - topLeft: - Radius.circular( - 2.0), - ), - ), - width: 40, - height: 4, - ), - ), - Container( - width: double.infinity, - child: AutofillGroup( - child: Column( - crossAxisAlignment: - CrossAxisAlignment - .end, - children: [ - // username - Padding( - padding: - const EdgeInsets - .only( - bottom: - 6.0), - child: Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Expanded( - child: Text( - "username" - .i18n, - maxLines: - 1, - style: - TextStyle( - color: AppColors.of(context) - .loginPrimary, - fontWeight: - FontWeight.w500, - fontSize: - 12.0, - ), - ), - ), - Expanded( - child: Text( - "usernameHint" - .i18n, - maxLines: - 1, - textAlign: - TextAlign - .right, - style: - TextStyle( - color: AppColors.of(context) - .loginSecondary, - fontWeight: - FontWeight.w500, - fontSize: - 12.0, - ), - ), - ), - ], - ), - ), - Padding( - padding: - const EdgeInsets - .only( - bottom: - 12.0), - child: LoginInput( - style: - LoginInputStyle - .username, - controller: - usernameController, - ), - ), - - // password - Padding( - padding: - const EdgeInsets - .only( - bottom: - 6.0), - child: Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Expanded( - child: Text( - "password" - .i18n, - maxLines: - 1, - style: - TextStyle( - color: AppColors.of(context) - .loginPrimary, - fontWeight: - FontWeight.w500, - fontSize: - 12.0, - ), - ), - ), - Expanded( - child: Text( - "passwordHint" - .i18n, - maxLines: - 1, - textAlign: - TextAlign - .right, - style: - TextStyle( - color: AppColors.of(context) - .loginSecondary, - fontWeight: - FontWeight.w500, - fontSize: - 12.0, - ), - ), - ), - ], - ), - ), - Padding( - padding: - const EdgeInsets - .only( - bottom: - 12.0), - child: LoginInput( - style: - LoginInputStyle - .password, - controller: - passwordController, - ), - ), - - // school - Padding( - padding: - const EdgeInsets - .only( - bottom: - 6.0), - child: Text( - "school".i18n, - maxLines: 1, - style: - TextStyle( - color: AppColors.of( - context) - .loginPrimary, - fontWeight: - FontWeight - .w500, - fontSize: - 12.0, - ), - ), - ), - SchoolInput( - scroll: - _scrollController, - controller: - schoolController, - ), - ], - ), - ), - ), - const Padding( - padding: EdgeInsets.only( - left: 22.0, - right: 22.0, - top: 0.0, - ), - ), - Padding( - padding: - const EdgeInsets.only( - top: 35.0, - left: 22.0, - right: 22.0, - ), - child: Visibility( - visible: _loginState != - LoginState - .inProgress, - replacement: - const Padding( - padding: EdgeInsets - .symmetric( - vertical: - 6.0), - child: - CircularProgressIndicator( - valueColor: - AlwaysStoppedAnimation< - Color>( - Colors - .white), - ), - ), - child: LoginButton( - child: Text( - "login".i18n, - maxLines: 1, - style: - const TextStyle( - fontWeight: - FontWeight - .bold, - fontSize: 20.0, - )), - onPressed: () => - _loginAPI( - context: - context), - ), - ), - ), - ]), - ); - }, - ); - }, - child: Text( - "login".i18n, - style: const TextStyle( - fontFamily: 'Montserrat', - fontSize: 20, - fontWeight: FontWeight.w700), - )), - ), - ), - const SizedBox(height: 8), - ], - ), - ), - ), - ], + ), ), + // app icon + Padding( + padding: EdgeInsets.zero, + child: Image.asset( + 'assets/icons/ic_rounded.png', + width: 50.0, + ), + ), + + // texts + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10.0, + vertical: 12.0, + ), + child: Text( + 'reFilc', + style: TextStyle( + color: AppColors.of(context).loginPrimary, + fontSize: 28.0, + fontWeight: FontWeight.bold, + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10.0, + ), + child: Text( + 'login_w_kreten'.i18n, + textAlign: TextAlign.center, + style: TextStyle( + color: AppColors.of(context).loginPrimary, + fontSize: 18.0, + fontWeight: FontWeight.w500, + height: 1.2, + ), + ), + ), + + const Spacer( + flex: 2, + ), + + // inputs + Padding( + padding: const EdgeInsets.only( + left: 22.0, + right: 22.0, + top: 0.0, + ), + child: AutofillGroup( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // username + Padding( + padding: const EdgeInsets.only(bottom: 6.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "username".i18n, + maxLines: 1, + style: TextStyle( + color: AppColors.of(context).loginPrimary, + fontWeight: FontWeight.w500, + fontSize: 12.0, + ), + ), + ), + Expanded( + child: Text( + "usernameHint".i18n, + maxLines: 1, + textAlign: TextAlign.right, + style: TextStyle( + color: + AppColors.of(context).loginSecondary, + fontWeight: FontWeight.w500, + fontSize: 12.0, + ), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: LoginInput( + style: LoginInputStyle.username, + controller: usernameController, + ), + ), + + // password + Padding( + padding: const EdgeInsets.only(bottom: 6.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + "password".i18n, + maxLines: 1, + style: TextStyle( + color: AppColors.of(context).loginPrimary, + fontWeight: FontWeight.w500, + fontSize: 12.0, + ), + ), + ), + Expanded( + child: Text( + "passwordHint".i18n, + maxLines: 1, + textAlign: TextAlign.right, + style: TextStyle( + color: + AppColors.of(context).loginSecondary, + fontWeight: FontWeight.w500, + fontSize: 12.0, + ), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: LoginInput( + style: LoginInputStyle.password, + controller: passwordController, + ), + ), + + // school + Padding( + padding: const EdgeInsets.only(bottom: 6.0), + child: Text( + "school".i18n, + maxLines: 1, + style: TextStyle( + color: AppColors.of(context).loginPrimary, + fontWeight: FontWeight.w500, + fontSize: 12.0, + ), + ), + ), + SchoolInput( + scroll: _scrollController, + controller: schoolController, + ), + ], + ), + ), + ), + + // login button + Padding( + padding: const EdgeInsets.only( + top: 35.0, + left: 22.0, + right: 22.0, + ), + child: Visibility( + visible: _loginState != LoginState.inProgress, + replacement: const Padding( + padding: EdgeInsets.symmetric(vertical: 6.0), + child: CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation(Colors.white), + ), + ), + child: LoginButton( + child: Text("login".i18n, + maxLines: 1, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20.0, + )), + onPressed: () => _loginAPI(context: context), + ), + ), + ), + + // error messages if (_loginState == LoginState.missingFields || _loginState == LoginState.invalidGrant || _loginState == LoginState.failed) @@ -543,6 +312,8 @@ class LoginScreenState extends State { textAlign: TextAlign.center, ), ), + const SizedBox(height: 22.0), + // privacy policy GestureDetector( onTap: () => PrivacyView.show(context), @@ -555,6 +326,10 @@ class LoginScreenState extends State { ), ), ), + + const Spacer( + flex: 1, + ), ], ), ), diff --git a/refilc_mobile_ui/lib/screens/login/login_screen_new.dart b/refilc_mobile_ui/lib/screens/login/login_screen_new.dart new file mode 100644 index 0000000..7c9cab0 --- /dev/null +++ b/refilc_mobile_ui/lib/screens/login/login_screen_new.dart @@ -0,0 +1,618 @@ +// // import 'dart:async'; + +// import 'package:refilc/api/client.dart'; +// import 'package:refilc/api/login.dart'; +// import 'package:refilc/theme/colors/colors.dart'; +// import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart'; +// import 'package:refilc_mobile_ui/common/custom_snack_bar.dart'; +// import 'package:refilc_mobile_ui/common/system_chrome.dart'; +// import 'package:refilc_mobile_ui/common/widgets/absence/absence_display.dart'; +// import 'package:refilc_mobile_ui/screens/login/login_button.dart'; +// import 'package:refilc_mobile_ui/screens/login/login_input.dart'; +// import 'package:refilc_mobile_ui/screens/login/school_input/school_input.dart'; +// import 'package:refilc_mobile_ui/screens/settings/privacy_view.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter/services.dart'; +// import 'login_screen.i18n.dart'; +// import 'package:carousel_slider/carousel_slider.dart'; +// import 'package:flutter_svg/flutter_svg.dart'; + +// class LoginScreen extends StatefulWidget { +// const LoginScreen({super.key, this.back = false}); + +// final bool back; + +// @override +// LoginScreenState createState() => LoginScreenState(); +// } + +// class LoginScreenState extends State { +// final usernameController = TextEditingController(); +// final passwordController = TextEditingController(); +// final schoolController = SchoolInputController(); +// final _scrollController = ScrollController(); + +// LoginState _loginState = LoginState.normal; +// bool showBack = false; + +// // Scaffold Gradient background +// // final LinearGradient _backgroundGradient = const LinearGradient( +// // colors: [ +// // Color.fromARGB(255, 61, 122, 244), +// // Color.fromARGB(255, 23, 77, 185), +// // Color.fromARGB(255, 7, 42, 112), +// // ], +// // begin: Alignment(-0.8, -1.0), +// // end: Alignment(0.8, 1.0), +// // stops: [-1.0, 0.0, 1.0], +// // ); + +// late String tempUsername = ''; + +// @override +// void initState() { +// super.initState(); +// showBack = widget.back; + +// SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( +// statusBarColor: Colors.transparent, +// statusBarIconBrightness: Brightness.light, +// systemNavigationBarColor: Colors.white, +// systemNavigationBarIconBrightness: Brightness.dark, +// )); + +// FilcAPI.getSchools().then((schools) { +// if (schools != null) { +// schoolController.update(() { +// schoolController.schools = schools; +// }); +// } else { +// ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( +// content: Text("schools_error".i18n, +// style: const TextStyle(color: Colors.white)), +// backgroundColor: AppColors.of(context).red, +// context: context, +// )); +// } +// }); +// } + +// @override +// Widget build(BuildContext context) { +// precacheImage(const AssetImage('assets/images/showcase1.png'), context); +// precacheImage(const AssetImage('assets/images/showcase2.png'), context); +// precacheImage(const AssetImage('assets/images/showcase3.png'), context); +// precacheImage(const AssetImage('assets/images/showcase4.png'), context); +// bool selected = false; + +// return Scaffold( +// body: Container( +// decoration: const BoxDecoration(color: Color(0xFFDAE4F7)), +// child: SingleChildScrollView( +// physics: const ClampingScrollPhysics(), +// controller: _scrollController, +// child: Container( +// decoration: const BoxDecoration(color: Color(0xFFDAE4F7)), +// width: MediaQuery.of(context).size.width, +// height: MediaQuery.of(context).size.height, +// child: SafeArea( +// child: Column( +// children: [ +// // app icon +// Padding( +// padding: const EdgeInsets.only(left: 24, top: 20), +// child: Row( +// children: [ +// Image.asset( +// 'assets/icons/ic_rounded.png', +// width: 30.0, +// ), +// const SizedBox(width: 8), +// const Text( +// 'reFilc', +// style: TextStyle( +// color: Color(0xFF050B15), +// fontSize: 18.0, +// fontWeight: FontWeight.bold, +// fontFamily: 'Montserrat'), +// ), +// Material( +// type: MaterialType.transparency, +// child: showBack +// ? BackButton(color: AppColors.of(context).text) +// : const SizedBox(height: 48.0), +// ), +// ], +// )), +// Stack( +// alignment: Alignment.bottomCenter, +// children: [ +// Column( +// //login buttons and ui starts here +// mainAxisAlignment: MainAxisAlignment.end, +// crossAxisAlignment: CrossAxisAlignment.end, +// children: [ +// const SizedBox(height: 21), +// CarouselSlider( +// options: CarouselOptions( +// height: MediaQuery.of(context).size.height, +// viewportFraction: 1, +// autoPlay: true, +// autoPlayInterval: const Duration(seconds: 6), +// pauseAutoPlayOnTouch: true), +// items: [1, 2, 3, 4].map((i) { +// return Builder( +// builder: (BuildContext context) { +// return Column( +// crossAxisAlignment: +// CrossAxisAlignment.start, +// mainAxisAlignment: MainAxisAlignment.start, +// children: [ +// Padding( +// padding: +// const EdgeInsets.only(left: 24), +// child: Column( +// crossAxisAlignment: +// CrossAxisAlignment.start, +// mainAxisAlignment: +// MainAxisAlignment.start, +// children: [ +// Text( +// "welcome_title_$i".i18n, +// style: const TextStyle( +// color: Color(0xFF050B15), +// fontSize: 19, +// fontFamily: 'Montserrat', +// fontWeight: FontWeight.w700, +// height: 1.3), +// ), +// const SizedBox( +// height: 14.375), //meth +// Padding( +// padding: const EdgeInsets.only( +// right: 20), +// child: Text( +// "welcome_text_$i".i18n, +// style: const TextStyle( +// color: Color(0xFF050B15), +// fontFamily: 'FigTree', +// fontWeight: +// FontWeight.w500, +// fontSize: 17, +// height: 1.3), +// ), +// ), +// ], +// )), +// const SizedBox(height: 15.625), +// Padding( +// padding: const EdgeInsets.only( +// left: 16, right: 16), +// child: Image.asset( +// 'assets/images/showcase$i.png')) +// ], +// ); +// }, +// ); +// }).toList(), +// ), +// ], +// ), +// Container( +// height: 300, +// width: double.infinity, +// decoration: const BoxDecoration( +// gradient: LinearGradient( +// colors: [Color(0x00DAE4F7), Color(0xFFDAE4F7)], +// stops: [0, 0.12], +// begin: Alignment.topCenter, +// end: Alignment.bottomCenter, +// ), +// ), +// child: Padding( +// padding: EdgeInsets.only(top: 50, bottom: MediaQuery.of(context).viewInsets.bottom), +// child: Column( +// children: [ +// SizedBox( +// height: 48, +// width: double.infinity, +// child: Padding( +// padding: const EdgeInsets.symmetric( +// horizontal: 16), +// child: FilledButton( +// style: ButtonStyle( +// shape: MaterialStateProperty.all< +// RoundedRectangleBorder>( +// const RoundedRectangleBorder( +// borderRadius: BorderRadius.all( +// Radius.circular(12)), +// ))), +// onPressed: () { +// showModalBottomSheet( +// backgroundColor: Colors.transparent, +// context: context, +// builder: (BuildContext context) { +// return Container( +// height: MediaQuery.of(context) +// .size +// .height * +// 0.5 + MediaQuery.of(context).viewInsets.bottom, +// decoration: const BoxDecoration( +// color: Color(0xFFDAE4F7), +// borderRadius: BorderRadius.only( +// topRight: +// Radius.circular(24.0), +// topLeft: +// Radius.circular(24.0), +// ), +// ), +// child: Column( +// crossAxisAlignment: +// CrossAxisAlignment.center, +// mainAxisAlignment: +// MainAxisAlignment.start, +// children: [ +// Padding( +// padding: +// const EdgeInsets.only( +// top: 18), +// child: Container( +// decoration: +// const BoxDecoration( +// color: +// Color(0xFFB9C8E5), +// borderRadius: +// BorderRadius.only( +// topRight: +// Radius.circular( +// 2.0), +// topLeft: +// Radius.circular( +// 2.0), +// ), +// ), +// width: 40, +// height: 4, +// ), +// ), +// Container( +// width: double.infinity, +// child: AutofillGroup( +// child: Column( +// crossAxisAlignment: +// CrossAxisAlignment +// .end, +// children: [ +// // username +// Padding( +// padding: +// const EdgeInsets +// .only( +// bottom: +// 6.0), +// child: Row( +// mainAxisAlignment: +// MainAxisAlignment +// .spaceBetween, +// children: [ +// Expanded( +// child: Text( +// "username" +// .i18n, +// maxLines: +// 1, +// style: +// TextStyle( +// color: AppColors.of(context) +// .loginPrimary, +// fontWeight: +// FontWeight.w500, +// fontSize: +// 12.0, +// ), +// ), +// ), +// Expanded( +// child: Text( +// "usernameHint" +// .i18n, +// maxLines: +// 1, +// textAlign: +// TextAlign +// .right, +// style: +// TextStyle( +// color: AppColors.of(context) +// .loginSecondary, +// fontWeight: +// FontWeight.w500, +// fontSize: +// 12.0, +// ), +// ), +// ), +// ], +// ), +// ), +// Padding( +// padding: +// const EdgeInsets +// .only( +// bottom: +// 12.0), +// child: LoginInput( +// style: +// LoginInputStyle +// .username, +// controller: +// usernameController, +// ), +// ), + +// // password +// Padding( +// padding: +// const EdgeInsets +// .only( +// bottom: +// 6.0), +// child: Row( +// mainAxisAlignment: +// MainAxisAlignment +// .spaceBetween, +// children: [ +// Expanded( +// child: Text( +// "password" +// .i18n, +// maxLines: +// 1, +// style: +// TextStyle( +// color: AppColors.of(context) +// .loginPrimary, +// fontWeight: +// FontWeight.w500, +// fontSize: +// 12.0, +// ), +// ), +// ), +// Expanded( +// child: Text( +// "passwordHint" +// .i18n, +// maxLines: +// 1, +// textAlign: +// TextAlign +// .right, +// style: +// TextStyle( +// color: AppColors.of(context) +// .loginSecondary, +// fontWeight: +// FontWeight.w500, +// fontSize: +// 12.0, +// ), +// ), +// ), +// ], +// ), +// ), +// Padding( +// padding: +// const EdgeInsets +// .only( +// bottom: +// 12.0), +// child: LoginInput( +// style: +// LoginInputStyle +// .password, +// controller: +// passwordController, +// ), +// ), + +// // school +// Padding( +// padding: +// const EdgeInsets +// .only( +// bottom: +// 6.0), +// child: Text( +// "school".i18n, +// maxLines: 1, +// style: +// TextStyle( +// color: AppColors.of( +// context) +// .loginPrimary, +// fontWeight: +// FontWeight +// .w500, +// fontSize: +// 12.0, +// ), +// ), +// ), +// SchoolInput( +// scroll: +// _scrollController, +// controller: +// schoolController, +// ), +// ], +// ), +// ), +// ), +// const Padding( +// padding: EdgeInsets.only( +// left: 22.0, +// right: 22.0, +// top: 0.0, +// ), +// ), +// Padding( +// padding: +// const EdgeInsets.only( +// top: 35.0, +// left: 22.0, +// right: 22.0, +// ), +// child: Visibility( +// visible: _loginState != +// LoginState +// .inProgress, +// replacement: +// const Padding( +// padding: EdgeInsets +// .symmetric( +// vertical: +// 6.0), +// child: +// CircularProgressIndicator( +// valueColor: +// AlwaysStoppedAnimation< +// Color>( +// Colors +// .white), +// ), +// ), +// child: LoginButton( +// child: Text( +// "login".i18n, +// maxLines: 1, +// style: +// const TextStyle( +// fontWeight: +// FontWeight +// .bold, +// fontSize: 20.0, +// )), +// onPressed: () => +// _loginAPI( +// context: +// context), +// ), +// ), +// ), +// ]), +// ); +// }, +// ); +// }, +// child: Text( +// "login".i18n, +// style: const TextStyle( +// fontFamily: 'Montserrat', +// fontSize: 20, +// fontWeight: FontWeight.w700), +// )), +// ), +// ), +// const SizedBox(height: 8), +// ], +// ), +// ), +// ), +// ], +// ), + +// if (_loginState == LoginState.missingFields || +// _loginState == LoginState.invalidGrant || +// _loginState == LoginState.failed) +// Padding( +// padding: const EdgeInsets.only( +// top: 8.0, left: 12.0, right: 12.0), +// child: Text( +// [ +// "missing_fields", +// "invalid_grant", +// "error" +// ][_loginState.index] +// .i18n, +// style: const TextStyle( +// color: Colors.red, +// fontWeight: FontWeight.w500, +// ), +// textAlign: TextAlign.center, +// ), +// ), +// // privacy policy +// GestureDetector( +// onTap: () => PrivacyView.show(context), +// child: Text( +// 'privacy'.i18n, +// style: TextStyle( +// color: AppColors.of(context).loginSecondary, +// fontWeight: FontWeight.w500, +// fontSize: 14.0, +// ), +// ), +// ), +// ], +// ), +// ), +// ), +// ), +// ), +// ); +// } + +// void _loginAPI({required BuildContext context}) { +// String username = usernameController.text; +// String password = passwordController.text; + +// tempUsername = username; + +// if (username == "" || +// password == "" || +// schoolController.selectedSchool == null) { +// return setState(() => _loginState = LoginState.missingFields); +// } + +// // ignore: no_leading_underscores_for_local_identifiers +// void _callAPI() { +// loginAPI( +// username: username, +// password: password, +// instituteCode: schoolController.selectedSchool!.instituteCode, +// context: context, +// onLogin: (user) { +// ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( +// context: context, +// brightness: Brightness.light, +// content: Text("welcome".i18n.fill([user.name]), +// overflow: TextOverflow.ellipsis), +// )); +// }, +// onSuccess: () { +// ScaffoldMessenger.of(context).hideCurrentSnackBar(); +// setSystemChrome(context); +// Navigator.of(context).pushReplacementNamed("login_to_navigation"); +// }).then( +// (res) => setState(() { +// // if (res == LoginState.invalidGrant && +// // tempUsername.replaceAll(username, '').length <= 3) { +// // tempUsername = username + ' '; +// // Timer( +// // const Duration(milliseconds: 500), +// // () => _loginAPI(context: context), +// // ); +// // // _loginAPI(context: context); +// // } else { +// _loginState = res; +// // } +// }), +// ); +// } + +// setState(() => _loginState = LoginState.inProgress); +// _callAPI(); +// } +// } diff --git a/refilc_mobile_ui/lib/screens/settings/settings_helper.dart b/refilc_mobile_ui/lib/screens/settings/settings_helper.dart index e566eea..75959e2 100644 --- a/refilc_mobile_ui/lib/screens/settings/settings_helper.dart +++ b/refilc_mobile_ui/lib/screens/settings/settings_helper.dart @@ -751,7 +751,7 @@ class _BellDelaySettingState extends State Provider.of(context, listen: false) .update(bellDelay: currentDelay.inSeconds); _tabController.index = currentDelay.inSeconds > 0 ? 1 : 0; - if(Platform.isIOS){ + if (Platform.isIOS) { LiveCardProvider.hasActivitySettingsChanged = true; } setState(() {}); @@ -764,7 +764,7 @@ class _BellDelaySettingState extends State //Provider.of(context, listen: false).update(context, rounding: (r * 10).toInt()); Provider.of(context, listen: false) .update(bellDelay: currentDelay.inSeconds); - if(Platform.isIOS){ + if (Platform.isIOS) { LiveCardProvider.hasActivitySettingsChanged = true; } Navigator.of(context).maybePop(); diff --git a/refilc_plus b/refilc_plus index 574d3ab..1f5cca7 160000 --- a/refilc_plus +++ b/refilc_plus @@ -1 +1 @@ -Subproject commit 574d3ab0aca1bb68d21607c2b9df1046aa46fc55 +Subproject commit 1f5cca7b8e2ac896155a6c494e79fb057628379e