diff --git a/lib/app_provider.dart b/lib/app_provider.dart index 79f9554cd..ccde7e926 100644 --- a/lib/app_provider.dart +++ b/lib/app_provider.dart @@ -164,7 +164,7 @@ List dependentServices = [ if (userDataProvider.isLoggedIn && (userDataProvider.userProfileModel!.classifications?.student ?? false)) { - cardsDataProvider.activateAuthenticatedCards(); + cardsDataProvider.activateStudentCards(); } else { cardsDataProvider.deactivateStudentCards(); } @@ -173,7 +173,7 @@ List dependentServices = [ if (userDataProvider.isLoggedIn && (userDataProvider.userProfileModel!.classifications?.staff ?? false)) { - cardsDataProvider.activateAuthenticatedCards(); + cardsDataProvider.activateStaffCards(); } else { cardsDataProvider.deactivateStaffCards(); } diff --git a/lib/core/models/user_profile.dart b/lib/core/models/user_profile.dart index caf8fc8ba..1d55802fa 100644 --- a/lib/core/models/user_profile.dart +++ b/lib/core/models/user_profile.dart @@ -38,8 +38,6 @@ class UserProfileModel extends HiveObject { List? surveyCompletion; @HiveField(7) List? selectedVentilationLocations; - @HiveField(8) - Map? cardMappings; UserProfileModel( {this.classifications, @@ -54,8 +52,7 @@ class UserProfileModel extends HiveObject { this.selectedParkingLots, this.selectedStops, this.surveyCompletion, - this.selectedVentilationLocations, - this.cardMappings}); + this.selectedVentilationLocations}); factory UserProfileModel.fromJson(Map json) => UserProfileModel( @@ -97,10 +94,6 @@ class UserProfileModel extends HiveObject { ? [] : List.from( json["selectedVentilationLocations"].map((x) => x)), - cardMappings: json["cardMappings"] == null - ? Map() - : Map.from(json["cardMappings"] - .map((x, y) => MapEntry(x, y))), ); Map toJson() => { @@ -136,10 +129,6 @@ class UserProfileModel extends HiveObject { "selectedVentilationLocations": selectedVentilationLocations == null ? null : List.from(selectedVentilationLocations!.map((x) => x)), - "cardMappings": cardMappings == null - ? null - : Map.from( - cardMappings!.map((x, y) => MapEntry(x, y))), }; } diff --git a/lib/core/providers/cards.dart b/lib/core/providers/cards.dart index dbed60595..88daae370 100644 --- a/lib/core/providers/cards.dart +++ b/lib/core/providers/cards.dart @@ -154,6 +154,23 @@ class CardsDataProvider extends ChangeNotifier { await _loadCardStates(); } + /// Update the [_cardOrder] stored in state + /// overwrite the [_cardOrder] in persistent storage with the model passed in + Future updateCardOrder(List? newOrder) async { + if (_userDataProvider == null || _userDataProvider!.isInSilentLogin) { + return; + } + try { + await _cardOrderBox.put(DataPersistence.cardOrder, newOrder); + } catch (e) { + _cardOrderBox = await Hive.openBox(DataPersistence.cardOrder); + await _cardOrderBox.put(DataPersistence.cardOrder, newOrder); + } + _cardOrder = newOrder; + _lastUpdated = DateTime.now(); + notifyListeners(); + } + /// Load [_cardOrder] from persistent storage /// Will create persistent storage if no data is found Future _loadCardOrder() async { @@ -186,138 +203,6 @@ class CardsDataProvider extends ChangeNotifier { notifyListeners(); } - /// Returns the account type (visitor, staff, or student) as a string. - String _accountType() { - // set bool vars to user values if they exist, else they will be set to false - bool isStudent = - _userDataProvider?.userProfileModel?.classifications?.student ?? false; - bool isStaff = - _userDataProvider?.userProfileModel?.classifications?.staff ?? false; - String accountType; - - // check which type of account the user is currently - if (isStudent) { - accountType = 'student'; - } else if (isStaff) { - accountType = 'staff'; - } else { - accountType = 'visitor'; - } - - return accountType; - } - - /// Checks if the account has cardOrder and cardStates. If not then it - /// calls createDefault to create default ones. - /// Returns true when both exist, false otherwise. - /// NOTE: Function should only get called when the user is logged in. - bool checkIfExist() { - try { - // define authenticated variables - String account = _accountType(); - List accountCards = - _userDataProvider!.userProfileModel!.classifications!.student! - ? _studentCards - : _staffCards; - - // check if a cardMapping exists for the account type - if (_userDataProvider!.userProfileModel!.cardMappings![account] != null) { - // cardMapping, cardOrder, and cardStates exist, so exit the function - if (_userDataProvider!.userProfileModel!.cardMappings![account] - ['cardOrder'] != - null && - _userDataProvider!.userProfileModel!.cardMappings![account] - ['cardStates'] != - null) { - return true; - } - } - - // create a cardMapping for the account - else { - _userDataProvider!.userProfileModel!.cardMappings![account] = - new Map(); - } - - // create default cardOrder and cardStates - _createDefault(account, accountCards); - - return false; - } catch (e) { - // in case function is somehow called when use is a visitor - print("Providers/Cards/checkIfExist Error: $e"); - } - - return true; - } - - /// Creates default, authenticated _cardOrder and _cardStates. - _createDefault(String account, List accountCards) { - // insert authenticated cards to the start of the list - int index = _cardOrder!.indexOf('MyStudentChart') + 1; - _cardOrder!.insertAll(index, accountCards); - - // TODO: test w/o this - _cardOrder = List.from(_cardOrder!.toSet().toList()); - - // set all card's state to true - for (String card in accountCards) { - _cardStates![card] = true; - } - - updateCardOrder(_cardOrder); - updateCardStates( - _cardStates!.keys.where((card) => _cardStates![card]!).toList()); - } - - /// Call [updateCardOrder()] and updates [userDataProvider] - updateProfileAndCardOrder(List? newOrder) async { - updateCardOrder(newOrder); - - // if the account is not a visitor, update user profile as well - String account = _accountType(); - if (account != 'visitor') { - _userDataProvider!.userProfileModel!.cardMappings![account]['cardOrder'] = - newOrder; - await _userDataProvider! - .postUserProfile(_userDataProvider!.userProfileModel); - } - } - - /// Update the [_cardOrder] stored in state - /// overwrite the [_cardOrder] in persistent storage with the model passed in - Future updateCardOrder(List? newOrder) async { - if (_userDataProvider == null || _userDataProvider!.isInSilentLogin) { - return; - } - try { - await _cardOrderBox.put(DataPersistence.cardOrder, newOrder); - } catch (e) { - _cardOrderBox = await Hive.openBox(DataPersistence.cardOrder); - await _cardOrderBox.put(DataPersistence.cardOrder, newOrder); - } - _cardOrder = newOrder; - _lastUpdated = DateTime.now(); - notifyListeners(); - } - - /// Toggles [_cardStates] and updates [userDataProvider] - void toggleCard(String card) async { - // update the card's visibility and cardStates - _cardStates![card] = !_cardStates![card]!; - updateCardStates( - _cardStates!.keys.where((card) => _cardStates![card]!).toList()); - - // if the account is not a visitor, update user profile as well - String account = _accountType(); - if (account != 'visitor') { - _userDataProvider!.userProfileModel!.cardMappings![account] - ['cardStates'] = _cardStates; - await _userDataProvider! - .postUserProfile(_userDataProvider!.userProfileModel); - } - } - /// Update the [_cardStates] stored in state /// overwrite the [_cardStates] in persistent storage with the model passed in Future updateCardStates(List activeCards) async { @@ -337,27 +222,33 @@ class CardsDataProvider extends ChangeNotifier { notifyListeners(); } - /// Updates [_cardOrder] and [_cardStates] to remove and turn off all - /// student cards - deactivateStudentCards() { - // remove and turn off all authenticated cards - for (String card in _studentCards) { - _cardOrder!.remove(card); + _deactivateAllCards() { + for (String card in _cardStates!.keys) { _cardStates![card] = false; } + } + + activateStudentCards() { + int index = _cardOrder!.indexOf('MyStudentChart') + 1; + _cardOrder!.insertAll(index, _studentCards.toList()); + + // TODO: test w/o this + _cardOrder = List.from(_cardOrder!.toSet().toList()); updateCardOrder(_cardOrder); updateCardStates( _cardStates!.keys.where((card) => _cardStates![card]!).toList()); } - /// Updates [_cardOrder] and [_cardStates] to remove and turn off all - /// staff cards - deactivateStaffCards() { - // remove and turn off all authenticated cards - for (String card in _staffCards) { - _cardOrder!.remove(card); - _cardStates![card] = false; + showAllStudentCards() { + int index = _cardOrder!.indexOf('MyStudentChart') + 1; + _cardOrder!.insertAll(index, _studentCards.toList()); + + // TODO: test w/o this + _cardOrder = List.from(_cardOrder!.toSet().toList()); + + for (String card in _studentCards) { + _cardStates![card] = true; } updateCardOrder(_cardOrder); @@ -365,29 +256,19 @@ class CardsDataProvider extends ChangeNotifier { _cardStates!.keys.where((card) => _cardStates![card]!).toList()); } - /// Update [_cardStates] to all false - _deactivateAllCards() { - for (String card in _cardStates!.keys) { + deactivateStudentCards() { + for (String card in _studentCards) { + _cardOrder!.remove(card); _cardStates![card] = false; } + updateCardOrder(_cardOrder); + updateCardStates( + _cardStates!.keys.where((card) => _cardStates![card]!).toList()); } - /// Update [_cardOrder] to include authenticated cards - activateAuthenticatedCards() { - // define authenticated variables - List accountCards = - _userDataProvider!.userProfileModel!.classifications!.student! - ? _studentCards - : _staffCards; - - // do nothing if an account card already exists in the list - for (String card in accountCards) { - if (_cardOrder!.contains(card)) return; - } - - // account cards do not exist in the list, so add them in + activateStaffCards() { int index = _cardOrder!.indexOf('MyStudentChart') + 1; - _cardOrder!.insertAll(index, accountCards.toList()); + _cardOrder!.insertAll(index, _staffCards.toList()); // TODO: test w/o this _cardOrder = List.from(_cardOrder!.toSet().toList()); @@ -396,50 +277,45 @@ class CardsDataProvider extends ChangeNotifier { _cardStates!.keys.where((card) => _cardStates![card]!).toList()); } - /// If user is logged in, fetch cardStates and cardOrder from user profile; - /// however, if those do not exist, create and upload default order and states. - showAllAuthenticatedCards() async { - // grab account type - String account = _accountType(); + showAllStaffCards() { + int index = _cardOrder!.indexOf('MyStudentChart') + 1; + _cardOrder!.insertAll(index, _staffCards.toList()); - // leave function if the account is a visitor - if (account == 'visitor') { - return; - } + // TODO: test w/o this + _cardOrder = List.from(_cardOrder!.toSet().toList()); - // created a default cardOrder and cardStates - if (!checkIfExist()) { - // give the user the default cardOrder and cardStates - _userDataProvider!.userProfileModel!.cardMappings![account]['cardOrder'] = - _cardOrder; - _userDataProvider!.userProfileModel!.cardMappings![account] - ['cardStates'] = _cardStates; - - // update user profile - await _userDataProvider! - .postUserProfile(_userDataProvider!.userProfileModel); + for (String card in _staffCards) { + _cardStates![card] = true; } + updateCardOrder(_cardOrder); + updateCardStates( + _cardStates!.keys.where((card) => _cardStates![card]!).toList()); + } - // load in cardOrder and cardStates from the user profile - else { - _cardOrder = _userDataProvider! - .userProfileModel!.cardMappings![account]['cardOrder'] - .cast(); - _cardStates = _userDataProvider! - .userProfileModel!.cardMappings![account]['cardStates'] - .cast(); + deactivateStaffCards() { + for (String card in _staffCards) { + _cardOrder!.remove(card); + _cardStates![card] = false; } - - // update app preferences updateCardOrder(_cardOrder); updateCardStates( _cardStates!.keys.where((card) => _cardStates![card]!).toList()); } - /// SIMPLE SETTERS + void reorderCards(List order) { + _cardOrder = order; + notifyListeners(); + } + + void toggleCard(String card) { + _cardStates![card] = !_cardStates![card]!; + updateCardStates( + _cardStates!.keys.where((card) => _cardStates![card]!).toList()); + } + set userDataProvider(UserDataProvider value) => _userDataProvider = value; - /// SIMPLE GETTERS + ///SIMPLE GETTERS bool? get isLoading => _isLoading; bool? get noInternet => _noInternet; String? get error => _error; diff --git a/lib/core/providers/user.dart b/lib/core/providers/user.dart index 0f96f8522..44d37193e 100644 --- a/lib/core/providers/user.dart +++ b/lib/core/providers/user.dart @@ -6,6 +6,7 @@ import 'package:campus_mobile_experimental/core/models/user_profile.dart'; import 'package:campus_mobile_experimental/core/providers/cards.dart'; import 'package:campus_mobile_experimental/core/providers/notifications.dart'; import 'package:campus_mobile_experimental/core/services/authentication.dart'; +import 'package:campus_mobile_experimental/core/services/notifications.dart'; import 'package:campus_mobile_experimental/core/services/user.dart'; import 'package:encrypt/encrypt.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; @@ -189,7 +190,11 @@ class UserDataProvider extends ChangeNotifier { _encryptAndSaveCredentials(username, password); if (await silentLogin()) { - _cardsDataProvider!.showAllAuthenticatedCards(); + if (_userProfileModel!.classifications!.student!) { + _cardsDataProvider!.showAllStudentCards(); + } else if (_userProfileModel!.classifications!.staff!) { + _cardsDataProvider!.showAllStaffCards(); + } _isLoading = false; notifyListeners(); return true; @@ -322,8 +327,7 @@ class UserDataProvider extends ChangeNotifier { newModel.username = await getUsernameFromDevice(); newModel.ucsdaffiliation = _authenticationModel!.ucsdaffiliation; newModel.pid = _authenticationModel!.pid; - List castSubscriptions = - newModel.subscribedTopics!.cast(); + List castSubscriptions = newModel.subscribedTopics!.cast(); newModel.subscribedTopics = castSubscriptions.toSet().toList(); print('UserDataProvider:fetchUserProfile:newModel'); print(newModel.toJson()); @@ -344,8 +348,7 @@ class UserDataProvider extends ChangeNotifier { Classifications.fromJson({'student': false, 'staff': false}); } await updateUserProfileModel(newModel); - _pushNotificationDataProvider - .subscribeToTopics(newModel.subscribedTopics!.cast()); + _pushNotificationDataProvider.subscribeToTopics(newModel.subscribedTopics!.cast()); } } else { _error = _userProfileService.error; diff --git a/lib/ui/profile/cards.dart b/lib/ui/profile/cards.dart index e6992a88b..2eb29a229 100644 --- a/lib/ui/profile/cards.dart +++ b/lib/ui/profile/cards.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:campus_mobile_experimental/core/providers/cards.dart'; import 'package:campus_mobile_experimental/ui/common/container_view.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; @@ -10,8 +12,6 @@ class CardsView extends StatefulWidget { class _CardsViewState extends State { CardsDataProvider? _cardsDataProvider; - List cardsToRemove = ["NativeScanner"]; - List hiddenCards = ["NativeScanner", "ventilation", "student_survey"]; @override Widget build(BuildContext context) { @@ -27,62 +27,50 @@ class _CardsViewState extends State { onReorder: _onReorder, ); - if (_cardsDataProvider!.noInternet!) { - Future.delayed( - Duration.zero, - () => { - showDialog( - context: context, - builder: (BuildContext ctx) => AlertDialog( - title: const Text('No Internet'), - content: const Text( - 'Cards requires an internet connection.'), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context, 'Ok'), - child: const Text('Ok'), - ), - ])) - }); + if( _cardsDataProvider!.noInternet! ) { + Future.delayed(Duration.zero, () => { + showDialog(context: context, builder: (BuildContext ctx) => AlertDialog( + title: const Text('No Internet'), + content: const Text('Cards requires an internet connection.'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, 'Ok'), + child: const Text('Ok'), + ), + ] + )) + }); } return tempView; } void _onReorder(int oldIndex, int newIndex) { - // ? if (newIndex > oldIndex) { newIndex -= 1; } - - // declare variables - List cardsOrder = _cardsDataProvider!.cardOrder!; - List addBack = []; - - // remove all unwanted cards from the cards list - for (String card in cardsToRemove) { - // if the card was removed, add it back at the end - if (cardsOrder.remove(card)) { - addBack.add(card); - } + List newOrder = _cardsDataProvider!.cardOrder!; + List toRemove = []; + if (_cardsDataProvider!.cardOrder!.contains('NativeScanner')) { + toRemove.add('NativeScanner'); } - // change the position for the moved card - String movedCard = cardsOrder.removeAt(oldIndex); - cardsOrder.insert(newIndex, movedCard); - - // add back all unwanted cards to the end of the list - cardsOrder.addAll(addBack.toList()); - - // update card order - _cardsDataProvider!.updateProfileAndCardOrder(cardsOrder); + newOrder.removeWhere((element) => toRemove.contains(element)); + String item = newOrder.removeAt(oldIndex); + newOrder.insert(newIndex, item); + List orderList = []; + for (String item in newOrder) { + orderList.add(item); + } + orderList.addAll(toRemove.toList()); + _cardsDataProvider!.updateCardOrder(orderList); setState(() {}); } List createList(BuildContext context) { List list = []; for (String card in _cardsDataProvider!.cardOrder!) { - if (hiddenCards.contains(card)) continue; + if (card == 'NativeScanner') continue; try { list.add(ListTile( leading: Icon(Icons.reorder), @@ -96,11 +84,14 @@ class _CardsViewState extends State { activeColor: Theme.of(context).buttonColor, ), )); - } catch (e) { + } + catch (e) { FirebaseCrashlytics.instance.log('error getting $card in profile'); FirebaseCrashlytics.instance.recordError( e, StackTrace.fromString(e.toString()), - reason: "Profile/Cards: Failed to load Cards page", fatal: false); + reason: "Profile/Cards: Failed to load Cards page", + fatal: false + ); _cardsDataProvider!.changeInternetStatus(true); }