diff --git a/dictionaries/README.md b/dictionaries/README.md index 90041e7e..78d0d17a 100644 --- a/dictionaries/README.md +++ b/dictionaries/README.md @@ -24,7 +24,7 @@ This dictionary contains the names of the API endpoints. This dictionary contains the service names related to NTUT. - `iSchool` -- `Zuvio` +- `NTUT` ### third_party_terms diff --git a/dictionaries/ntut_terms.dic b/dictionaries/ntut_terms.dic index 19983725..54857bf6 100644 --- a/dictionaries/ntut_terms.dic +++ b/dictionaries/ntut_terms.dic @@ -1,3 +1,2 @@ iSchool NTUT -Zuvio diff --git a/lib/generated/intl/messages_all.dart b/lib/generated/intl/messages_all.dart index 1771ed2d..c65333cc 100644 --- a/lib/generated/intl/messages_all.dart +++ b/lib/generated/intl/messages_all.dart @@ -19,10 +19,10 @@ import 'package:intl/src/intl_helpers.dart'; import 'messages_en.dart' as messages_en; import 'messages_zh_TW.dart' as messages_zh_tw; -typedef Future LibraryLoader(); +typedef LibraryLoader = Future Function(); Map _deferredLibraries = { - 'en': () => new SynchronousFuture(null), - 'zh_TW': () => new SynchronousFuture(null), + 'en': () => SynchronousFuture(null), + 'zh_TW': () => SynchronousFuture(null), }; MessageLookupByLibrary? _findExact(String localeName) { @@ -41,13 +41,13 @@ Future initializeMessages(String localeName) { var availableLocale = Intl.verifiedLocale(localeName, (locale) => _deferredLibraries[locale] != null, onFailure: (_) => null); if (availableLocale == null) { - return new SynchronousFuture(false); + return SynchronousFuture(false); } var lib = _deferredLibraries[availableLocale]; - lib == null ? new SynchronousFuture(false) : lib(); - initializeInternalMessageLookup(() => new CompositeMessageLookup()); + lib == null ? SynchronousFuture(false) : lib(); + initializeInternalMessageLookup(() => CompositeMessageLookup()); messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); - return new SynchronousFuture(true); + return SynchronousFuture(true); } bool _messagesExistFor(String locale) { diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 60f02c7f..eb4e4f2f 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -13,9 +13,9 @@ import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; -final messages = new MessageLookup(); +final messages = MessageLookup(); -typedef String MessageIfAbsent(String messageStr, List args); +typedef MessageIfAbsent = String Function(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'en'; @@ -53,7 +53,6 @@ class MessageLookup extends MessageLookupByLibrary { "areYouSureToDownload": MessageLookupByLibrary.simpleMessage("Are you sure you want to download the file"), "authCodeFail": MessageLookupByLibrary.simpleMessage("Verification code error"), "autoAppCheck": MessageLookupByLibrary.simpleMessage("Auto App Check"), - "autoRollCall": MessageLookupByLibrary.simpleMessage("Auto RollCall"), "begin": MessageLookupByLibrary.simpleMessage("Begin"), "calculationCredit": MessageLookupByLibrary.simpleMessage("Calculation credit"), "calendar": MessageLookupByLibrary.simpleMessage("Calendar"), @@ -239,11 +238,6 @@ class MessageLookup extends MessageLookupByLibrary { "restart": MessageLookupByLibrary.simpleMessage("Restart"), "resultsOfVariousSubjects": MessageLookupByLibrary.simpleMessage("Results of various subjects"), "revisedCommonCompulsory": MessageLookupByLibrary.simpleMessage("Revised Common Compulsory"), - "rollCall": MessageLookupByLibrary.simpleMessage("RollCall"), - "rollCallRemind": MessageLookupByLibrary.simpleMessage("RollCall Remind"), - "rollCallScheduleCanceled": MessageLookupByLibrary.simpleMessage("The roll call schedule has been cancelled"), - "rollCallScheduledSuccessfully": - MessageLookupByLibrary.simpleMessage("The roll call schedule was added successfully"), "ruleDimension": MessageLookupByLibrary.simpleMessage("Rule of law"), "sameOldPassword": MessageLookupByLibrary.simpleMessage("Same password as before"), "save": MessageLookupByLibrary.simpleMessage("Save"), @@ -305,8 +299,6 @@ class MessageLookup extends MessageLookupByLibrary { "videoMayLoadFailedWarningMsg": MessageLookupByLibrary.simpleMessage( "The video loading time depends on the current network environment\nIf it takes too long to load, try leave this page and trying again"), "warning": MessageLookupByLibrary.simpleMessage("Warning"), - "willRestart": MessageLookupByLibrary.simpleMessage("Will restart automatically"), - "zuvioAutoRollCallFeatureReleaseNotice": - MessageLookupByLibrary.simpleMessage("Zuvio\'s (auto) roll-call reminder is coming soon!") + "willRestart": MessageLookupByLibrary.simpleMessage("Will restart automatically") }; } diff --git a/lib/generated/intl/messages_zh_TW.dart b/lib/generated/intl/messages_zh_TW.dart index 839f6481..e9e01efa 100644 --- a/lib/generated/intl/messages_zh_TW.dart +++ b/lib/generated/intl/messages_zh_TW.dart @@ -13,9 +13,9 @@ import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; -final messages = new MessageLookup(); +final messages = MessageLookup(); -typedef String MessageIfAbsent(String messageStr, List args); +typedef MessageIfAbsent = String Function(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'zh_TW'; @@ -51,7 +51,6 @@ class MessageLookup extends MessageLookupByLibrary { "areYouSureToDownload": MessageLookupByLibrary.simpleMessage("確定要下載檔案嗎"), "authCodeFail": MessageLookupByLibrary.simpleMessage("驗證碼錯誤"), "autoAppCheck": MessageLookupByLibrary.simpleMessage("自動檢查更新"), - "autoRollCall": MessageLookupByLibrary.simpleMessage("自動點名"), "begin": MessageLookupByLibrary.simpleMessage("開始"), "calculationCredit": MessageLookupByLibrary.simpleMessage("計算學分"), "calendar": MessageLookupByLibrary.simpleMessage("行事曆"), @@ -229,10 +228,6 @@ class MessageLookup extends MessageLookupByLibrary { "restart": MessageLookupByLibrary.simpleMessage("重試"), "resultsOfVariousSubjects": MessageLookupByLibrary.simpleMessage("各科成績"), "revisedCommonCompulsory": MessageLookupByLibrary.simpleMessage("校訂共同必修"), - "rollCall": MessageLookupByLibrary.simpleMessage("點名"), - "rollCallRemind": MessageLookupByLibrary.simpleMessage("點名提醒"), - "rollCallScheduleCanceled": MessageLookupByLibrary.simpleMessage("點名行程已取消"), - "rollCallScheduledSuccessfully": MessageLookupByLibrary.simpleMessage("點名行程新增成功"), "ruleDimension": MessageLookupByLibrary.simpleMessage("法治向度"), "sameOldPassword": MessageLookupByLibrary.simpleMessage("不可以與之前密碼相同"), "save": MessageLookupByLibrary.simpleMessage("儲存"), @@ -288,7 +283,6 @@ class MessageLookup extends MessageLookupByLibrary { "versionInfo": MessageLookupByLibrary.simpleMessage("版本資訊"), "videoMayLoadFailedWarningMsg": MessageLookupByLibrary.simpleMessage("影片載入時間視當下網路環境而定\n若載入時間過長,請嘗試退出後再試一次"), "warning": MessageLookupByLibrary.simpleMessage("警告"), - "willRestart": MessageLookupByLibrary.simpleMessage("將自動重啟並套用語言"), - "zuvioAutoRollCallFeatureReleaseNotice": MessageLookupByLibrary.simpleMessage("Zuvio (自動)點名提醒的功能即將上線\n敬請期待!") + "willRestart": MessageLookupByLibrary.simpleMessage("將自動重啟並套用語言") }; } diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index b7bcad38..fad70c64 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -2428,36 +2428,6 @@ class S { ); } - /// `Auto RollCall` - String get autoRollCall { - return Intl.message( - 'Auto RollCall', - name: 'autoRollCall', - desc: '', - args: [], - ); - } - - /// `RollCall` - String get rollCall { - return Intl.message( - 'RollCall', - name: 'rollCall', - desc: '', - args: [], - ); - } - - /// `RollCall Remind` - String get rollCallRemind { - return Intl.message( - 'RollCall Remind', - name: 'rollCallRemind', - desc: '', - args: [], - ); - } - /// `The video loading time depends on the current network environment\nIf it takes too long to load, try leave this page and trying again` String get videoMayLoadFailedWarningMsg { return Intl.message( @@ -2468,26 +2438,6 @@ class S { ); } - /// `The roll call schedule was added successfully` - String get rollCallScheduledSuccessfully { - return Intl.message( - 'The roll call schedule was added successfully', - name: 'rollCallScheduledSuccessfully', - desc: '', - args: [], - ); - } - - /// `The roll call schedule has been cancelled` - String get rollCallScheduleCanceled { - return Intl.message( - 'The roll call schedule has been cancelled', - name: 'rollCallScheduleCanceled', - desc: '', - args: [], - ); - } - /// `Missing required information` String get missingRequiredInformation { return Intl.message( @@ -2578,16 +2528,6 @@ class S { ); } - /// `Zuvio's (auto) roll-call reminder is coming soon!` - String get zuvioAutoRollCallFeatureReleaseNotice { - return Intl.message( - 'Zuvio\'s (auto) roll-call reminder is coming soon!', - name: 'zuvioAutoRollCallFeatureReleaseNotice', - desc: '', - args: [], - ); - } - /// `Coming Soon!` String get comingSoon { return Intl.message( diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 0de91936..426d1c67 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -238,12 +238,7 @@ "save": "Save", "loading": "Loading...", "pleaseConnectToNetwork": "Please connect to network", - "autoRollCall": "Auto RollCall", - "rollCall": "RollCall", - "rollCallRemind": "RollCall Remind", "videoMayLoadFailedWarningMsg": "The video loading time depends on the current network environment\nIf it takes too long to load, try leave this page and trying again", - "rollCallScheduledSuccessfully": "The roll call schedule was added successfully", - "rollCallScheduleCanceled": "The roll call schedule has been cancelled", "missingRequiredInformation": "Missing required information", "incorrectInformationEntered": "Incorrect information entered", "pleaseSelectStartTime": "Please select a start time", @@ -253,7 +248,6 @@ "capitalAdd": "ADD", "end": "End", "begin": "Begin", - "zuvioAutoRollCallFeatureReleaseNotice": "Zuvio's (auto) roll-call reminder is coming soon!", "comingSoon": "Coming Soon!", "androidPrivateBrowseGuideTitle": "About Incognito Browse", "androidPrivateBrowseGuideSubTitle": "Open Incognito browsing to enhanced security", @@ -268,4 +262,4 @@ "nationalTaipeiUniversity": "NTPU", "taipeiMedicineUniversity": "TMU", "aduit": "Aduit" -} \ No newline at end of file +} diff --git a/lib/l10n/intl_zh_TW.arb b/lib/l10n/intl_zh_TW.arb index 2661478a..5cda410a 100644 --- a/lib/l10n/intl_zh_TW.arb +++ b/lib/l10n/intl_zh_TW.arb @@ -238,12 +238,7 @@ "save": "儲存", "loading": "載入中...", "pleaseConnectToNetwork": "請確認網路連接狀態", - "autoRollCall": "自動點名", - "rollCall": "點名", - "rollCallRemind": "點名提醒", "videoMayLoadFailedWarningMsg": "影片載入時間視當下網路環境而定\n若載入時間過長,請嘗試退出後再試一次", - "rollCallScheduledSuccessfully": "點名行程新增成功", - "rollCallScheduleCanceled": "點名行程已取消", "missingRequiredInformation": "缺少必要資訊", "incorrectInformationEntered": "輸入的資訊有誤", "pleaseSelectStartTime": "請選擇起始時間", @@ -253,7 +248,6 @@ "capitalAdd": "新增", "end": "結束", "begin": "開始", - "zuvioAutoRollCallFeatureReleaseNotice": "Zuvio (自動)點名提醒的功能即將上線\n敬請期待!", "comingSoon": "即將上線!", "androidPrivateBrowseGuideTitle": "關於隱私瀏覽", "androidPrivateBrowseGuideSubTitle": "開啟隱私瀏覽,安全更有保障", @@ -268,4 +262,4 @@ "nationalTaipeiUniversity": "國立臺北大學", "taipeiMedicineUniversity": "臺北醫學大學", "aduit": "隨班附讀" -} \ No newline at end of file +} diff --git a/lib/src/config/app_config.dart b/lib/src/config/app_config.dart index 7e217471..0d3b59b6 100644 --- a/lib/src/config/app_config.dart +++ b/lib/src/config/app_config.dart @@ -3,7 +3,6 @@ import 'package:flutter_app/src/util/remote_config_util.dart'; class AppConfig { static const appName = "TAT"; static const methodChannelName = "tat/global"; - static final zuvioRollCallFeatureEnabled = RemoteConfigUtil.getFeatureToggleZuvioRollCallFlag(); // Only for android use. static final androidChromeIncognitoFlagSetupPageUrl = RemoteConfigUtil.getAndroidIncognitoSetupGuidePageUrl(); diff --git a/lib/src/controllers/zuvio_auth_controller.dart b/lib/src/controllers/zuvio_auth_controller.dart deleted file mode 100644 index ebece80a..00000000 --- a/lib/src/controllers/zuvio_auth_controller.dart +++ /dev/null @@ -1,88 +0,0 @@ -// ignore_for_file: import_of_legacy_library_into_null_safe - -import 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter_app/debug/log/log.dart'; -import 'package:flutter_app/src/controllers/suspend_interactions_transaction_mixin.dart'; -import 'package:flutter_app/src/r.dart'; -import 'package:flutter_app/src/store/local_storage.dart'; -import 'package:flutter_app/ui/other/msg_dialog.dart'; -import 'package:get/get.dart'; -import 'package:tat_core/core/zuvio/domain/login_credential.dart'; -import 'package:tat_core/core/zuvio/domain/user_info.dart'; -import 'package:tat_core/core/zuvio/usecase/login_use_case.dart'; - -class ZAuthController extends GetxController with SuspendInteractionsTransaction { - ZAuthController({ - required this.isLoginBtnEnabled, - required this.isInputBoxesEnabled, - required ZLoginUseCase loginUseCase, - }) : _loginUseCase = loginUseCase; - - static ZAuthController get to => Get.find(); - - bool isLoginBtnEnabled; - bool isInputBoxesEnabled; - - final ZLoginUseCase _loginUseCase; - - @override - void resumeUIInteractions() { - isLoginBtnEnabled = true; - isInputBoxesEnabled = true; - } - - @override - void suspendUIInteractions() { - isLoginBtnEnabled = false; - isInputBoxesEnabled = false; - } - - Future _saveCredential(ZLoginCredential credential) => - LocalStorage.instance.saveZuvioLoginCredential(credential); - - Future _saveUserInfo(ZUserInfo userInfo) => LocalStorage.instance.saveZuvioUserInfo(userInfo); - - Future _login(String username, String password) async { - final loginCredential = ZLoginCredential(email: username, password: password); - final result = await _loginUseCase(credential: loginCredential); - - if (result.userInfo == null) { - Log.d('Zuvio login failed.'); - - // TODO: move dialog showing to the UI layer. - MsgDialog(MsgDialogParameter( - desc: result.msg, - title: R.current.error, - )).show(); - - return; - } - - Log.d('Zuvio login successfully.'); - - await _saveCredential(loginCredential); - Log.d('Zuvio login credential saved.'); - - await _saveUserInfo(result.userInfo!); - Log.d('Zuvio user info saved.'); - - if (kDebugMode) { - Log.d(LocalStorage.instance.getZuvioLoginCredential().email); - Log.d(LocalStorage.instance.getZuvioUserInfo()); - } - } - - Future login(String username, String password) => - suspendInteractionsTransaction(transaction: () => _login(username, password)); - - bool isLoggedIntoZuvio() { - final accessToken = LocalStorage.instance.getZuvioUserInfo()?.accessToken; - - // TODO(TU): Check if the accessToken is valid. - return accessToken != null && accessToken.isNotEmpty; - } - - ZUserInfo? currentZUserInfo() => LocalStorage.instance.getZuvioUserInfo(); -} diff --git a/lib/src/controllers/zuvio_auto_roll_call_schedule_controller.dart b/lib/src/controllers/zuvio_auto_roll_call_schedule_controller.dart deleted file mode 100644 index 52521e1d..00000000 --- a/lib/src/controllers/zuvio_auto_roll_call_schedule_controller.dart +++ /dev/null @@ -1,177 +0,0 @@ -// ignore_for_file: import_of_legacy_library_into_null_safe - -import 'package:flutter_app/debug/log/log.dart'; -import 'package:flutter_app/src/controllers/suspend_interactions_transaction_mixin.dart'; -import 'package:flutter_app/src/controllers/zuvio_auth_controller.dart'; -import 'package:flutter_app/src/r.dart'; -import 'package:flutter_app/ui/other/msg_dialog.dart'; -import 'package:flutter_app/ui/other/my_progress_dialog.dart'; -import 'package:get/get.dart'; -import 'package:tat_core/tat_core.dart'; -import 'package:uuid/uuid.dart'; - -class ZAutoRollCallScheduleController extends GetxController with SuspendInteractionsTransaction { - ZAutoRollCallScheduleController({ - required ZGetRollCallUseCase getRollCallUseCase, - required ZMakeRollCallUseCase makeRollCallUseCase, - required ZGetStudentCourseListUseCase getCourseListUseCase, - required AddAutoRollCallScheduleUseCase addAutoRollCallScheduleUseCase, - required GetMyAutoRollCallScheduleUseCase getMyAutoRollCallScheduleUseCase, - required CancelAutoRollCallScheduleUseCase cancelAutoRollCallScheduleUseCase, - required EnableAutoRollCallScheduleUseCase enableAutoRollCallScheduleUseCase, - required DisableAutoRollCallScheduleUseCase disableAutoRollCallScheduleUseCase, - }) : _getRollCallUseCase = getRollCallUseCase, - _makeRollCallUseCase = makeRollCallUseCase, - _getCourseListUseCase = getCourseListUseCase, - _addAutoRollCallScheduleUseCase = addAutoRollCallScheduleUseCase, - _getMyAutoRollCallScheduleUseCase = getMyAutoRollCallScheduleUseCase, - _cancelAutoRollCallScheduleUseCase = cancelAutoRollCallScheduleUseCase, - _enableAutoRollCallScheduleUseCase = enableAutoRollCallScheduleUseCase, - _disableAutoRollCallScheduleUseCase = disableAutoRollCallScheduleUseCase; - - static ZAutoRollCallScheduleController get to => Get.find(); - - final ZGetRollCallUseCase _getRollCallUseCase; - final ZMakeRollCallUseCase _makeRollCallUseCase; - final ZGetStudentCourseListUseCase _getCourseListUseCase; - final AddAutoRollCallScheduleUseCase _addAutoRollCallScheduleUseCase; - final GetMyAutoRollCallScheduleUseCase _getMyAutoRollCallScheduleUseCase; - final CancelAutoRollCallScheduleUseCase _cancelAutoRollCallScheduleUseCase; - final EnableAutoRollCallScheduleUseCase _enableAutoRollCallScheduleUseCase; - final DisableAutoRollCallScheduleUseCase _disableAutoRollCallScheduleUseCase; - - final courses = []; - final schedules = []; - - @override - void resumeUIInteractions() { - MyProgressDialog.hideProgressDialog(); - } - - @override - void suspendUIInteractions() { - MyProgressDialog.progressDialog(R.current.loading); - } - - ZUserInfo? _getUserInfo() { - final userInfo = ZAuthController.to.currentZUserInfo(); - - if (userInfo == null) { - MsgDialog( - MsgDialogParameter( - desc: R.current.pleaseLogin, - title: R.current.error, - ), - ).show(); - Log.e('User may not logged in, could not get user info'); - return null; - } - - return userInfo; - } - - String _generateId() => const Uuid().v4(); - - Future _enableSchedule(AutoRollCallSchedule schedule) async { - await _enableAutoRollCallScheduleUseCase(schedule: schedule); - await getScheduledAutoRollCalls(); - } - - Future _disableSchedule(AutoRollCallSchedule schedule) async { - await _disableAutoRollCallScheduleUseCase(schedule: schedule); - await getScheduledAutoRollCalls(); - } - - Future getScheduledAutoRollCalls() async { - await suspendInteractionsTransaction(transaction: () async { - schedules.clear(); - - final zUserInfo = _getUserInfo(); - if (zUserInfo == null) { - return; - } - - final mySchedules = await _getMyAutoRollCallScheduleUseCase(userId: zUserInfo.id); - schedules.insertAll(0, mySchedules); - }); - } - - Future loadZCourses() async { - await suspendInteractionsTransaction(transaction: () async { - final zUserInfo = _getUserInfo(); - if (zUserInfo == null) { - return; - } - - final fetchedCourses = await _getCourseListUseCase(userInfo: zUserInfo); - - if (fetchedCourses != null) { - courses - ..clear() - ..addAll(fetchedCourses.skipWhile((course) => course.isSpecialCourse)); - } - }); - } - - Future submitNewSchedule({ - required ZCourse course, - required Week weekday, - required TimeOfDayPeriod period, - }) async { - await suspendInteractionsTransaction(transaction: () async { - final zUserInfo = _getUserInfo(); - - if (zUserInfo == null) { - return; - } - - final newScheduleTimeRange = AutoRollCallScheduleTimeRange( - period: period, - selectedWeekDay: weekday, - ); - - final schedule = AutoRollCallSchedule( - id: _generateId(), - enabled: true, - targetCourse: course, - targetUser: zUserInfo, - timeRange: newScheduleTimeRange, - ); - - await _addAutoRollCallScheduleUseCase(schedule: schedule); - - return getScheduledAutoRollCalls(); - }); - } - - Future updateSchedule({required bool newStatus, required AutoRollCallSchedule schedule}) => - suspendInteractionsTransaction( - transaction: () => (newStatus ? _enableSchedule(schedule) : _disableSchedule(schedule)), - ); - - Future removeSchedule({required String monitorId}) async { - await suspendInteractionsTransaction(transaction: () async { - await _cancelAutoRollCallScheduleUseCase(scheduleId: monitorId); - return getScheduledAutoRollCalls(); - }); - } - - Future makeRollCall({required ZCourse course}) async => suspendInteractionsTransaction(transaction: () async { - final zUserInfo = _getUserInfo(); - if (zUserInfo == null) { - return false; - } - - final currentRollCall = await _getRollCallUseCase(course: course, userInfo: zUserInfo); - if (currentRollCall == null) { - // TODO: show the message which indicates the reason why this roll-call is null. - return false; - } - - return _makeRollCallUseCase( - userInfo: zUserInfo, - course: course, - rollCall: currentRollCall, - ); - }); -} diff --git a/lib/src/repositories/auto_roll_call_schedule_repository_impl.dart b/lib/src/repositories/auto_roll_call_schedule_repository_impl.dart deleted file mode 100644 index 324c78a2..00000000 --- a/lib/src/repositories/auto_roll_call_schedule_repository_impl.dart +++ /dev/null @@ -1,140 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_app/debug/log/log.dart'; -import 'package:flutter_app/src/controllers/zuvio_auth_controller.dart'; -import 'package:tat_core/core/auto_roll_call/data/auto_roll_call_schedule_repository.dart'; -import 'package:tat_core/core/auto_roll_call/domain/auto_roll_call_schedule.dart'; - -const String _kAutoRollCallUsersCollectionName = 'auto-roll-call-users'; -const String _kSchedulesFieldName = 'schedules'; -const String _kUserInfoFieldName = 'userInfo'; -const String _kUserDeviceTokensFieldName = 'userDeviceTokens'; - -@immutable -class AutoRollCallScheduleRepositoryImpl implements AutoRollCallScheduleRepository { - AutoRollCallScheduleRepositoryImpl({ - required FirebaseAuth firebaseAuth, - required FirebaseMessaging firebaseMessaging, - }) : assert(firebaseAuth.currentUser != null), - _firebaseAuth = firebaseAuth, - _firebaseMessaging = firebaseMessaging; - - final FirebaseAuth _firebaseAuth; - final FirebaseMessaging _firebaseMessaging; - final CollectionReference> _scheduleCollectionRef = - FirebaseFirestore.instance.collection(_kAutoRollCallUsersCollectionName); - late final CollectionReference> _userScheduleCollectionRef; - - bool _checkUserSignedIn() { - if (_firebaseAuth.currentUser == null) { - // TODO: implement the error handling. - Log.e('User not sign in!'); - return false; - } - - return true; - } - - // TODO: call this method when user sign in, if the auto roll call feature is enabled. - Future createUserDocumentIfNotExists() async { - final isSignedIn = _checkUserSignedIn(); - if (!isSignedIn) return; - - final user = _firebaseAuth.currentUser!; - final userDoc = _scheduleCollectionRef.doc(user.uid); - final userDocSnapshot = await userDoc.get(); - - _userScheduleCollectionRef = userDoc.collection(_kSchedulesFieldName); - - if (!userDocSnapshot.exists) { - // create schedules collection for this user. - await _userScheduleCollectionRef.doc().set(const {}); - - // clean up the schedules collection, - // this is because the `doc().set({})` will create a document with an empty map. - await _cleanUpUserScheduleCollection(); - } - } - - // FIXME: Don't call this method in each storage access method (should we?). - Future _updateZUserInfo() async { - final isSignedIn = _checkUserSignedIn(); - if (!isSignedIn) return; - - final user = _firebaseAuth.currentUser!; - final zUserInfo = ZAuthController.to.currentZUserInfo(); - - assert(zUserInfo != null, 'ZUserInfo is null!'); - - if (zUserInfo != null) { - final userDoc = _scheduleCollectionRef.doc(user.uid); - final existingUserInfo = (await userDoc.get()).data(); - - // FIXME: Don't use `List`. - final existingUserDeviceTokens = existingUserInfo?.containsKey(_kUserDeviceTokensFieldName) == true - ? existingUserInfo![_kUserDeviceTokensFieldName] as List - : const []; - - final currentUserDeviceToken = await _firebaseMessaging.getToken(); - final newUserDeviceTokens = {...existingUserDeviceTokens}; - - if (currentUserDeviceToken != null) { - newUserDeviceTokens.add(currentUserDeviceToken); - } - - await userDoc.set({ - _kUserDeviceTokensFieldName: newUserDeviceTokens.toList(), - _kUserInfoFieldName: zUserInfo.toJson(), - }); - } - } - - // clean up the user schedule collection. - Future _cleanUpUserScheduleCollection() async { - final userScheduleDocs = await _userScheduleCollectionRef.get(); - for (final doc in userScheduleDocs.docs) { - await doc.reference.delete(); - } - } - - @override - Future addSchedule(AutoRollCallSchedule schedule) async { - _updateZUserInfo(); - return _userScheduleCollectionRef.doc(schedule.id).set(schedule.toJson()); - } - - @override - Future deleteSchedule(String scheduleId) { - _updateZUserInfo(); - return _userScheduleCollectionRef.doc(scheduleId).delete(); - } - - @override - Future> getAllSchedules(String userId) async { - _updateZUserInfo(); - final userScheduleSnapshot = await _userScheduleCollectionRef.get(); - final schedules = userScheduleSnapshot.docs.map((scheduleMap) { - return AutoRollCallSchedule.fromJson(scheduleMap.data()); - }).toList(); - return schedules; - } - - @override - Future getSchedule(String scheduleId) async { - _updateZUserInfo(); - final scheduleSnapshot = await _userScheduleCollectionRef.doc(scheduleId).get(); - if (!scheduleSnapshot.exists) { - return null; - } - - return AutoRollCallSchedule.fromJson(scheduleSnapshot.data()!); - } - - @override - Future updateSchedule(AutoRollCallSchedule schedule) { - _updateZUserInfo(); - return _userScheduleCollectionRef.doc(schedule.id).update(schedule.toJson()); - } -} diff --git a/lib/src/store/local_storage.dart b/lib/src/store/local_storage.dart index 400230a2..8cafd3bf 100644 --- a/lib/src/store/local_storage.dart +++ b/lib/src/store/local_storage.dart @@ -12,7 +12,6 @@ import 'package:flutter_app/src/model/userdata/user_data_json.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:tat_core/tat_core.dart'; import '../model/course/course_class_json.dart'; @@ -34,8 +33,6 @@ class LocalStorage { final _courseSemesterJsonKey = "CourseSemesterListJson"; final _scoreCreditJsonKey = "ScoreCreditJsonKey"; final _settingJsonKey = "SettingJsonKey"; - final _zUserInfoKey = 'ZUserInfoKey'; - final _zLoginCredentialKey = 'ZLoginCredentialKey'; final _firstRun = {}; final _courseTableList = []; @@ -220,21 +217,6 @@ class LocalStorage { return _saveAnnouncementSetting(); } - Future saveZuvioLoginCredential(ZLoginCredential credential) => - _writeString(_zLoginCredentialKey, jsonEncode(credential.toJson())); - - Future saveZuvioUserInfo(ZUserInfo userInfo) => _writeString(_zUserInfoKey, jsonEncode(userInfo.toJson())); - - ZLoginCredential getZuvioLoginCredential() { - final savedData = _readString(_zLoginCredentialKey); - return (savedData == null) ? null : ZLoginCredential.fromJson(jsonDecode(savedData) as Map); - } - - ZUserInfo getZuvioUserInfo() { - final savedData = _readString(_zUserInfoKey); - return (savedData == null) ? null : ZUserInfo.fromJson(jsonDecode(savedData) as Map); - } - void clearSemesterJsonList() => _courseSemesterList?.clear(); void _loadSemesterJsonList() { diff --git a/lib/src/util/remote_config_util.dart b/lib/src/util/remote_config_util.dart index bb319a9f..4a7ba320 100644 --- a/lib/src/util/remote_config_util.dart +++ b/lib/src/util/remote_config_util.dart @@ -8,7 +8,6 @@ class RemoteConfigUtil { static final _remoteConfig = FirebaseRemoteConfig.instance; static const _versionConfigKey = "version_config"; - static const _featureToggleZuvioRollCallKey = "enable_zuvio_roll_call_feature"; static const _androidIncognitoSetupGuidePageUrlKey = "android_incognito_setup_guide_page_url"; static Future getVersionConfig() async { @@ -17,11 +16,6 @@ class RemoteConfigUtil { return RemoteConfigVersionInfo.fromJson(json.decode(result)); } - static Future getFeatureToggleZuvioRollCallFlag() async { - await _remoteConfig.fetchAndActivate(); - return _remoteConfig.getBool(_featureToggleZuvioRollCallKey); - } - static Future getAndroidIncognitoSetupGuidePageUrl() async { await _remoteConfig.fetchAndActivate(); return _remoteConfig.getString(_androidIncognitoSetupGuidePageUrlKey); diff --git a/lib/tat_app.dart b/lib/tat_app.dart index 5a40e4aa..01729dfd 100644 --- a/lib/tat_app.dart +++ b/lib/tat_app.dart @@ -4,7 +4,6 @@ import 'package:bot_toast/bot_toast.dart'; import 'package:cookie_jar/cookie_jar.dart'; import 'package:dio_cookie_manager/dio_cookie_manager.dart'; import 'package:firebase_auth/firebase_auth.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_app/debug/log/log.dart'; @@ -14,11 +13,8 @@ import 'package:flutter_app/src/config/app_themes.dart'; import 'package:flutter_app/src/connector/blocked_cookies.dart'; import 'package:flutter_app/src/connector/interceptors/request_interceptor.dart'; import 'package:flutter_app/src/controllers/calendar_controller.dart'; -import 'package:flutter_app/src/controllers/zuvio_auth_controller.dart'; -import 'package:flutter_app/src/controllers/zuvio_auto_roll_call_schedule_controller.dart'; import 'package:flutter_app/src/providers/app_provider.dart'; import 'package:flutter_app/src/providers/category_provider.dart'; -import 'package:flutter_app/src/repositories/auto_roll_call_schedule_repository_impl.dart'; import 'package:flutter_app/src/store/local_storage.dart'; import 'package:flutter_app/src/util/analytics_utils.dart'; import 'package:flutter_app/ui/pages/webview/web_view_page.dart'; @@ -30,29 +26,14 @@ import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; import 'package:tat_core/core/api/interceptors/response_cookie_filter.dart'; import 'package:tat_core/core/api/school_api_service.dart'; -import 'package:tat_core/core/api/zuvio_api_service.dart'; -import 'package:tat_core/core/auto_roll_call/usecase/add_auto_roll_call_schedule_use_case.dart'; -import 'package:tat_core/core/auto_roll_call/usecase/cancel_auto_roll_call_schedule_use_case.dart'; -import 'package:tat_core/core/auto_roll_call/usecase/disable_auto_roll_call_schedule_use_case.dart'; -import 'package:tat_core/core/auto_roll_call/usecase/enable_auto_roll_call_schedule_use_case.dart'; -import 'package:tat_core/core/auto_roll_call/usecase/get_my_auto_roll_call_schedule_use_case.dart'; import 'package:tat_core/core/portal/data/check_session_repository.dart'; import 'package:tat_core/core/portal/data/simple_login_repository.dart'; import 'package:tat_core/core/portal/usecase/check_session_use_case.dart'; import 'package:tat_core/core/portal/usecase/simple_login_use_case.dart'; -import 'package:tat_core/core/zuvio/data/get_roll_call_repository.dart'; -import 'package:tat_core/core/zuvio/data/login_repository.dart'; -import 'package:tat_core/core/zuvio/data/make_roll_call_repository.dart'; -import 'package:tat_core/core/zuvio/data/student_course_list_repository.dart'; -import 'package:tat_core/core/zuvio/usecase/get_roll_call_use_case.dart'; -import 'package:tat_core/core/zuvio/usecase/get_student_course_list_use_case.dart'; -import 'package:tat_core/core/zuvio/usecase/login_use_case.dart'; -import 'package:tat_core/core/zuvio/usecase/make_roll_call_use_case.dart'; typedef _FutureVoidCallBack = Future Function(); Future runTATApp() async { - final firebaseMessaging = FirebaseMessaging.instance; final firebaseAuth = FirebaseAuth.instance; await firebaseAuth.signInAnonymously(); @@ -67,60 +48,14 @@ Future runTATApp() async { ]; final schoolApiService = SchoolApiService(interceptors: apiInterceptors); - final zuvioApiService = ZuvioApiService(); - final zuvioLoginRepository = ZLoginRepository(apiService: zuvioApiService); - final zStudentCourseListRepository = ZStudentCourseListRepository(apiService: zuvioApiService); - final zGetRollCallRepository = ZGetRollCallRepository(apiService: zuvioApiService); - final zMakeRollCallRepository = ZMakeRollCallRepository(apiService: zuvioApiService); final simpleLoginRepository = SimpleLoginRepository(apiService: schoolApiService); final checkSessionRepository = CheckSessionRepository(apiService: schoolApiService); - final zuvioLoginUseCase = ZLoginUseCase(zuvioLoginRepository); - final zuvioGetCourseListUseCase = ZGetStudentCourseListUseCase(zStudentCourseListRepository); - final zuvioGetRollCallUseCase = ZGetRollCallUseCase(zGetRollCallRepository); - final zuvioMakeRollCallUseCase = ZMakeRollCallUseCase( - zGetRollCallRepository, - zMakeRollCallRepository, - ); final simpleLoginUseCase = SimpleLoginUseCase(simpleLoginRepository); - final autoRollCallScheduleRepository = AutoRollCallScheduleRepositoryImpl( - firebaseAuth: firebaseAuth, - firebaseMessaging: firebaseMessaging, - ); - final addAutoRollCallScheduleUseCase = AddAutoRollCallScheduleUseCase( - autoRollCallScheduleRepository, - ); - final cancelAutoRollCallScheduleUseCase = CancelAutoRollCallScheduleUseCase( - autoRollCallScheduleRepository, - ); - final getMyAutoRollCallScheduleUseCase = GetMyAutoRollCallScheduleUseCase( - autoRollCallScheduleRepository, - ); - final enableAutoRollCallScheduleUseCase = EnableAutoRollCallScheduleUseCase( - autoRollCallScheduleRepository, - ); - final disableAutoRollCallScheduleUseCase = DisableAutoRollCallScheduleUseCase( - autoRollCallScheduleRepository, - ); final checkSessionIsAliveUseCase = CheckSessionUseCase(checkSessionRepository); - final zAuthController = ZAuthController( - isLoginBtnEnabled: true, - isInputBoxesEnabled: true, - loginUseCase: zuvioLoginUseCase, - ); - final zRollCallMonitorController = ZAutoRollCallScheduleController( - getRollCallUseCase: zuvioGetRollCallUseCase, - makeRollCallUseCase: zuvioMakeRollCallUseCase, - getCourseListUseCase: zuvioGetCourseListUseCase, - addAutoRollCallScheduleUseCase: addAutoRollCallScheduleUseCase, - getMyAutoRollCallScheduleUseCase: getMyAutoRollCallScheduleUseCase, - cancelAutoRollCallScheduleUseCase: cancelAutoRollCallScheduleUseCase, - enableAutoRollCallScheduleUseCase: enableAutoRollCallScheduleUseCase, - disableAutoRollCallScheduleUseCase: disableAutoRollCallScheduleUseCase, - ); final calendarController = CalendarController(); const webViewPage = WebViewPage(); @@ -131,8 +66,6 @@ Future runTATApp() async { } Get.put(webViewPage); - Get.put(zAuthController); - Get.put(zRollCallMonitorController); Get.put(simpleLoginUseCase); Get.put(cookieJar); Get.put(calendarController); diff --git a/lib/ui/other/route_utils.dart b/lib/ui/other/route_utils.dart index a7bba903..934599b5 100644 --- a/lib/ui/other/route_utils.dart +++ b/lib/ui/other/route_utils.dart @@ -3,8 +3,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_app/src/connector/core/dio_connector.dart'; -import 'package:flutter_app/src/controllers/zuvio_auth_controller.dart'; -import 'package:flutter_app/src/controllers/zuvio_auto_roll_call_schedule_controller.dart'; import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; import 'package:flutter_app/ui/pages/coursedetail/course_detail_page.dart'; import 'package:flutter_app/ui/pages/coursedetail/screen/ischoolplus/iplus_announcement_detail_page.dart'; @@ -16,8 +14,6 @@ import 'package:flutter_app/ui/pages/other/page/dev_page.dart'; import 'package:flutter_app/ui/pages/other/page/privacy_policy_page.dart'; import 'package:flutter_app/ui/pages/other/page/setting_page.dart'; import 'package:flutter_app/ui/pages/other/page/sub_system_page.dart'; -import 'package:flutter_app/ui/pages/roll_call_remind/roll_call_dashboard_page.dart'; -import 'package:flutter_app/ui/pages/roll_call_remind/zuvio_login_page.dart'; import 'package:flutter_app/ui/pages/videoplayer/class_video_player.dart'; import 'package:flutter_app/ui/pages/webview/web_view_page.dart'; import 'package:flutter_app/ui/screen/login_screen.dart'; @@ -133,33 +129,4 @@ class RouteUtils { () => ClassVideoPlayer(url, courseInfo, name), transition: transition, ); - - static Future? launchRollCallDashBoardPageAfterLogin() => (!isLoggedIntoZuvio()) - ? launchZuvioLoginPage(loginSuccessAction: () => launchRollCallDashBoardPage()) - : launchRollCallDashBoardPage(); - - static Future? launchRollCallDashBoardPage() { - ZAutoRollCallScheduleController.to.getScheduledAutoRollCalls(); - return Get.to( - () => const RollCallDashboardPage(), - transition: transition, - preventDuplicates: true, - ); - } - - static Future? launchZuvioLoginPage({ - LoginSuccessAction? loginSuccessAction, - }) => - Get.to( - () => ZuvioLoginPage( - onLoginSuccess: () { - Get.back(); - loginSuccessAction?.call(); - }, - onPageClose: () => Get.back(), - ), - transition: transition, - ); - - static bool isLoggedIntoZuvio() => ZAuthController.to.isLoggedIntoZuvio(); } diff --git a/lib/ui/pages/other/other_page.dart b/lib/ui/pages/other/other_page.dart index 07a44e6c..1ec63c76 100644 --- a/lib/ui/pages/other/other_page.dart +++ b/lib/ui/pages/other/other_page.dart @@ -3,10 +3,10 @@ import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:eva_icons_flutter/eva_icons_flutter.dart'; -import 'package:firebase_analytics/firebase_analytics.dart'; +// import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_app/debug/log/log.dart'; -import 'package:flutter_app/src/config/app_config.dart'; +// import 'package:flutter_app/src/config/app_config.dart'; import 'package:flutter_app/src/config/app_link.dart'; import 'package:flutter_app/src/connector/ntut_connector.dart'; import 'package:flutter_app/src/file/file_store.dart'; @@ -31,7 +31,6 @@ enum OnListViewPress { about, login, subSystem, - rollCallRemind, } class OtherPage extends StatefulWidget { @@ -57,12 +56,6 @@ class _OtherPageState extends State { "title": R.current.informationSystem, "onPress": OnListViewPress.subSystem }, - { - "icon": Icons.access_alarm, - "color": Colors.red, - "title": R.current.rollCallRemind, - "onPress": OnListViewPress.rollCallRemind, - }, { "icon": EvaIcons.downloadOutline, "color": Colors.yellow[700], @@ -144,28 +137,6 @@ class _OtherPageState extends State { initialUrl: link ?? AppLink.feedbackBaseUrl, title: R.current.feedback, ); - break; - case OnListViewPress.rollCallRemind: - // TODO(TU): update this log to the real feature log. - await FirebaseAnalytics.instance.logEvent( - name: 'z_roll_call_pre_msg_clicked', - parameters: { - 'position': 'other_page', - }, - ); - - if (await AppConfig.zuvioRollCallFeatureEnabled) { - RouteUtils.launchRollCallDashBoardPageAfterLogin(); - } else { - MsgDialog(MsgDialogParameter( - desc: R.current.zuvioAutoRollCallFeatureReleaseNotice, - title: R.current.comingSoon, - dialogType: DialogType.info, - removeCancelButton: true, - okButtonText: R.current.sure, - )).show(); - } - break; default: MyToast.show(R.current.noFunction); diff --git a/lib/ui/pages/roll_call_remind/horizontal_side_container.dart b/lib/ui/pages/roll_call_remind/horizontal_side_container.dart deleted file mode 100644 index 6a63e6fa..00000000 --- a/lib/ui/pages/roll_call_remind/horizontal_side_container.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; - -class HorizontalSideContainer extends StatelessWidget { - const HorizontalSideContainer({ - super.key, - required Size size, - required double ratio, - required Color color, - Radius leftRadius = Radius.zero, - Radius rightRadius = Radius.zero, - EdgeInsetsGeometry? padding, - Widget? content, - }) : _size = size, - _ratio = ratio, - _color = color, - _leftRadius = leftRadius, - _rightRadius = rightRadius, - _padding = padding, - _child = content; - - final Size _size; - final double _ratio; - final Color _color; - final Radius _leftRadius; - final Radius _rightRadius; - final EdgeInsetsGeometry? _padding; - final Widget? _child; - - @override - Widget build(BuildContext context) { - final borderRadius = BorderRadius.horizontal( - left: _leftRadius, - right: _rightRadius, - ); - return Material( - borderRadius: borderRadius, - elevation: 6, - color: Colors.transparent, - child: Container( - width: _size.width * _ratio, - padding: _padding, - decoration: BoxDecoration( - borderRadius: borderRadius, - color: _color, - ), - child: RepaintBoundary( - child: _child, - ), - ), - ); - } -} diff --git a/lib/ui/pages/roll_call_remind/new_roll_call_monitor_card_widget.dart b/lib/ui/pages/roll_call_remind/new_roll_call_monitor_card_widget.dart deleted file mode 100644 index 3bc788fd..00000000 --- a/lib/ui/pages/roll_call_remind/new_roll_call_monitor_card_widget.dart +++ /dev/null @@ -1,318 +0,0 @@ -// ignore_for_file: import_of_legacy_library_into_null_safe - -import 'dart:async'; - -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:awesome_dialog/awesome_dialog.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_app/src/r.dart'; -import 'package:flutter_app/ui/other/msg_dialog.dart'; -import 'package:flutter_app/ui/pages/roll_call_remind/horizontal_side_container.dart'; -import 'package:get/get.dart'; -import 'package:tat_core/tat_core.dart'; -import 'package:weekday_selector/weekday_selector.dart'; - -/// A type of the function which handles the add roll-call monitor event. -typedef OnAddMonitorPressed = FutureOr Function(Week weekday, TimeOfDayPeriod period); - -class NewRollCallMonitorCard extends StatefulWidget { - const NewRollCallMonitorCard({ - super.key, - required bool isDarkMode, - required String courseName, - required String teacherName, - required String semesterName, - required OnAddMonitorPressed onAddMonitorPressed, - }) : _isDarkMode = isDarkMode, - _courseName = courseName, - _teacherName = teacherName, - _semesterName = semesterName, - _onAddMonitorPressed = onAddMonitorPressed; - - final bool _isDarkMode; - final String _courseName; - final String _teacherName; - final String _semesterName; - final OnAddMonitorPressed _onAddMonitorPressed; - - @override - State createState() => _NewRollCallMonitorCardState(); -} - -class _NewRollCallMonitorCardState extends State { - final _monitoringStartTime = ValueNotifier(null); - final _monitoringEndTime = ValueNotifier(null); - final _selectedWeekdays = ValueNotifier(List.filled(7, false)); - - String _toTimeTextFrom(TimeOfDay? time) { - String addLeadingZeroIfNeeded(int value) => value < 10 ? '0$value' : value.toString(); - return time == null ? ' --:-- ' : '${addLeadingZeroIfNeeded(time.hour)}:${addLeadingZeroIfNeeded(time.minute)}'; - } - - bool _verifyCardInfo() { - final hasStartTime = _monitoringStartTime.value != null; - if (!hasStartTime) { - MsgDialog(MsgDialogParameter( - dialogType: DialogType.warning, - title: R.current.missingRequiredInformation, - desc: R.current.pleaseSelectStartTime, - okButtonText: R.current.sure, - removeCancelButton: true, - )).show(); - return false; - } - - final hasEndTime = _monitoringEndTime.value != null; - if (!hasEndTime) { - MsgDialog(MsgDialogParameter( - dialogType: DialogType.warning, - title: R.current.missingRequiredInformation, - desc: R.current.pleaseSelectEndTime, - okButtonText: R.current.sure, - removeCancelButton: true, - )).show(); - return false; - } - - final hasWeekDay = _selectedWeekdays.value.any((selection) => selection); - if (!hasWeekDay) { - MsgDialog(MsgDialogParameter( - dialogType: DialogType.warning, - title: R.current.missingRequiredInformation, - desc: R.current.pleaseSelectWeekday, - okButtonText: R.current.sure, - removeCancelButton: true, - )).show(); - return false; - } - - int timeInMinuteOf(TimeOfDay? time) => time == null ? 0 : time.hour * 60 + time.minute; - - if (timeInMinuteOf(_monitoringStartTime.value) > timeInMinuteOf(_monitoringEndTime.value)) { - MsgDialog(MsgDialogParameter( - dialogType: DialogType.warning, - title: R.current.incorrectInformationEntered, - desc: R.current.endTimeMustBeAfterStartTime, - okButtonText: R.current.sure, - removeCancelButton: true, - )).show(); - return false; - } - - return true; - } - - void _onAddMonitorButtonPressed() async { - final isCardValid = _verifyCardInfo(); - if (!isCardValid) { - return; - } - - final weekday = Week.values[_selectedWeekdays.value.indexOf(true)]; - final startTime = _monitoringStartTime.value ?? TimeOfDay.now(); - final endTime = _monitoringEndTime.value ?? TimeOfDay.now(); - - await widget._onAddMonitorPressed(weekday, TimeOfDayPeriod(startTime, endTime)); - Get.back(); - } - - Widget _buildLeftSection() => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FittedBox( - child: Text( - widget._courseName, - style: const TextStyle( - color: Colors.white, - fontSize: 30, - fontWeight: FontWeight.bold, - ), - ), - ), - const SizedBox(height: 10), - FittedBox( - child: Row( - children: [ - const Icon( - Icons.account_circle_outlined, - color: Colors.white, - ), - Text( - widget._teacherName, - style: const TextStyle( - color: Colors.white, - fontSize: 16, - ), - ), - const SizedBox(width: 20), - const Icon( - Icons.schedule, - color: Colors.white, - ), - Text( - widget._semesterName, - style: const TextStyle( - color: Colors.white, - fontSize: 16, - ), - ), - ], - ), - ), - const Spacer(), - _buildWeekDaySelector(), - ], - ); - - Widget _buildAddMonitorButton() => ElevatedButton( - onPressed: _onAddMonitorButtonPressed, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.green, - ), - child: Row( - children: [ - const Icon( - Icons.add, - size: 14, - color: Colors.white, - ), - const SizedBox(width: 6), - Text( - R.current.capitalAdd, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ); - - Widget _buildTimeSelectionButton({ - required ValueNotifier monitorTime, - required VoidCallback onPressed, - required String defaultText, - }) => - OutlinedButton( - style: OutlinedButton.styleFrom( - side: BorderSide( - width: 2, - color: widget._isDarkMode ? Colors.white : Colors.black54, - ), - ), - onPressed: onPressed, - child: Row( - children: [ - Icon( - Icons.access_time_outlined, - size: 14, - color: widget._isDarkMode ? Colors.white : Colors.black, - ), - const SizedBox(width: 2), - Expanded( - child: ValueListenableBuilder( - valueListenable: monitorTime, - builder: (_, time, __) => AutoSizeText( - time == null ? defaultText : _toTimeTextFrom(time), - textAlign: TextAlign.center, - minFontSize: 6, - wrapWords: false, - style: TextStyle( - color: widget._isDarkMode - ? (time == null ? Colors.white : Colors.yellowAccent) - : (time == null ? Colors.black : Colors.purple), - fontWeight: FontWeight.w100, - ), - ), - ), - ), - ], - ), - ); - - Widget _buildRightSection(BuildContext context) => SizedBox.expand( - child: ConstrainedBox( - constraints: const BoxConstraints.tightFor(width: 60), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _buildTimeSelectionButton( - monitorTime: _monitoringStartTime, - defaultText: R.current.begin, - onPressed: () async { - _monitoringStartTime.value = await showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ); - }, - ), - _buildTimeSelectionButton( - monitorTime: _monitoringEndTime, - defaultText: R.current.end, - onPressed: () async { - _monitoringEndTime.value = await showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ); - }, - ), - _buildAddMonitorButton(), - ], - ), - ), - ); - - Widget _buildWeekDaySelector() => ValueListenableBuilder>( - valueListenable: _selectedWeekdays, - builder: (_, selectedWeekdays, __) { - const border = Border( - top: BorderSide(width: 6.0, color: Colors.black12), - left: BorderSide(width: 6.0, color: Colors.black12), - right: BorderSide(width: 6.0, color: Colors.black26), - bottom: BorderSide(width: 6.0, color: Colors.black26), - ); - return WeekdaySelector( - shape: border, - selectedShape: border, - selectedFillColor: Colors.red, - values: selectedWeekdays, - onChanged: (day) => _selectedWeekdays.value = List.filled(7, false, growable: false)..[day % 7] = true, - ); - }, - ); - - @override - Widget build(BuildContext context) { - final size = MediaQuery.of(context).size; - return FittedBox( - child: SizedBox( - height: size.height * 0.25, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - HorizontalSideContainer( - size: size, - ratio: 0.7, - color: widget._isDarkMode ? const Color(0xFF205375) : const Color(0xFF2155CD), - leftRadius: const Radius.circular(15), - padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16), - content: _buildLeftSection(), - ), - HorizontalSideContainer( - size: size, - ratio: 0.3, - color: widget._isDarkMode ? const Color(0xFF112B3C) : const Color(0xFFA2E7F7), - rightRadius: const Radius.circular(15), - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14), - content: _buildRightSection(context), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/ui/pages/roll_call_remind/roll_call_bottom_sheet.dart b/lib/ui/pages/roll_call_remind/roll_call_bottom_sheet.dart deleted file mode 100644 index 84fb2714..00000000 --- a/lib/ui/pages/roll_call_remind/roll_call_bottom_sheet.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_app/src/controllers/zuvio_auto_roll_call_schedule_controller.dart'; -import 'package:flutter_app/ui/pages/roll_call_remind/new_roll_call_monitor_card_widget.dart'; -import 'package:get/get.dart'; - -class RollCallBottomSheet extends StatelessWidget { - const RollCallBottomSheet({super.key}); - - Widget _buildCloseSheetButton( - BuildContext context, { - VoidCallback? onTab, - }) => - GestureDetector( - onTap: onTab, - child: Container( - padding: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.1), - shape: BoxShape.circle, - ), - child: Icon( - Icons.close, - size: 24, - color: Get.isDarkMode ? Theme.of(context).primaryColorLight : Theme.of(context).primaryColor, - ), - ), - ); - - Widget get _courseCardList { - final isDarkMode = Get.isDarkMode; - return GetBuilder( - builder: (controller) => ListView.builder( - itemCount: controller.courses.length, - itemBuilder: (_, index) { - final course = controller.courses[index]; - return NewRollCallMonitorCard( - key: ValueKey(course.hashCode + index), - courseName: course.name, - teacherName: course.teacherName, - semesterName: course.semesterName, - isDarkMode: isDarkMode, - onAddMonitorPressed: (weekday, period) => controller.submitNewSchedule( - course: course, - period: period, - weekday: weekday, - ), - ); - }, - ), - ); - } - - @override - Widget build(BuildContext context) => Stack( - children: [ - Positioned( - right: 12, - top: 12, - child: _buildCloseSheetButton( - context, - onTab: () => Get.back(), - ), - ), - Container( - alignment: Alignment.bottomCenter, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox( - width: 0, - height: 32, - ), - Expanded( - child: Container( - margin: const EdgeInsets.only(top: 10), - padding: const EdgeInsets.fromLTRB(20, 12, 20, 0), - child: _courseCardList, - ), - ), - ], - ), - ), - ], - ); -} diff --git a/lib/ui/pages/roll_call_remind/roll_call_dashboard_page.dart b/lib/ui/pages/roll_call_remind/roll_call_dashboard_page.dart deleted file mode 100644 index c62636c4..00000000 --- a/lib/ui/pages/roll_call_remind/roll_call_dashboard_page.dart +++ /dev/null @@ -1,84 +0,0 @@ -// ignore_for_file: import_of_legacy_library_into_null_safe - -import 'package:flutter/material.dart'; -import 'package:flutter_app/src/controllers/zuvio_auto_roll_call_schedule_controller.dart'; -import 'package:flutter_app/src/r.dart'; -import 'package:flutter_app/ui/pages/roll_call_remind/roll_call_bottom_sheet.dart'; -import 'package:flutter_app/ui/pages/roll_call_remind/scheduled_roll_call_monitor_card_widget.dart'; -import 'package:get/get.dart'; -import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; - -class RollCallDashboardPage extends StatelessWidget { - const RollCallDashboardPage({super.key}); - - PreferredSizeWidget get _appBar => AppBar( - title: Row( - children: [ - const Icon(Icons.access_alarm), - Padding( - padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 12), - child: Text(R.current.rollCallRemind), - ), - ], - ), - ); - - Widget _buildAddNewButton( - BuildContext context, { - VoidCallback? onPressed, - }) => - FloatingActionButton( - onPressed: onPressed, - backgroundColor: Get.isDarkMode ? const Color(0xFF2155CD) : const Color(0xFFFF9F29), - child: const Icon(Icons.add), - ); - - void _onAddNewButtonPressed(BuildContext context) { - ZAutoRollCallScheduleController.to.loadZCourses(); - showCupertinoModalBottomSheet( - context: context, - builder: (context) => const RollCallBottomSheet(), - ); - } - - Widget get _scheduledMonitorList => GetBuilder( - builder: (controller) => ListView.builder( - // TODO: hide the floating button when scrolling down the list. - padding: const EdgeInsets.only(bottom: 72), - itemCount: controller.schedules.length, - itemBuilder: (_, index) { - final schedule = controller.schedules[index]; - return ScheduledRollCallMonitorCard( - period: schedule.timeRange.period, - courseName: schedule.targetCourse.name, - selectedWeekDay: schedule.timeRange.selectedWeekDay, - isMonitorEnabled: schedule.enabled, - onRemoveMonitorPressed: () => controller.removeSchedule(monitorId: schedule.id), - onRollCallPressed: () => controller.makeRollCall(course: schedule.targetCourse), - onActivationStatusSwitchPressed: (enabled) => controller.updateSchedule( - newStatus: enabled, - schedule: schedule, - ), - ); - }, - ), - ); - - @override - Widget build(BuildContext context) => Scaffold( - appBar: _appBar, - floatingActionButton: _buildAddNewButton( - context, - onPressed: () => _onAddNewButtonPressed(context), - ), - body: SafeArea( - child: MediaQuery( - data: MediaQuery.of(context).copyWith(textScaleFactor: 1), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: _scheduledMonitorList, - ), - ), - ), - ); -} diff --git a/lib/ui/pages/roll_call_remind/scheduled_roll_call_monitor_card_widget.dart b/lib/ui/pages/roll_call_remind/scheduled_roll_call_monitor_card_widget.dart deleted file mode 100644 index 543e4e39..00000000 --- a/lib/ui/pages/roll_call_remind/scheduled_roll_call_monitor_card_widget.dart +++ /dev/null @@ -1,238 +0,0 @@ -// ignore_for_file: import_of_legacy_library_into_null_safe - -import 'dart:async'; - -import 'package:awesome_dialog/awesome_dialog.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_app/src/r.dart'; -import 'package:flutter_app/ui/other/msg_dialog.dart'; -import 'package:flutter_app/ui/pages/roll_call_remind/horizontal_side_container.dart'; -import 'package:get/get.dart'; -import 'package:tat_core/tat_core.dart'; -import 'package:weekday_selector/weekday_selector.dart'; - -typedef OnRemoveMonitorPressed = FutureOr Function(); -typedef OnRollCallPressed = FutureOr Function(); -typedef OnActivationStatusSwitchPressed = FutureOr Function(bool); - -class ScheduledRollCallMonitorCard extends StatelessWidget { - // TODO(TU): the real _isDarkMode - const ScheduledRollCallMonitorCard({ - super.key, - required TimeOfDayPeriod period, - required String courseName, - required Week selectedWeekDay, - required bool isMonitorEnabled, - required OnRemoveMonitorPressed onRemoveMonitorPressed, - required OnRollCallPressed onRollCallPressed, - required OnActivationStatusSwitchPressed onActivationStatusSwitchPressed, - }) : _period = period, - _courseName = courseName, - _selectedWeekDay = selectedWeekDay, - _isMonitorEnabled = isMonitorEnabled, - _onRemoveMonitorPressed = onRemoveMonitorPressed, - _onRollCallPressed = onRollCallPressed, - _onActivationStatusSwitchPressed = onActivationStatusSwitchPressed; - - final bool _isMonitorEnabled; - final Week _selectedWeekDay; - final TimeOfDayPeriod _period; - final String _courseName; - final OnRemoveMonitorPressed _onRemoveMonitorPressed; - final OnRollCallPressed _onRollCallPressed; - final OnActivationStatusSwitchPressed _onActivationStatusSwitchPressed; - - String _toTimeTextFrom(TimeOfDay? time) { - String addLeadingZeroIfNeeded(int value) => value < 10 ? '0$value' : value.toString(); - return time == null ? ' --:-- ' : '${addLeadingZeroIfNeeded(time.hour)}:${addLeadingZeroIfNeeded(time.minute)}'; - } - - Widget _buildLeftSection() => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FittedBox( - child: Text( - '${_toTimeTextFrom(_period.startTime)} ~ ${_toTimeTextFrom(_period.endTime)}', - style: const TextStyle( - color: Colors.white, - fontSize: 30, - fontWeight: FontWeight.bold, - ), - ), - ), - const SizedBox(height: 10), - FittedBox( - child: Row( - children: [ - const Icon( - Icons.account_balance, - color: Colors.white, - ), - Text( - _courseName, - style: const TextStyle( - color: Colors.white, - fontSize: 16, - ), - ), - ], - ), - ), - const Spacer(), - _buildWeekDaySelector(), - ], - ); - - Widget _buildWeekDaySelector() { - const border = Border( - top: BorderSide(width: 6.0, color: Colors.black12), - left: BorderSide(width: 6.0, color: Colors.black12), - right: BorderSide(width: 6.0, color: Colors.black26), - bottom: BorderSide(width: 6.0, color: Colors.black26), - ); - return WeekdaySelector( - shape: border, - selectedShape: border, - selectedFillColor: Colors.red, - values: List.generate(7, (index) => index == _selectedWeekDay.index, growable: false), - onChanged: (_) {}, - ); - } - - Widget _buildRightSection() => Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text(_isMonitorEnabled ? 'ON' : 'Off'), - Switch( - value: _isMonitorEnabled, - onChanged: _onActivationStatusSwitchPressed, - ), - ], - ), - _buildRollCallButton(), - _buildRemoveMonitorButton(), - ], - ); - - Widget _buildRemoveMonitorButton() => ElevatedButton( - onPressed: () { - MsgDialog(MsgDialogParameter( - dialogType: DialogType.question, - title: '', - desc: 'Do you really want to remove this scheduled roll-call remind?', - okButtonText: R.current.sure, - onOkButtonClicked: () => _onRemoveMonitorPressed.call(), - )).show(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.deepOrange, - ), - child: FittedBox( - child: Row( - children: const [ - Icon( - Icons.cancel_outlined, - size: 14, - color: Colors.white, - ), - SizedBox(width: 6), - Text( - // TODO(TU): replace text with `R.current.xxx` - 'Remove', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ); - - Widget _buildRollCallButton() => ElevatedButton( - onPressed: () async { - final isSuccess = await _onRollCallPressed(); - if (isSuccess) { - MsgDialog(MsgDialogParameter( - dialogType: DialogType.success, - title: 'RollCall Success', - desc: 'You have roll-called to $_courseName.', - okButtonText: R.current.sure, - removeCancelButton: true, - )).show(); - } else { - MsgDialog(MsgDialogParameter( - dialogType: DialogType.error, - title: 'RollCall Failed', - desc: 'Failed to roll-called to $_courseName, please try again.', - okButtonText: R.current.sure, - removeCancelButton: true, - )).show(); - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - ), - child: FittedBox( - child: Row( - children: const [ - Icon( - Icons.games, - size: 14, - color: Colors.black, - ), - SizedBox(width: 6), - Text( - // TODO(TU): replace text with `R.current.xxx` - 'RollCall', - style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ); - - @override - Widget build(BuildContext context) { - final size = MediaQuery.of(context).size; - final isDarkMode = Get.isDarkMode; - return FittedBox( - child: SizedBox( - height: size.height * 0.25, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - HorizontalSideContainer( - size: size, - ratio: 0.7, - color: _isMonitorEnabled - ? (isDarkMode ? const Color(0xFFFF9F29) : const Color(0xFF1A4D2E)) - : const Color(0xFFAAAAAA), - leftRadius: const Radius.circular(15), - padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16), - content: _buildLeftSection(), - ), - HorizontalSideContainer( - size: size, - ratio: 0.3, - color: isDarkMode ? const Color(0xFF1A4D2E) : const Color(0xFFA2E7F7), - rightRadius: const Radius.circular(15), - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14), - content: _buildRightSection(), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/ui/pages/roll_call_remind/zuvio_login_page.dart b/lib/ui/pages/roll_call_remind/zuvio_login_page.dart deleted file mode 100644 index dbe6a883..00000000 --- a/lib/ui/pages/roll_call_remind/zuvio_login_page.dart +++ /dev/null @@ -1,307 +0,0 @@ -// ignore_for_file: import_of_legacy_library_into_null_safe - -import 'dart:math'; - -import 'package:email_validator/email_validator.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_app/src/controllers/zuvio_auth_controller.dart'; -import 'package:flutter_app/src/r.dart'; -import 'package:get/get.dart'; - -/// A function to be invoked after logged in successfully. -typedef LoginSuccessAction = void Function(); - -/// A function to be invoked after login canceled. -typedef CancelLoginAction = void Function(); - -/// A function to called when content of a [TextFormField] or [TextField] changed. -typedef TextFieldValidator = String? Function(String?); - -class ZuvioLoginPage extends StatefulWidget { - const ZuvioLoginPage({ - super.key, - LoginSuccessAction? onLoginSuccess, - CancelLoginAction? onPageClose, - }) : _onPageClose = onPageClose, - _onLoginSuccess = onLoginSuccess; - - final LoginSuccessAction? _onLoginSuccess; - final CancelLoginAction? _onPageClose; - - @override - State createState() => _ZuvioLoginPageState(); -} - -class _ZuvioLoginPageState extends State { - late final TextEditingController _userNameInputBoxController; - late final TextEditingController _passwordInputBoxController; - - final _closeButtonSize = 24.0; - final _loginFormKey = GlobalKey(); - final _showPassword = ValueNotifier(false); - - @override - void initState() { - super.initState(); - _userNameInputBoxController = TextEditingController(); - _passwordInputBoxController = TextEditingController(); - } - - @override - void dispose() { - _userNameInputBoxController.dispose(); - _passwordInputBoxController.dispose(); - super.dispose(); - } - - void _onLoginPressed() { - final isValidate = _loginFormKey.currentState?.validate() ?? false; - if (!isValidate) { - return; - } - - final username = _userNameInputBoxController.text.trim(); - final password = _passwordInputBoxController.text; - - _showPassword.value = false; - - ZAuthController.to.login(username, password); - } - - void _handleLoginCallBack() { - if (ZAuthController.to.isLoggedIntoZuvio()) { - widget._onLoginSuccess?.call(); - } - } - - String? _emailValidator(String? rawData) { - if (rawData == null || rawData.isEmpty) { - return R.current.accountNull; - } - - final isValid = EmailValidator.validate(rawData.trim()); - return isValid ? null : R.current.error; - } - - String? _passwordValidator(String? rawData) => (rawData == null || rawData.isEmpty) ? R.current.passwordNull : null; - - Widget _buildLoginButton({required bool enabled}) => Padding( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 0), - child: ElevatedButton.icon( - onPressed: enabled ? _onLoginPressed : null, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFFF6363), - disabledForegroundColor: Colors.black12.withOpacity(0.38), - disabledBackgroundColor: Colors.black12.withOpacity(0.12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(18.0), - ), - ), - icon: const Icon(Icons.login, color: Colors.white), - label: Text( - R.current.login, - style: const TextStyle(color: Colors.white), - ), - ), - ); - - Widget get _boxTitle => Padding( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 0), - child: Text( - '${R.current.login} Zuvio', - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, - color: Colors.black54, - ), - ), - ); - - BoxDecoration get _boxDecoration => BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(10)), - color: const Color(0xFFFAF5E4), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.2), - spreadRadius: 5, - blurRadius: 7, - offset: const Offset(0, 3), - ), - ], - ); - - Widget _buildEmailTextField({required bool enabled}) => _InputBox( - controller: _userNameInputBoxController, - keyboardType: TextInputType.emailAddress, - icon: Icons.account_circle_rounded, - hintText: R.current.account, - enabled: enabled, - validator: _emailValidator, - ); - - Widget _buildPasswordTextField({required bool enabled}) => ValueListenableBuilder( - valueListenable: _showPassword, - builder: (context, showPassword, _) => _InputBox( - controller: _passwordInputBoxController, - keyboardType: TextInputType.visiblePassword, - icon: Icons.lock, - hintText: R.current.password, - obscure: !showPassword, - enabled: enabled, - validator: _passwordValidator, - suffixIcon: IconButton( - icon: Icon( - showPassword ? Icons.visibility : Icons.visibility_off, - color: Theme.of(context).primaryColorDark, - ), - onPressed: () => _showPassword.value = !showPassword, - ), - ), - ); - - Widget get _loginBox => Padding( - padding: const EdgeInsets.all(18), - child: Container( - decoration: _boxDecoration, - child: Padding( - padding: const EdgeInsets.all(20), - child: LayoutBuilder( - builder: (context, constraints) => SingleChildScrollView( - child: MediaQuery( - data: MediaQuery.of(context).copyWith(textScaleFactor: 1), - child: ConstrainedBox( - constraints: BoxConstraints.tightForFinite( - /// To avoid overlapping with the close button, - /// we have to set the height not to exceed the height of the full screen minus the height of the close button, - /// since the close button is placed at the top left. - /// The size of the close button is its icon size (`_closeButtonSize`) plus the size of the inner edge of the button, - /// and the default size of the inner edge is 12 per side. - /// Please refer to [IconButton] for more details. - /// And we also have to plus the edge size of the first padding widget of the _loginBox, - /// So the final height of the _loginBox should be `max - (_closeButtonSize + closeButtonTotalHeight + first Padding height)`. - height: min(330, constraints.maxHeight - (_closeButtonSize + 24 + 18)), - ), - child: GetBuilder( - builder: (controller) { - // Since the [didChangeDependencies] of the [GetBuilderState] won't be called, - // we need to put the logics needs to be done after [build] finished here. - WidgetsBinding.instance.addPostFrameCallback((_) => _handleLoginCallBack()); - - return Wrap( - runAlignment: WrapAlignment.center, - children: [ - Form( - key: _loginFormKey, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _boxTitle, - _buildEmailTextField(enabled: controller.isInputBoxesEnabled), - _buildPasswordTextField(enabled: controller.isInputBoxesEnabled), - _buildLoginButton(enabled: controller.isLoginBtnEnabled), - ], - ), - ), - ], - ); - }, - ), - ), - ), - ), - ), - ), - ), - ); - - Widget get _closePageButton => IconButton( - icon: Icon(Icons.clear, size: _closeButtonSize), - iconSize: _closeButtonSize, - onPressed: () => widget._onPageClose?.call(), - ); - - Color get _bgColor => const Color(0xFF125B50); - - @override - Widget build(BuildContext context) => Scaffold( - body: Container( - decoration: BoxDecoration( - color: _bgColor, - ), - child: SafeArea( - child: Stack( - children: [ - Align( - alignment: Alignment.topLeft, - child: _closePageButton, - ), - Align( - alignment: Alignment.center, - child: _loginBox, - ) - ], - ), - ), - ), - ); -} - -class _InputBox extends StatelessWidget { - const _InputBox({ - required TextEditingController controller, - required TextInputType keyboardType, - required IconData icon, - required String hintText, - bool obscure = false, - bool enabled = true, - TextFieldValidator? validator, - Widget? suffixIcon, - }) : _controller = controller, - _keyboardType = keyboardType, - _icon = icon, - _hintText = hintText, - _obscure = obscure, - _enabled = enabled, - _validator = validator, - _suffixIcon = suffixIcon; - - final TextEditingController _controller; - final TextInputType _keyboardType; - final IconData _icon; - final String _hintText; - final bool _obscure; - final bool _enabled; - final TextFieldValidator? _validator; - final Widget? _suffixIcon; - - OutlineInputBorder _buildBorder({ - required Color color, - }) => - OutlineInputBorder( - borderSide: BorderSide(width: 3, color: color), - borderRadius: BorderRadius.circular(15), - ); - - @override - Widget build(BuildContext context) => Padding( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 0), - child: TextFormField( - controller: _controller, - keyboardType: _keyboardType, - obscureText: _obscure, - enabled: _enabled, - validator: _validator, - decoration: InputDecoration( - fillColor: const Color(0xFFEAE5D2), - filled: true, - icon: Icon(_icon, color: Colors.black54), - hintText: _hintText, - hintStyle: const TextStyle(color: Colors.black26), - border: _buildBorder(color: Colors.transparent), - focusedBorder: _buildBorder(color: Colors.black54), - suffixIcon: _suffixIcon, - ), - style: const TextStyle(color: Colors.black87), - ), - ); -}