diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..f02f3293e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,43 @@ +# EditorConfig helps maintain consistent coding styles +# for multiple developers working on the same project +# across various editors and IDEs. +# https://editorconfig.org + +root = true + +# All files +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +# Dart files +[*.dart] +indent_style = space +indent_size = 2 +max_line_length = 120 + +# YAML files +[*.{yaml,yml}] +indent_style = space +indent_size = 2 + +# JSON files +[*.json] +indent_style = space +indent_size = 2 + +# Markdown files +[*.md] +trim_trailing_whitespace = false + +# Gradle files +[*.gradle] +indent_style = space +indent_size = 4 + +# Properties files +[*.properties] +indent_style = space +indent_size = 4 \ No newline at end of file diff --git a/.github/workflows/pre_commit_check.yml b/.github/workflows/pre_commit_check.yml new file mode 100644 index 000000000..15afcf728 --- /dev/null +++ b/.github/workflows/pre_commit_check.yml @@ -0,0 +1,172 @@ +name: Pre-Commit Checks + +on: + pull_request: + branches: ['**'] + +permissions: + contents: write + pull-requests: write + +jobs: + precommit: + runs-on: ubuntu-latest + outputs: + violations_found: ${{ steps.check.outputs.violations_found }} + steps: + # 1 - Checkout code + - name: Setup workspace + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.head_ref }} + + # 2 - Set up Python (pre-commit requires it) + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + # 3 - Install our pre-commit + - name: Install pre-commit + run: | + python -m pip install --upgrade pip + pip install pre-commit + + # 4 - Run pre-commit. If the script exits with a non-zero code, the GitHub Action will fail the PR. + - name: Run pre-commit hooks + run: pre-commit run --all-files + + - name: Check code style violations + id: check + run: | + chmod +x ./scripts/auto_check_all.sh + violations_file="/tmp/style_violations.txt" + export VIOLATIONS_OUTPUT="$violations_file" + + if ./scripts/auto_check_all.sh; then + echo "violations_found=false" >> $GITHUB_OUTPUT + else + echo "violations_found=true" >> $GITHUB_OUTPUT + echo "violations_file=$violations_file" >> $GITHUB_OUTPUT + + if [[ ! -f "$violations_file" || ! -s "$violations_file" ]]; then + echo "VIOLATIONS_DETAILS_START" > "$violations_file" + echo "Code style violations detected but details unavailable" >> "$violations_file" + echo "VIOLATIONS_DETAILS_END" >> "$violations_file" + fi + fi + + - name: Comment on PR + if: github.event_name == 'pull_request' && steps.check.outputs.violations_found == 'true' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + + try { + const violationsFound = '${{ steps.check.outputs.violations_found }}' === 'true'; + // Create violations comment if violations were found + if (violationsFound) { + let violationsComment = '## Code Style Violations Found\n\n'; + + // Try to parse violation details from the file + const violationsFile = '${{ steps.check.outputs.violations_file }}'; + let detailsSection = ''; + let totalCategories = ''; + let totalViolations = ''; + + if (fs.existsSync(violationsFile)) { + try { + const content = fs.readFileSync(violationsFile, 'utf8'); + const detailsMatch = content.match(/VIOLATIONS_DETAILS_START\n([\s\S]*?)\nVIOLATIONS_DETAILS_END/); + + if (detailsMatch) { + const details = detailsMatch[1]; + const lines = details.split('\n').filter(line => line.trim()); + const failedChecks = []; + + lines.forEach(line => { + if (line.includes('Total Categories Failed:')) { + totalCategories = line.split(': ')[1]; + } else if (line.includes('Total Individual Violations:')) { + totalViolations = line.split(': ')[1]; + } else if (line.includes('FAILED') && line.includes('violations')) { + // Parse lines like "- Static Constants (UPPER_SNAKE_CASE): FAILED (91 violations)" + const match = line.match(/- ([^:]+): FAILED \((\d+) violations\)/); + if (match) { + failedChecks.push('- **' + match[1] + '**: ' + match[2] + ' violations'); + } + } + }); + + if (totalCategories && totalViolations && failedChecks.length > 0) { + detailsSection = failedChecks.join('\n'); + } + } + } catch (error) { + console.log('Could not parse violation details:', error.message); + } + } + + // If we couldn't parse details, use a generic message + if (!detailsSection) { + detailsSection = 'Based on the automated check, violations were found in multiple categories.'; + } + + violationsComment += detailsSection + '\n\n' + + '**Total Violations**: ' + (totalViolations || 'Unknown') + '\n' + + '**Total Categories Failed**: ' + (totalCategories || 'Unknown') + '\n\n' + + '## Here are the formatting rules to follow:\n\n' + + '- **Static Constants** have to use `UPPER_SNAKE_CASE` (e.g., `MAX_RETRY_COUNT`)\n' + + '- **Classes** have to use `UpperCamelCase` (e.g., `UserProfile`)\n' + + '- **Variables/Functions** have to use `lowerCamelCase` (e.g., `userName`)\n' + + '- **Files/Directories** have to use `snake_case` (e.g., `user_profile.dart`)\n' + + '- **Imports** have to use full package paths (e.g., `package:app_name/path/file.dart`)\n' + + '- **If Conditions** have to split long/complex conditions into variables for readability\n\n' + + '### Example of a long if condition violation:\n' + + '```dart\n' + + 'if (userDataProvider.isLoggedIn &&\n' + + ' (userDataProvider.userProfileModel.classifications?.staff ?? false)) {\n' + + ' // action\n' + + '}\n' + + '```\n\n' + + '```dart\n' + + '// GOOD: split condition into variables for readability\n' + + 'var isLoggedIn = userDataProvider.isLoggedIn;\n' + + 'var isStaff = userDataProvider.userProfileModel.classifications?.staff ?? false;\n' + + 'if (isLoggedIn && isStaff) {\n' + + ' // action\n' + + '}\n' + + '```\n\n' + + '## To see more details and fix these violations:\n\n' + + '1. Run `./scripts/auto_check_all.sh` locally.\n' + + '2. Apply the suggested corrections.\n' + + '3. Re-run the script to verify fixes.\n' + + '4. Commit your changes.\n\n' + + '---\n' + + '*This comment will continue to appear until all violations are resolved.*'; + + await github.rest.issues.createComment({ + owner, repo, issue_number, body: violationsComment + }); + console.log('Violations comment added successfully'); + } + } catch (error) { + console.error('Error creating PR comment:', error.message); + + try { + await github.rest.issues.createComment({ + owner, repo, issue_number, + body: '## Code Style Check Error\n\n' + + 'There was an error processing the code style check results.\n\n' + + '**Error**: ' + error.message + '\n\n' + + 'Please run the scripts locally to see the specific issues.' + }); + } catch (fallbackError) { + console.error('Failed to create fallback comment:', fallbackError.message); + } + } diff --git a/.gitignore b/.gitignore index d0e3ad4ef..0f6bdbb53 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ .history .svn/ *.env +*.bak # IntelliJ related *.iml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..9b51b3721 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +repos: + - repo: local + hooks: + - id: run-all-lint-scripts + name: Run all lint scripts + language: system + pass_filenames: false + always_run: true + verbose: true + entry: | + bash -c ' + echo "Running all lint scripts.." + bash scripts/auto_fix_all.sh + echo + echo "[pre-commit] All lint scripts completed." + ' + +# Run the following commands to set up pre-commit locally (one-time only setup): +# $pip install pre-commit (or $brew install pre-commit) +# $pre-commit install (or $python -m pre_commit install) diff --git a/Readme.md b/Readme.md index 55cf7d6ed..6dc0e0fe2 100644 --- a/Readme.md +++ b/Readme.md @@ -62,6 +62,18 @@ git checkout experimental git checkout -b experimental ``` +#### Using the Pre-Commit Hook +Before you start committing any changes, you can enable the repository's pre-commit hook, which will automatically run our lint/format scripts before each commit. + +To enable it: + +```shell +# Install pre-commit hook (one-time on your machine) +pip install pre-commit # or brew install pre-commit + +# Enable the repo's pre-commit hook +pre-commit install # or python -m pre_commit install +``` You are now ready to begin developing your new feature. Commit your code often, using present-tense and concise verbiage explaining the work completed. Example: Add, commit, and push your new feature: diff --git a/android/app/build.gradle b/android/app/build.gradle index 745bf8fab..003ec4c99 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -34,7 +34,7 @@ android { defaultConfig { applicationId = "edu.ucsd" - minSdk = 23 + minSdkVersion = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName diff --git a/lib/app_constants.dart b/lib/app_constants.dart index 95ebc0fb8..40340eb40 100644 --- a/lib/app_constants.dart +++ b/lib/app_constants.dart @@ -1,45 +1,43 @@ import 'package:flutter/material.dart'; class RoutePaths { - static const String Home = '/'; - static const String BottomNavigationBar = 'bottom_navigation_bar'; - static const String OnboardingInitial = 'onboarding/initial'; - static const String OnboardingLogin = 'onboarding/login'; - static const String Map = 'map/map'; - static const String MapSearch = 'map/map_search'; - static const String MapLocationList = 'map/map_location_list'; - static const String Notifications = 'notifications'; - static const String Profile = 'profile'; - static const String CardsView = 'profile/cards_view'; - static const String NotificationsFilter = 'notifications/filter'; - static const String NewsViewAll = 'news/newslist'; - static const String EventsViewAll = 'events/eventslist'; - static const String EventsAll = 'events/events_view_all'; - static const String NewsDetailView = 'news/news_detail_view'; - static const String EventDetailView = 'events/event_detail_view'; - static const String LinksViewAll = 'links/links_list'; - static const String ClassScheduleViewAll = 'class/classList'; - static const String ManageAvailabilityView = - 'availability/manage_locations_view'; - static const String ManageParkingView = 'parking/manage_parking_view'; - static const String ManageShuttleView = 'shuttle/manage_shuttle_view'; - static const String AddShuttleStopsView = 'shuttle/add_shuttle_stops_view'; - static const String DiningViewAllDiningOptions = - 'dining/view_all_dining_options'; - static const String DiningOptionDetailView = 'dining/option_detail_view'; - static const String DiningPaymentFilterView = "dining/payment_filter_view"; - // static const String DiningNutritionView = 'dining/dining_nutrition_view'; - static const String Parking = "parking/parking_view"; - static const String SpotTypesView = "parking/spot_types_view"; - static const String ParkingStructureView = "parking/parking_structure_view"; - static const String ParkingLotsView = "parking/parking_lots_view"; - static const String NeighborhoodsView = "parking/neighborhoods_view"; - static const String NeighborhoodsLotsView = "parking/neighborhoods_lot_view"; - static const String AvailabilityDetailedView = "availability/detailed_view"; + static const String HOME = '/'; + static const String BOTTOM_NAVIGATION_BAR = 'bottom_navigation_bar'; + static const String ONBOARDING_INITIAL = 'onboarding/initial'; + static const String ONBOARDING_LOGIN = 'onboarding/login'; + static const String MAP = 'map/map'; + static const String MAP_SEARCH = 'map/map_search'; + static const String MAP_LOCATION_LIST = 'map/map_location_list'; + static const String NOTIFICATIONS = 'notifications'; + static const String PROFILE = 'profile'; + static const String CARDS_VIEW = 'profile/cards_view'; + static const String NOTIFICATIONS_FILTER = 'notifications/filter'; + static const String NEWS_VIEW_ALL = 'news/newslist'; + static const String EVENTS_VIEW_ALL = 'events/eventslist'; + static const String EVENTS_ALL = 'events/events_view_all'; + static const String NEWS_DETAIL_VIEW = 'news/news_detail_view'; + static const String EVENT_DETAIL_VIEW = 'events/event_detail_view'; + static const String LINKS_VIEW_ALL = 'links/links_list'; + static const String CLASS_SCHEDULE_VIEW_ALL = 'class/classList'; + static const String MANAGE_AVAILABILITY_VIEW = 'availability/manage_locations_view'; + static const String MANAGE_PARKING_VIEW = 'parking/manage_parking_view'; + static const String MANAGE_SHUTTLE_VIEW = 'shuttle/manage_shuttle_view'; + static const String ADD_SHUTTLE_STOPS_VIEW = 'shuttle/add_shuttle_stops_view'; + static const String DINING_VIEW_ALL_DINING_OPTIONS = 'dining/view_all_dining_options'; + static const String DINING_OPTION_DETAIL_VIEW = 'dining/option_detail_view'; + static const String DINING_PAYMENT_FILTER_VIEW = "dining/payment_filter_view"; + // static const String DINING_NUTRITION_VIEW = 'dining/dining_nutrition_view'; + static const String PARKING = "parking/parking_view"; + static const String SPOT_TYPES_VIEW = "parking/spot_types_view"; + static const String PARKING_STRUCTURE_VIEW = "parking/parking_structure_view"; + static const String PARKING_LOTS_VIEW = "parking/parking_lots_view"; + static const String NEIGHBORHOODS_VIEW = "parking/neighborhoods_view"; + static const String NEIGHBORHOODS_LOTS_VIEW = "parking/neighborhoods_lot_view"; + static const String AVAILABILITY_DETAILED_VIEW = "availability/detailed_view"; } class RouteTitles { - static const titleMap = { + static const TITLE_MAP = { 'Maps': 'MAP', 'MapSearch': 'MAP', 'MapLocationList': 'MAP', @@ -70,17 +68,17 @@ class RouteTitles { } class ParkingDefaults { - static const defaultLots = [ + static const DEFAULT_LOTS = [ "Athena", "Gilman", "Hopkins", "Theatre District", ]; - static const defaultSpots = ["S", "B", "A"]; + static const DEFAULT_SPOTS = ["S", "B", "A"]; } class DiningConstants { - static const payment_filter_types = [ + static const PAYMENT_FILTER_TYPES = [ "Triton Cash", "Dining Dollars", "Apple/Google Pay", @@ -92,47 +90,44 @@ class DiningConstants { } class ButtonText { - static const ScanNowFull = 'SCAN YOUR COVID-19 KIT.'; - static const ScanNow = 'SCAN NOW'; - static const SignInFull = 'SCAN YOUR COVID-19 KIT.'; - static const SignIn = 'SIGN IN'; + static const SCAN_NOW_FULL = 'SCAN YOUR COVID-19 KIT.'; + static const SCAN_NOW = 'SCAN NOW'; + static const SIGN_IN_FULL = 'SCAN YOUR COVID-19 KIT.'; + static const SIGN_IN = 'SIGN IN'; } class ErrorConstants { - static const authorizedPostErrors = 'Failed to upload data: '; - static const authorizedPutErrors = 'Failed to update data: '; - static const invalidBearerToken = 'Invalid bearer token'; - static const notAcceptable = - 'DioError [DioErrorType.response]: Http status error [406]'; - static const duplicateRecord = - 'DioError [DioErrorType.response]: Http status error [409]'; - static const invalidMedia = - 'DioError [DioErrorType.response]: Http status error [415]'; - static const silentLoginFailed = "Silent login failed"; - static const locationFailed = "Location was not available"; + static const AUTHORIZED_POST_ERRORS = 'Failed to upload data: '; + static const AUTHORIZED_PUT_ERRORS = 'Failed to update data: '; + static const INVALID_BEARER_TOKEN = 'Invalid bearer token'; + static const NOT_ACCEPTABLE = 'DioError [DioErrorType.response]: Http status error [406]'; + static const DUPLICATE_RECORD = 'DioError [DioErrorType.response]: Http status error [409]'; + static const INVALID_MEDIA = 'DioError [DioErrorType.response]: Http status error [415]'; + static const SILENT_LOGIN_FAILED = "Silent login failed"; + static const LOCATION_FAILED = "Location was not available"; } class LoginConstants { - static const silentLoginFailedTitle = 'Oops! You\'re not logged in.'; - static const silentLoginFailedDesc = + static const SILENT_LOGIN_FAILED_TITLE = 'Oops! You\'re not logged in.'; + static const SILENT_LOGIN_FAILED_DESC = 'The system has logged you out (probably by mistake). Go to Profile to log back in.'; - static const loginFailedTitle = 'Sorry, unable to sign you in.'; - static const loginFailedDesc = + static const LOGIN_FAILED_TITLE = 'Sorry, unable to sign you in.'; + static const LOGIN_FAILED_DESC = 'Be sure you are using the correct credentials; TritonLink login if you are a student, SSO (AD or Active Directory) if you are a Faculty/Staff.'; - static const shuttleMaxTitle = 'Maximum shuttle stops reached'; - static const shuttleMaxDesc = + static const SHUTTLE_MAX_TITLE = 'Maximum shuttle stops reached'; + static const SHUTTLE_MAX_DESC = 'The maximum number of shuttle stops allowed is five. Please remove some stops to add more.'; } class ParkingConstants { - static const spotMaxTitle = 'Maximum parking spots reached'; - static const spotMaxDesc = + static const SPOT_MAX_TITLE = 'Maximum parking spots reached'; + static const SPOT_MAX_DESC = 'The maximum number of parking spots allowed is three. Please remove some spots to add more.'; - static const lotMaxTitle = 'Maximum parking lots reached'; - static const lotMaxDesc = + static const LOT_MAX_TITLE = 'Maximum parking lots reached'; + static const LOT_MAX_DESC = 'You have reached the maximum number of lots (10) that can be selected. Please deselect some lots before adding more.'; - static const Map stringToIconData = { + static const Map STRING_TO_ICON_DATA = { 'icon - e03e': Icons.accessible, 'icon - e486': Icons.group, }; @@ -140,35 +135,33 @@ class ParkingConstants { class WifiConstants { // Initial State - static const wifiIssueFailedTitle = 'Could not report issue'; - static const wifiIssueFailedDesc = 'Please run speed test to report issue.'; + static const WIFI_ISSUE_FAILED_TITLE = 'Could not report issue'; + static const WIFI_ISSUE_FAILED_DESC = 'Please run speed test to report issue.'; // Finished State - static const wifiIssueSuccessTitle = 'Issue Reported'; - static const wifiIssueSuccessDesc = + static const WIFI_ISSUE_SUCCESS_TITLE = 'Issue Reported'; + static const WIFI_ISSUE_SUCCESS_DESC = 'Thank you for helping improve UCSD wireless. Your test results have been sent to IT Services.'; } class Plugins { - static const FrontCamera = 'FRONT CAMERA'; + static const FRONT_CAMERA = 'FRONT CAMERA'; } class NavigatorConstants { - static const HomeTab = 0; - static const MapTab = 1; - static const NotificationsTab = 2; - static const ProfileTab = 3; + static const HOME_TAB = 0; + static const MAP_TAB = 1; + static const NOTIFICATIONS_TAB = 2; + static const PROFILE_TAB = 3; } class NotificationsConstants { - static const statusNoMessages = 'You have no notifications.\n' + + static const STATUS_NO_MESSAGES = 'You have no notifications.\n' + 'It looks like you\'ve unsubscribed from all topics.\n\n' + 'You can re-subscribe to specific topics via the Notifications Filter.'; - static const statusFetchProblem = - 'There was a problem fetching your messages.\n\n' + - 'Please try again soon.'; - static const statusFetching = 'Loading your notifications, please wait.'; - static const statusNone = ''; - static const statusNoMoreMessages = 'No more messages.'; + static const STATUS_FETCH_PROBLEM = 'There was a problem fetching your messages.\n\n' + 'Please try again soon.'; + static const STATUS_FETCHING = 'Loading your notifications, please wait.'; + static const STATUS_NONE = ''; + static const STATUS_NO_MORE_MESSAGES = 'No more messages.'; } class MessageTypeConstants { @@ -179,17 +172,17 @@ class MessageTypeConstants { } class DataPersistence { - static const cardStates = 'cardStates'; - static const cardOrder = 'cardOrder'; - static const AuthenticationModel = 'AuthenticationModel'; - static const UserProfileModel = 'UserProfileModel'; + static const CARD_STATES = 'cardStates'; + static const CARD_ORDER = 'cardOrder'; + static const AUTHENTICATION_MODEL = 'AuthenticationModel'; + static const USER_PROFILE_MODEL = 'UserProfileModel'; } /// Maps Card IDs to Card titles class CardTitleConstants { - static const titleMap = { - 'MyStudentChart': 'MyStudentChart', - 'MyUCSDChart': 'MyUCSDChart', + static const TITLE_MAP = { + 'my_student_chart': 'MY STUDENT CHART', + 'my_ucsd_chart': 'MY UCSD CHART', 'student_id': 'STUDENT ID', 'speed_test': "TEST WIFI SPEED", 'employee_id': 'STAFF ID', @@ -205,12 +198,11 @@ class CardTitleConstants { } class CardMenuOptionConstants { - static const reloadCard = 'reload card'; - static const hideCard = 'hide card'; + static const RELOAD_CARD = 'reload card'; + static const HIDE_CARD = 'hide card'; } class ConnectivityConstants { - static const offlineAlert = - 'It appears you are currently offline. Check network status and try again.'; - static const offlineTitle = 'No Internet'; + static const OFFLINE_ALERT = 'It appears you are currently offline. Check network status and try again.'; + static const OFFLINE_TITLE = 'No Internet'; } diff --git a/lib/app_networking.dart b/lib/app_networking.dart index d86385c76..be082b042 100644 --- a/lib/app_networking.dart +++ b/lib/app_networking.dart @@ -7,8 +7,7 @@ import 'package:get/get.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; class NetworkHelper { - ///TODO: inside each service that file place a switch statement to handle all - ///TODO: different errors thrown by the Dio client DioErrorType.RESPONSE + // TODO: different errors thrown by the Dio client DioErrorType.RESPONSE - December 2025 // private constructor to show that this class should not be instantiated const NetworkHelper._(); @@ -16,8 +15,7 @@ class NetworkHelper { static const int SSO_REFRESH_MAX_RETRIES = 3; static const int SSO_REFRESH_RETRY_INCREMENT = 5000; static const int SSO_REFRESH_RETRY_MULTIPLIER = 3; - static final DEFAULT_TIMEOUT = - Duration(milliseconds: int.parse(dotenv.get('DEFAULT_TIMEOUT'))); + static final DEFAULT_TIMEOUT = Duration(milliseconds: int.parse(dotenv.get('DEFAULT_TIMEOUT'))); static Future fetchData(String url) async { Dio dio = new Dio(); @@ -30,14 +28,13 @@ class NetworkHelper { // If server returns an OK response, return the body return _response.data; } else { - ///TODO: log this as a bug because the response was bad + // TODO: log this as a bug because the response was bad - December 2025 // If that response was not OK, throw an error. throw Exception('Failed to fetch data: ' + _response.data); } } - static Future authorizedFetch( - String url, Map headers) async { + static Future authorizedFetch(String url, Map headers) async { Dio dio = new Dio(); dio.options.connectTimeout = DEFAULT_TIMEOUT; dio.options.receiveTimeout = DEFAULT_TIMEOUT; @@ -50,7 +47,7 @@ class NetworkHelper { // If server returns an OK response, return the body return _response.data; } else { - ///TODO: log this as a bug because the response was bad + // TODO: log this as a bug because the response was bad - December 2025 // If that response was not OK, throw an error. throw Exception('Failed to fetch data: ' + _response.data); @@ -59,8 +56,8 @@ class NetworkHelper { static Widget getSilentLoginDialog() { return AlertDialog( - title: const Text(LoginConstants.silentLoginFailedTitle), - content: const Text(LoginConstants.silentLoginFailedDesc), + title: const Text(LoginConstants.SILENT_LOGIN_FAILED_TITLE), + content: const Text(LoginConstants.SILENT_LOGIN_FAILED_DESC), actions: [ TextButton( style: TextButton.styleFrom( @@ -77,8 +74,7 @@ class NetworkHelper { // method for implementing exponential backoff for silentLogin // mimicking existing code from React Native versions of campus-mobile - static Future authorizedPublicPost( - String url, Map headers, dynamic body) async { + static Future authorizedPublicPost(String url, Map headers, dynamic body) async { int retries = 0; int waitTime = 0; try { @@ -109,71 +105,79 @@ class NetworkHelper { // if here, silent login has failed // throw exception to inform caller await Get.dialog(getSilentLoginDialog()); - throw new Exception(ErrorConstants.silentLoginFailed); + throw new Exception(ErrorConstants.SILENT_LOGIN_FAILED); } - static Future authorizedPost( - String url, Map? headers, dynamic body) async { + static Future authorizedPost(String url, Map? headers, dynamic body) async { Dio dio = new Dio(); dio.options.connectTimeout = DEFAULT_TIMEOUT; dio.options.receiveTimeout = DEFAULT_TIMEOUT; dio.options.headers = headers; final _response = await dio.post(url, data: body); - if (_response.statusCode == 200 || _response.statusCode == 201) { + switch (_response.statusCode) { // If server returns an OK response, return the body - return _response.data; - } else if (_response.statusCode == 400) { + case 200: + case 201: + return _response.data; // If that response was not OK, throw an error. - String message = _response.data['message'] ?? ''; - throw Exception(ErrorConstants.authorizedPostErrors + message); - } else if (_response.statusCode == 401) { - throw Exception(ErrorConstants.authorizedPostErrors + - ErrorConstants.invalidBearerToken); - } else if (_response.statusCode == 404) { - String message = _response.data['message'] ?? ''; - throw Exception(ErrorConstants.authorizedPostErrors + message); - } else if (_response.statusCode == 500) { - String message = _response.data['message'] ?? ''; - throw Exception(ErrorConstants.authorizedPostErrors + message); - } else if (_response.statusCode == 409) { - String message = _response.data['message'] ?? ''; - throw Exception(ErrorConstants.duplicateRecord + message); - } else { - throw Exception(ErrorConstants.authorizedPostErrors + 'unknown error'); + case 400: + String message = _response.data['message'] ?? ''; + throw Exception(ErrorConstants.AUTHORIZED_POST_ERRORS + message); + + case 401: + throw Exception(ErrorConstants.AUTHORIZED_POST_ERRORS + ErrorConstants.INVALID_BEARER_TOKEN); + + case 404: + String message = _response.data['message'] ?? ''; + throw Exception(ErrorConstants.AUTHORIZED_POST_ERRORS + message); + + case 409: + String message = _response.data['message'] ?? ''; + throw Exception(ErrorConstants.DUPLICATE_RECORD + message); + + case 500: + String message = _response.data['message'] ?? ''; + throw Exception(ErrorConstants.AUTHORIZED_POST_ERRORS + message); + + default: + throw Exception(ErrorConstants.AUTHORIZED_POST_ERRORS + 'unknown error'); } } - static Future authorizedPut( - String url, Map headers, dynamic body) async { + static Future authorizedPut(String url, Map headers, dynamic body) async { Dio dio = new Dio(); dio.options.connectTimeout = DEFAULT_TIMEOUT; dio.options.receiveTimeout = DEFAULT_TIMEOUT; dio.options.headers = headers; final _response = await dio.put(url, data: body); - if (_response.statusCode == 200 || _response.statusCode == 201) { + switch (_response.statusCode) { // If server returns an OK response, return the body - return _response.data; - } else if (_response.statusCode == 400) { + case 200: + case 201: + return _response.data; // If that response was not OK, throw an error. - String message = _response.data['message'] ?? ''; - throw Exception(ErrorConstants.authorizedPutErrors + message); - } else if (_response.statusCode == 401) { - throw Exception(ErrorConstants.authorizedPutErrors + - ErrorConstants.invalidBearerToken); - } else if (_response.statusCode == 404) { - String message = _response.data['message'] ?? ''; - throw Exception(ErrorConstants.authorizedPutErrors + message); - } else if (_response.statusCode == 500) { - String message = _response.data['message'] ?? ''; - throw Exception(ErrorConstants.authorizedPutErrors + message); - } else { - throw Exception(ErrorConstants.authorizedPutErrors + 'unknown error'); + case 400: + String message = _response.data['message'] ?? ''; + throw Exception(ErrorConstants.AUTHORIZED_PUT_ERRORS + message); + + case 401: + throw Exception(ErrorConstants.AUTHORIZED_PUT_ERRORS + ErrorConstants.INVALID_BEARER_TOKEN); + + case 404: + String message = _response.data['message'] ?? ''; + throw Exception(ErrorConstants.AUTHORIZED_PUT_ERRORS + message); + + case 500: + String message = _response.data['message'] ?? ''; + throw Exception(ErrorConstants.AUTHORIZED_PUT_ERRORS + message); + + default: + throw Exception(ErrorConstants.AUTHORIZED_PUT_ERRORS + 'unknown error'); } } - static Future authorizedDelete( - String url, Map headers) async { + static Future authorizedDelete(String url, Map headers) async { Dio dio = new Dio(); dio.options.connectTimeout = DEFAULT_TIMEOUT; dio.options.receiveTimeout = DEFAULT_TIMEOUT; @@ -184,7 +188,7 @@ class NetworkHelper { // If server returns an OK response, return the body return _response.data; } else { - ///TODO: log this as a bug because the response was bad + // TODO: log this as a bug because the response was bad - December 2025 // If that response was not OK, throw an error. throw Exception('Failed to delete data: ' + _response.data); } @@ -204,8 +208,7 @@ class NetworkHelper { "Authorization": dotenv.get('MOBILE_APP_PUBLIC_DATA_KEY') }; try { - var response = await authorizedPost( - tokenEndpoint, tokenHeaders, "grant_type=client_credentials"); + var response = await authorizedPost(tokenEndpoint, tokenHeaders, "grant_type=client_credentials"); headers["Authorization"] = "Bearer " + response["access_token"]; return true; } catch (e) { diff --git a/lib/app_provider.dart b/lib/app_provider.dart index 6230a2bd2..e58e625b0 100644 --- a/lib/app_provider.dart +++ b/lib/app_provider.dart @@ -31,8 +31,7 @@ List providers = [ ]; final FirebaseAnalytics analytics = FirebaseAnalytics.instance; -final FirebaseAnalyticsObserver observer = - FirebaseAnalyticsObserver(analytics: analytics); +final FirebaseAnalyticsObserver observer = FirebaseAnalyticsObserver(analytics: analytics); List independentServices = [ Provider.value(value: observer), @@ -82,8 +81,7 @@ List independentServices = [ ChangeNotifierProvider( create: (_) { print("CreateProvider: InternetConnectivityProvider"); - InternetConnectivityProvider _connectivityProvider = - InternetConnectivityProvider(); + InternetConnectivityProvider _connectivityProvider = InternetConnectivityProvider(); _connectivityProvider.monitorInternet(); return _connectivityProvider; }, @@ -113,15 +111,12 @@ List dependentServices = [ /// try to load any persistent saved data /// once loaded from memory get the user's online profile - _userDataProvider - .loadSavedData() - .whenComplete(() => _userDataProvider.fetchUserProfile()); + _userDataProvider.loadSavedData().whenComplete(() => _userDataProvider.fetchUserProfile()); return _userDataProvider; }, lazy: false, update: (_, pushNotificationDataProvider, _userDataProvider) { - _userDataProvider!.pushNotificationDataProvider = - pushNotificationDataProvider; + _userDataProvider!.pushNotificationDataProvider = pushNotificationDataProvider; return _userDataProvider; }), ChangeNotifierProxyProvider( @@ -136,22 +131,20 @@ List dependentServices = [ cardsDataProvider ..loadSavedData().then((_) { // Update available cards - cardsDataProvider.updateAvailableCards( - userDataProvider.authenticationModel.ucsdaffiliation); + cardsDataProvider.updateAvailableCards(userDataProvider.authenticationModel.ucsdaffiliation); // Student card activation - if (userDataProvider.isLoggedIn && - (userDataProvider.userProfileModel.classifications?.student ?? - false)) { + final bool isLoggedIn = userDataProvider.isLoggedIn; + final bool isStudent = userDataProvider.userProfileModel.classifications?.student ?? false; + if (isLoggedIn && isStudent) { cardsDataProvider.activateStudentCards(); } else { cardsDataProvider.deactivateStudentCards(); } // Staff card activation - if (userDataProvider.isLoggedIn && - (userDataProvider.userProfileModel.classifications?.staff ?? - false)) { + final bool isStaff = userDataProvider.userProfileModel.classifications?.staff ?? false; + if (isLoggedIn && isStaff) { cardsDataProvider.activateStaffCards(); } else { cardsDataProvider.deactivateStaffCards(); @@ -159,43 +152,39 @@ List dependentServices = [ }); return cardsDataProvider; }), - ChangeNotifierProxyProvider( - create: (_) { + ChangeNotifierProxyProvider(create: (_) { var classDataProvider = ClassScheduleDataProvider(); return classDataProvider; }, update: (_, userDataProvider, classScheduleDataProvider) { classScheduleDataProvider!.userDataProvider = userDataProvider; - if (userDataProvider.isLoggedIn && !classScheduleDataProvider.isLoading) { - classScheduleDataProvider.fetchData(); - } + final bool isLoggedIn = userDataProvider.isLoggedIn; + final bool isNotLoading = !classScheduleDataProvider.isLoading; + if (isLoggedIn && isNotLoading) classScheduleDataProvider.fetchData(); return classScheduleDataProvider; }), - ChangeNotifierProxyProvider( - create: (_) { + ChangeNotifierProxyProvider(create: (_) { var studentIdDataProvider = StudentIdDataProvider(); return studentIdDataProvider; }, update: (_, userDataProvider, studentIdDataProvider) { studentIdDataProvider!.userDataProvider = userDataProvider; - //Verify that the user is logged in - if (userDataProvider.isLoggedIn && !studentIdDataProvider.isLoading) { - studentIdDataProvider.fetchData(); - } + // Verify that the user is logged in + final bool isLoggedIn = userDataProvider.isLoggedIn; + final bool isNotLoading = !studentIdDataProvider.isLoading; + if (isLoggedIn && isNotLoading) studentIdDataProvider.fetchData(); return studentIdDataProvider; }), - ChangeNotifierProxyProvider( - create: (_) { + ChangeNotifierProxyProvider(create: (_) { var employeeIdDataProvider = EmployeeIdDataProvider(); return employeeIdDataProvider; }, update: (_, userDataProvider, employeeIdDataProvider) { employeeIdDataProvider!.userDataProvider = userDataProvider; - //Verify that the user is logged in - if (userDataProvider.isLoggedIn && !employeeIdDataProvider.isLoading) { - employeeIdDataProvider.fetchData(); - } + // Verify that the user is logged in + final bool isLoggedIn = userDataProvider.isLoggedIn; + final bool isNotLoading = !employeeIdDataProvider.isLoading; + if (isLoggedIn && isNotLoading) employeeIdDataProvider.fetchData(); return employeeIdDataProvider; }), - ChangeNotifierProxyProvider( - create: (_) { + ChangeNotifierProxyProvider(create: (_) { var availabilityDataProvider = AvailabilityDataProvider(); availabilityDataProvider.fetchAvailability(); return availabilityDataProvider; @@ -203,8 +192,7 @@ List dependentServices = [ availabilityDataProvider!.userDataProvider = userDataProvider; return availabilityDataProvider; }), - ChangeNotifierProxyProvider2(create: (_) { + ChangeNotifierProxyProvider2(create: (_) { var shuttleDataProvider = ShuttleDataProvider(); return shuttleDataProvider; }, update: (_, coordinates, userDataProvider, shuttleDataProvider) { @@ -214,8 +202,7 @@ List dependentServices = [ shuttleDataProvider.fetchStops(true); return shuttleDataProvider; }), - ChangeNotifierProxyProvider2(create: (_) { + ChangeNotifierProxyProvider2(create: (_) { SpeedTestProvider speedTestProvider = SpeedTestProvider(); speedTestProvider.init(); return speedTestProvider; @@ -224,8 +211,7 @@ List dependentServices = [ speedTestProvider.userDataProvider = userDataProvider; return speedTestProvider; }), - ChangeNotifierProxyProvider( - create: (_) { + ChangeNotifierProxyProvider(create: (_) { var parkingDataProvider = ParkingDataProvider(); return parkingDataProvider; }, update: (_, userDataProvider, parkingDataProvider) { diff --git a/lib/app_router.dart b/lib/app_router.dart index 939fc55e2..1a5914362 100644 --- a/lib/app_router.dart +++ b/lib/app_router.dart @@ -38,78 +38,78 @@ import 'package:provider/provider.dart'; class Router { static Route generateRoute(RouteSettings settings) { switch (settings.name) { - case RoutePaths.BottomNavigationBar: + case RoutePaths.BOTTOM_NAVIGATION_BAR: return MaterialPageRoute(builder: (_) => BottomTabBar()); - case RoutePaths.OnboardingInitial: + case RoutePaths.ONBOARDING_INITIAL: return MaterialPageRoute(builder: (_) => OnboardingSlides()); - case RoutePaths.OnboardingLogin: + case RoutePaths.ONBOARDING_LOGIN: return MaterialPageRoute(builder: (_) => OnboardingLogin()); - case RoutePaths.Home: + case RoutePaths.HOME: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(null); return Home(); }); - case RoutePaths.Map: + case RoutePaths.MAP: return MaterialPageRoute(builder: (_) => prefix0.Maps()); - case RoutePaths.MapSearch: + case RoutePaths.MAP_SEARCH: return MaterialPageRoute(builder: (_) => MapSearchView()); - case RoutePaths.Notifications: + case RoutePaths.NOTIFICATIONS: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return NotificationsListView(); }); - case RoutePaths.Profile: + case RoutePaths.PROFILE: return MaterialPageRoute(builder: (_) => Profile()); - case RoutePaths.NewsViewAll: + case RoutePaths.NEWS_VIEW_ALL: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return NewsList(); }); - case RoutePaths.EventsViewAll: + case RoutePaths.EVENTS_VIEW_ALL: return MaterialPageRoute(builder: (context) { Provider.of(context).changeTitle(settings.name); return EventsCardList(); }); - case RoutePaths.NewsDetailView: + case RoutePaths.NEWS_DETAIL_VIEW: Item newsItem = settings.arguments as Item; return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return NewsDetailView(data: newsItem); }); - case RoutePaths.EventDetailView: + case RoutePaths.EVENT_DETAIL_VIEW: EventModel data = settings.arguments as EventModel; return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return EventDetailView(data: data); }); - case RoutePaths.EventsAll: + case RoutePaths.EVENTS_ALL: return MaterialPageRoute(builder: (context) { Provider.of(context).changeTitle(settings.name); return EventsAll(); }); - case RoutePaths.ManageAvailabilityView: + case RoutePaths.MANAGE_AVAILABILITY_VIEW: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return ManageAvailabilityView(); }); - case RoutePaths.AvailabilityDetailedView: + case RoutePaths.AVAILABILITY_DETAILED_VIEW: SubLocations subLocation = settings.arguments as SubLocations; return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return AvailabilityDetailedView(subLocation: subLocation); }); - case RoutePaths.DiningViewAllDiningOptions: + case RoutePaths.DINING_VIEW_ALL_DINING_OPTIONS: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return DiningList(); }); - case RoutePaths.DiningOptionDetailView: + case RoutePaths.DINING_OPTION_DETAIL_VIEW: DiningModel data = settings.arguments as DiningModel; return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return DiningDetailView(data: data); }); - case RoutePaths.DiningPaymentFilterView: + case RoutePaths.DINING_PAYMENT_FILTER_VIEW: // Don't Change the app bar title for this view return MaterialPageRoute(builder: (_) => DiningFilterView()); // case RoutePaths.DiningNutritionView: @@ -124,52 +124,52 @@ class Router { // disclaimer: disclaimer, // disclaimerEmail: disclaimerEmail, // )); - case RoutePaths.ManageParkingView: + case RoutePaths.MANAGE_PARKING_VIEW: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return ManageParkingView(); }); - case RoutePaths.SpotTypesView: + case RoutePaths.SPOT_TYPES_VIEW: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return SpotTypesView(); }); - case RoutePaths.ManageShuttleView: + case RoutePaths.MANAGE_SHUTTLE_VIEW: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return ManageShuttleView(); }); - case RoutePaths.AddShuttleStopsView: + case RoutePaths.ADD_SHUTTLE_STOPS_VIEW: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return AddShuttleStopsView(); }); - case RoutePaths.CardsView: + case RoutePaths.CARDS_VIEW: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return CardsView(); }); - case RoutePaths.NotificationsFilter: + case RoutePaths.NOTIFICATIONS_FILTER: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return NotificationsFilterView(); }); - case RoutePaths.ClassScheduleViewAll: + case RoutePaths.CLASS_SCHEDULE_VIEW_ALL: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name); return ClassList(); }); - case RoutePaths.ParkingStructureView: + case RoutePaths.PARKING_STRUCTURE_VIEW: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name, done: true); return ParkingStructureView(); }); - case RoutePaths.NeighborhoodsView: + case RoutePaths.NEIGHBORHOODS_VIEW: return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name, done: true); return NeighborhoodsView(); }); - case RoutePaths.NeighborhoodsLotsView: + case RoutePaths.NEIGHBORHOODS_LOTS_VIEW: List data = settings.arguments as List; return MaterialPageRoute(builder: (_) { Provider.of(_).changeTitle(settings.name, done: true); diff --git a/lib/app_styles.dart b/lib/app_styles.dart index b48c34991..db90fd9c0 100644 --- a/lib/app_styles.dart +++ b/lib/app_styles.dart @@ -63,17 +63,11 @@ const subHeaderStyle = TextStyle( fontWeight: FontWeight.w500, ); -final textButtonSmallLight = TextStyle( - fontFamily: 'Brix Sans', - fontSize: 22.0, - color: linkTextColorLight, - decoration: TextDecoration.underline); +final textButtonSmallLight = + TextStyle(fontFamily: 'Brix Sans', fontSize: 22.0, color: linkTextColorLight, decoration: TextDecoration.underline); -final textButtonSmallDark = TextStyle( - fontFamily: 'Brix Sans', - fontSize: 22.0, - color: linkTextColorDark, - decoration: TextDecoration.underline); +final textButtonSmallDark = + TextStyle(fontFamily: 'Brix Sans', fontSize: 22.0, color: linkTextColorDark, decoration: TextDecoration.underline); const titleSmallLight = TextStyle( fontFamily: 'Refrigerator Deluxe', @@ -89,17 +83,11 @@ const titleSmallDark = TextStyle( fontWeight: FontWeight.w900, color: Color(0xFFF5F0E6)); -const titleMediumLight = TextStyle( - fontFamily: 'Brix Sans', - fontSize: 22.0, - fontWeight: FontWeight.w700, - color: lightPrimaryColor); +const titleMediumLight = + TextStyle(fontFamily: 'Brix Sans', fontSize: 22.0, fontWeight: FontWeight.w700, color: lightPrimaryColor); -const titleMediumDark = TextStyle( - fontFamily: 'Brix Sans', - fontSize: 22.0, - fontWeight: FontWeight.w700, - color: Colors.white); +const titleMediumDark = + TextStyle(fontFamily: 'Brix Sans', fontSize: 22.0, fontWeight: FontWeight.w700, color: Colors.white); const headlineMediumLight = TextStyle( fontFamily: 'Brix Sans', @@ -118,30 +106,16 @@ const headlineMediumDark = TextStyle( color: linkColorDark); const headlineMediumLight2 = TextStyle( - fontFamily: 'Brix Sans', - fontSize: 20.0, - fontWeight: FontWeight.w700, - height: 1.2, - color: lightPrimaryColor); + fontFamily: 'Brix Sans', fontSize: 20.0, fontWeight: FontWeight.w700, height: 1.2, color: lightPrimaryColor); const headlineMediumDark2 = TextStyle( - fontFamily: 'Brix Sans', - fontSize: 20.0, - fontWeight: FontWeight.w700, - height: 1.2, - color: darkPrimaryColor2); + fontFamily: 'Brix Sans', fontSize: 20.0, fontWeight: FontWeight.w700, height: 1.2, color: darkPrimaryColor2); -const bodyMediumLight = TextStyle( - fontFamily: 'Brix Sans', - fontSize: 22.0, - fontWeight: FontWeight.w500, - color: descriptiveTextColorLight); +const bodyMediumLight = + TextStyle(fontFamily: 'Brix Sans', fontSize: 22.0, fontWeight: FontWeight.w500, color: descriptiveTextColorLight); -const bodyMediumDark = TextStyle( - fontFamily: 'Brix Sans', - fontSize: 22.0, - fontWeight: FontWeight.w500, - color: descriptiveTextColorDark); +const bodyMediumDark = + TextStyle(fontFamily: 'Brix Sans', fontSize: 22.0, fontWeight: FontWeight.w500, color: descriptiveTextColorDark); const appBarTitleStyle = TextStyle( fontFamily: 'Refrigerator Deluxe', @@ -150,11 +124,8 @@ const appBarTitleStyle = TextStyle( color: Colors.white, ); -const cardTitleStyleLight = TextStyle( - fontFamily: 'Refrigerator Deluxe', - fontSize: 28, - fontWeight: FontWeight.w900, - color: lightPrimaryColor); +const cardTitleStyleLight = + TextStyle(fontFamily: 'Refrigerator Deluxe', fontSize: 28, fontWeight: FontWeight.w900, color: lightPrimaryColor); const cardTitleStyleDark = TextStyle( fontFamily: 'Refrigerator Deluxe', @@ -187,11 +158,7 @@ const labelMediumStyleLight = TextStyle( color: descriptiveTextColorLight); const labelMediumStyleDark = TextStyle( - fontFamily: 'Brix Sans', - fontSize: 22.0, - fontWeight: FontWeight.w400, - height: 1.0, - color: descriptiveTextColorDark); + fontFamily: 'Brix Sans', fontSize: 22.0, fontWeight: FontWeight.w400, height: 1.0, color: descriptiveTextColorDark); const TextStyle notificationsTitleLight = TextStyle( color: linkTextColorLight, @@ -219,11 +186,9 @@ const TextStyle textSubheaderDark = TextStyle( height: 1.22, ); -const textSmallMoreInfoLight = TextStyle( - fontSize: 15.0, fontWeight: FontWeight.w700, color: lightPrimaryColor); +const textSmallMoreInfoLight = TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700, color: lightPrimaryColor); -const textSmallMoreInfoDark = TextStyle( - fontSize: 15.0, fontWeight: FontWeight.w700, color: darkPrimaryColor2); +const textSmallMoreInfoDark = TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700, color: darkPrimaryColor2); // New custom styles for heading2 const TextStyle heading2StyleLight = TextStyle( @@ -241,16 +206,10 @@ const TextStyle heading2StyleDark = TextStyle( ); const TextStyle linkTextDark = TextStyle( - fontFamily: 'Brix Sans', - fontWeight: FontWeight.w400, - decoration: TextDecoration.underline, - color: linkColorDark); + fontFamily: 'Brix Sans', fontWeight: FontWeight.w400, decoration: TextDecoration.underline, color: linkColorDark); const TextStyle linkTextLight = TextStyle( - fontFamily: 'Brix Sans', - fontWeight: FontWeight.w400, - decoration: TextDecoration.underline, - color: linkColorLight); + fontFamily: 'Brix Sans', fontWeight: FontWeight.w400, decoration: TextDecoration.underline, color: linkColorLight); const Color toggleActiveColor = Color(0xFF109B00); @@ -364,10 +323,8 @@ const Color c2 = Color.fromARGB(255, 0, 255, 0); const Color c3 = Color.fromARGB(255, 0, 0, 255); // List Tile Theme Data -const lightListTileTheme = - ListTileThemeData(selectedColor: const Color(0xFF00629B)); -const darkListTileTheme = - ListTileThemeData(selectedColor: const Color(0xFF00C6D7)); +const lightListTileTheme = ListTileThemeData(selectedColor: const Color(0xFF00629B)); +const darkListTileTheme = ListTileThemeData(selectedColor: const Color(0xFF00C6D7)); // New Onboarding Screen Colors const lightOnboardingScreen = Color.fromARGB(255, 245, 240, 228); @@ -382,5 +339,4 @@ const List grayscaleInvertMatrix = [ // Dining logo border and shadow colors for dark mode const Color darkLogoBorderColor = Color(0xFF444444); // subtle dark border -const Color darkLogoShadowColor = - Colors.white; // use white for shadow in dark mode +const Color darkLogoShadowColor = Colors.white; // use white for shadow in dark mode diff --git a/lib/core/models/authentication.dart b/lib/core/models/authentication.dart index af2a3bfd6..1b6615bfd 100644 --- a/lib/core/models/authentication.dart +++ b/lib/core/models/authentication.dart @@ -5,11 +5,9 @@ part 'authentication.g.dart'; // To parse this JSON data, do // // final authenticationModel = authenticationModelFromJson(jsonString); -AuthenticationModel authenticationModelFromJson(String str) => - AuthenticationModel.fromJson(json.decode(str)); +AuthenticationModel authenticationModelFromJson(String str) => AuthenticationModel.fromJson(json.decode(str)); -String authenticationModelToJson(AuthenticationModel data) => - json.encode(data.toJson()); +String authenticationModelToJson(AuthenticationModel data) => json.encode(data.toJson()); @HiveType(typeId: 1) class AuthenticationModel extends HiveObject { @@ -36,8 +34,7 @@ class AuthenticationModel extends HiveObject { return AuthenticationModel( accessToken: json["access_token"] == null ? null : json["access_token"], pid: json["pid"] == null ? null : json["pid"], - ucsdaffiliation: - json["ucsdaffiliation"] == null ? "" : json["ucsdaffiliation"], + ucsdaffiliation: json["ucsdaffiliation"] == null ? "" : json["ucsdaffiliation"], expiration: json["expiration"] == null ? 0 : json["expiration"], ); } @@ -52,18 +49,15 @@ class AuthenticationModel extends HiveObject { /// Checks if the token we got back is expired bool isLoggedIn(DateTime? lastUpdated) { /// User has not logged in previously - isLoggedIn FALSE - if (lastUpdated == null) { - return false; - } + if (lastUpdated == null) return false; /// User has no expiration or accessToken - isLoggedIn FALSE - if (expiration == null || accessToken == null) { - return false; - } + if (expiration == null || accessToken == null) return false; /// User has expiration and accessToken - if (DateTime.now() - .isBefore(lastUpdated.add(Duration(seconds: expiration!)))) { + final DateTime expirationTime = lastUpdated.add(Duration(seconds: expiration!)); + final bool isNotExpired = DateTime.now().isBefore(expirationTime); + if (isNotExpired) { /// Current datetime < expiration datetime - isLoggedIn TRUE return true; } else { diff --git a/lib/core/models/authentication.g.dart b/lib/core/models/authentication.g.dart index 7ab646f13..49577454a 100644 --- a/lib/core/models/authentication.g.dart +++ b/lib/core/models/authentication.g.dart @@ -43,7 +43,5 @@ class AuthenticationModelAdapter extends TypeAdapter { @override bool operator ==(Object other) => identical(this, other) || - other is AuthenticationModelAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; + other is AuthenticationModelAdapter && runtimeType == other.runtimeType && typeId == other.typeId; } diff --git a/lib/core/models/availability.dart b/lib/core/models/availability.dart index 8580151d9..d270cc2d0 100644 --- a/lib/core/models/availability.dart +++ b/lib/core/models/availability.dart @@ -3,11 +3,9 @@ import 'dart:convert'; // To parse this JSON data, do // // final availabilityStatus = availabilityStatusFromJson(jsonString); -AvailabilityStatus availabilityStatusFromJson(String str) => - AvailabilityStatus.fromJson(json.decode(str)); +AvailabilityStatus availabilityStatusFromJson(String str) => AvailabilityStatus.fromJson(json.decode(str)); -String availabilityStatusToJson(AvailabilityStatus data) => - json.encode(data.toJson()); +String availabilityStatusToJson(AvailabilityStatus data) => json.encode(data.toJson()); class AvailabilityStatus { AvailabilityStatus({ @@ -20,39 +18,31 @@ class AvailabilityStatus { List data; DateTime timestamp; - factory AvailabilityStatus.fromJson(Map json) => - AvailabilityStatus( + factory AvailabilityStatus.fromJson(Map json) => AvailabilityStatus( status: json["status"]!, - // TODO: rewrite this to be shorter! Functional style iterators?... + // TODO: rewrite this to be shorter! Functional style iterators?... - December 2025 data: (() { - List returnList = List.from( - json["data"]!.map((x) => AvailabilityModel.fromJson(x))); + List returnList = + List.from(json["data"]!.map((x) => AvailabilityModel.fromJson(x))); - // TODO: remove this line after the missing Markets data is fixed on the backend + // TODO: remove this line after the missing Markets data is fixed on the backend - December 2025 returnList.removeWhere((model) => model.name == "Markets"); for (int index = 0; index < returnList.length; index++) { if (returnList[index].subLocations.length > 3) { String baseName = returnList[index].name; int baseId = returnList[index].id; - List baseSubLocations = - returnList[index].subLocations; + List baseSubLocations = returnList[index].subLocations; returnList.removeAt(index); index--; int curPageIndex = 1; int maxPageIndex = (baseSubLocations.length / 3).ceil(); for (int i = 0; i < baseSubLocations.length; i += 3) { index++; - List curSubList = baseSubLocations.sublist( - i, - i + 3 > baseSubLocations.length - ? baseSubLocations.length - : i + 3); + List curSubList = + baseSubLocations.sublist(i, i + 3 > baseSubLocations.length ? baseSubLocations.length : i + 3); String curName = baseName + " ($curPageIndex/$maxPageIndex)"; - returnList.insert( - index, - AvailabilityModel( - id: baseId, name: curName, subLocations: curSubList)); + returnList.insert(index, AvailabilityModel(id: baseId, name: curName, subLocations: curSubList)); curPageIndex++; } } @@ -80,12 +70,10 @@ class AvailabilityModel { String name; List subLocations; - factory AvailabilityModel.fromJson(Map json) => - AvailabilityModel( + factory AvailabilityModel.fromJson(Map json) => AvailabilityModel( id: json["id"]!, name: json["name"]!, - subLocations: List.from( - json["childCounts"]!.map((x) => SubLocations.fromJson(x))), + subLocations: List.from(json["childCounts"]!.map((x) => SubLocations.fromJson(x))), ); Map toJson() => { @@ -97,11 +85,7 @@ class AvailabilityModel { class SubLocations { SubLocations( - {required this.id, - required this.name, - required this.percentage, - required this.isActive, - required this.floors}); + {required this.id, required this.name, required this.percentage, required this.isActive, required this.floors}); int id; String name; @@ -114,25 +98,14 @@ class SubLocations { name: json["name"]!, percentage: json["percentage"]!.toDouble(), isActive: json["isActive"]!, - floors: - List.from(json["childCounts"]!.map((x) => Floor.fromJson(x)))); + floors: List.from(json["childCounts"]!.map((x) => Floor.fromJson(x)))); - Map toJson() => { - "id": id, - "name": name, - "percentage": percentage, - "isActive": isActive, - "floors": floors - }; + Map toJson() => + {"id": id, "name": name, "percentage": percentage, "isActive": isActive, "floors": floors}; } class Floor { - Floor( - {required this.id, - required this.name, - required this.count, - required this.percentage, - required this.isActive}); + Floor({required this.id, required this.name, required this.count, required this.percentage, required this.isActive}); int id; String name; diff --git a/lib/core/models/cards.dart b/lib/core/models/cards.dart index 638e9dec4..5eb5bf564 100644 --- a/lib/core/models/cards.dart +++ b/lib/core/models/cards.dart @@ -4,11 +4,10 @@ import 'dart:convert'; // // final cardsModel = cardsModelFromJson(jsonString); Map cardsModelFromJson(String str) => - Map.from(json.decode(str)) - .map((k, v) => MapEntry(k, CardsModel.fromJson(v))); + Map.from(json.decode(str)).map((k, v) => MapEntry(k, CardsModel.fromJson(v))); -String cardsModelToJson(Map data) => json.encode( - Map.from(data).map((k, v) => MapEntry(k, v.toJson()))); +String cardsModelToJson(Map data) => + json.encode(Map.from(data).map((k, v) => MapEntry(k, v.toJson()))); class CardsModel { CardsModel( diff --git a/lib/core/models/classes.dart b/lib/core/models/classes.dart index 297774afe..87f173a63 100644 --- a/lib/core/models/classes.dart +++ b/lib/core/models/classes.dart @@ -3,11 +3,9 @@ import 'dart:convert'; // To parse this JSON data, do // // final classScheduleModel = classScheduleModelFromJson(jsonString); -ClassScheduleModel classScheduleModelFromJson(String str) => - ClassScheduleModel.fromJson(json.decode(str)); +ClassScheduleModel classScheduleModelFromJson(String str) => ClassScheduleModel.fromJson(json.decode(str)); -String classScheduleModelToJson(ClassScheduleModel data) => - json.encode(data.toJson()); +String classScheduleModelToJson(ClassScheduleModel data) => json.encode(data.toJson()); class ClassScheduleModel { Metadata? metadata; @@ -17,22 +15,14 @@ class ClassScheduleModel { this.metadata, this.data, }); - factory ClassScheduleModel.fromJson(Map json) => - ClassScheduleModel( - metadata: json["metadata"] == null - ? null - : Metadata.fromJson(json["metadata"]), - data: json["data"] == null - ? null - : List.from( - json["data"].map((x) => ClassData.fromJson(x))), + factory ClassScheduleModel.fromJson(Map json) => ClassScheduleModel( + metadata: json["metadata"] == null ? null : Metadata.fromJson(json["metadata"]), + data: json["data"] == null ? null : List.from(json["data"].map((x) => ClassData.fromJson(x))), ); Map toJson() => { "metadata": metadata == null ? null : metadata!.toJson(), - "data": data == null - ? null - : List.from(data!.map((x) => x.toJson())), + "data": data == null ? null : List.from(data!.map((x) => x.toJson())), }; } @@ -72,14 +62,11 @@ class ClassData { gradeOption: json["grade_option"] == null ? null : json["grade_option"], grade: json["grade"] == null ? null : json["grade"], courseTitle: json["course_title"] == null ? null : json["course_title"], - enrollmentStatus: json["enrollment_status"] == null - ? null - : json["enrollment_status"], + enrollmentStatus: json["enrollment_status"] == null ? null : json["enrollment_status"], repeatCode: json["repeat_code"] == null ? null : json["repeat_code"], sectionData: json["section_data"] == null ? null - : List.from( - json["section_data"].map((x) => SectionData.fromJson(x))), + : List.from(json["section_data"].map((x) => SectionData.fromJson(x))), ); Map toJson() => { @@ -93,9 +80,7 @@ class ClassData { "course_title": courseTitle == null ? null : courseTitle, "enrollment_status": enrollmentStatus == null ? null : enrollmentStatus, "repeat_code": repeatCode == null ? null : repeatCode, - "section_data": sectionData == null - ? null - : List.from(sectionData!.map((x) => x.toJson())), + "section_data": sectionData == null ? null : List.from(sectionData!.map((x) => x.toJson())), }; } diff --git a/lib/core/models/dining.dart b/lib/core/models/dining.dart index 98d7e1b74..8676b7c42 100644 --- a/lib/core/models/dining.dart +++ b/lib/core/models/dining.dart @@ -4,11 +4,10 @@ import 'package:campus_mobile_experimental/core/models/location.dart'; // To parse this JSON data, do // // final diningModel = diningModelFromJson(jsonString); -List diningModelFromJson(String str) => List.from( - json.decode(str).map((x) => DiningModel.fromJson(x))); +List diningModelFromJson(String str) => + List.from(json.decode(str).map((x) => DiningModel.fromJson(x))); -String diningModelToJson(List data) => - json.encode(List.from(data.map((x) => x.toJson()))); +String diningModelToJson(List data) => json.encode(List.from(data.map((x) => x.toJson()))); class DiningModel { String address; @@ -56,33 +55,26 @@ class DiningModel { DiningModel.fromJson(Map json) : id = json["id"], - name = json["name"], - description = json["description"], - location = json["location"], - address = json["address"], - tel = json["tel"], + name = json["name"] ?? "", + description = json["description"] ?? "", + location = json["location"] ?? "", + address = json["address"] ?? "", + tel = json["tel"] ?? "", meals = json["meals"] == null ? null : mealsValues.map[json["meals"]], persistentMenu = json["persistentMenu"], paymentOptions = - List.from(json["paymentOptions"].map((x) => x)), - paymentFilterTypes = json["paymentFilterTypes"], - images = json["images"] == null - ? null - : List.from(json["images"].map((x) => Image.fromJson(x))), - coordinates = json["coords"] == null - ? null - : Coordinates.fromJson(json["coords"]), + json["paymentOptions"] == null ? [] : List.from(json["paymentOptions"].map((x) => x ?? "")), + paymentFilterTypes = json["paymentFilterTypes"] ?? "", + images = json["images"] == null ? null : List.from(json["images"].map((x) => Image.fromJson(x))), + coordinates = json["coords"] == null ? null : Coordinates.fromJson(json["coords"]), regularHours = RegularHours.fromJson(json["regularHours"]), - specialHours = - (json["specialHours"] == null || json["specialHours"].isEmpty) - ? null - : SpecialHour.fromJson(json["specialHours"]), + specialHours = (json["specialHours"] == null || json["specialHours"].isEmpty) + ? null + : SpecialHour.fromJson(json["specialHours"]), vendorLogo = json["vendorLogo"], url = json["url"], menuWebsite = json["menuWebsite"], - specials = json["specials"] == null - ? null - : Specials.fromJson(json["specials"]); + specials = json["specials"] == null ? null : Specials.fromJson(json["specials"]); Map toJson() => { "id": id, @@ -95,9 +87,7 @@ class DiningModel { "persistentMenu": persistentMenu, "paymentOptions": List.from(paymentOptions.map((x) => x)), "paymentFilterTypes": paymentFilterTypes, - "images": images == null - ? null - : List.from(images!.map((x) => x.toJson())), + "images": images == null ? null : List.from(images!.map((x) => x.toJson())), "coords": coordinates == null ? null : coordinates!.toJson(), "regularHours": regularHours.toJson(), "specialHours": specialHours?.toJson(), @@ -111,10 +101,10 @@ class DiningModel { class Image { // links to different sizes of the image - // TODO: BUG ON SERVER?? There have been images with no images observed in the wild... + // TODO: BUG ON SERVER?? There have been images with no images observed in the wild... - December 2025 String? small; String? large; - // TODO: no caption is valid JSON response. Should this be empty str rather than null? + // TODO: no caption is valid JSON response. Should this be empty str rather than null? - December 2025 String? caption; Image({ @@ -137,10 +127,8 @@ class Image { enum Meals { BREAKFAST_LUNCH_DINNER, LUNCH_DINNER } -final mealsValues = EnumValues({ - "breakfast, lunch, dinner": Meals.BREAKFAST_LUNCH_DINNER, - "lunch, dinner": Meals.LUNCH_DINNER -}); +final mealsValues = + EnumValues({"breakfast, lunch, dinner": Meals.BREAKFAST_LUNCH_DINNER, "lunch, dinner": Meals.LUNCH_DINNER}); class RegularHours { // ALL CONFIRMED OPTIONAL @@ -186,7 +174,7 @@ class SpecialHour { String specialHoursEvent; String specialHoursEventDetails; - // TODO: double check if these can ever be null + // TODO: double check if these can ever be null - December 2025 String? specialHoursValidFrom; String? specialHoursValidTo; @@ -224,9 +212,7 @@ class Specials { Specials.fromJson(Map json) : specialDescription = json["specialDescription"], specialTitle = json["specialTitle"], - promoDates = json["promoDates"] == null - ? null - : PromoDates.fromJson(json["promoDates"]); + promoDates = json["promoDates"] == null ? null : PromoDates.fromJson(json["promoDates"]); Map toJson() => { "specialDescription": specialDescription, @@ -245,12 +231,8 @@ class PromoDates { }); PromoDates.fromJson(Map json) - : startDate = json["startDate"] is String - ? int.tryParse(json["startDate"]) - : json["startDate"], - endDate = json["endDate"] is String - ? int.tryParse(json["endDate"]) - : json["endDate"]; + : startDate = json["startDate"] is String ? int.tryParse(json["startDate"]) : json["startDate"], + endDate = json["endDate"] is String ? int.tryParse(json["endDate"]) : json["endDate"]; Map toJson() => { "startDate": startDate, diff --git a/lib/core/models/dining_menu.dart b/lib/core/models/dining_menu.dart index 562dea49b..dce643fdb 100644 --- a/lib/core/models/dining_menu.dart +++ b/lib/core/models/dining_menu.dart @@ -3,11 +3,9 @@ import 'dart:convert'; // To parse this JSON data, do // // final diningMenuItemsModel = diningMenuItemsModelFromJson(jsonString); -DiningMenuItemsModel diningMenuItemsModelFromJson(String str) => - DiningMenuItemsModel.fromJson(json.decode(str)); +DiningMenuItemsModel diningMenuItemsModelFromJson(String str) => DiningMenuItemsModel.fromJson(json.decode(str)); -String diningMenuItemsModelToJson(DiningMenuItemsModel data) => - json.encode(data.toJson()); +String diningMenuItemsModelToJson(DiningMenuItemsModel data) => json.encode(data.toJson()); class DiningMenuItemsModel { List? menuItems; @@ -22,16 +20,13 @@ class DiningMenuItemsModel { DiningMenuItemsModel.fromJson(Map json) : menuItems = json["menuitems"] != null - ? List.from( - json["menuitems"].map((x) => DiningMenuItem.fromJson(x))) + ? List.from(json["menuitems"].map((x) => DiningMenuItem.fromJson(x))) : [], disclaimer = json["disclaimer"], disclaimerEmail = json["disclaimerEmail"]; Map toJson() => { - "menuitems": menuItems!.isNotEmpty - ? List.from(menuItems!.map((x) => x.toJson())) - : null, + "menuitems": menuItems!.isNotEmpty ? List.from(menuItems!.map((x) => x.toJson())) : null, "disclaimer": disclaimer, "disclaimerEmail": disclaimerEmail, }; @@ -58,13 +53,13 @@ class DiningMenuItem { }); DiningMenuItem.fromJson(Map json) - : name = json["name"], + : name = json["name"] ?? "", itemId = json["itemID"], station = json["station"], - price = json["price"], + price = json["price"] ?? "", images = json["images"], - tags = json["tags"], - nutrition = Nutrition.fromJson(json["nutrition"]); + tags = json["tags"] ?? "", + nutrition = json["nutrition"] != null ? Nutrition.fromJson(json["nutrition"]) : Nutrition(); Map toJson() => { "name": name, diff --git a/lib/core/models/employee_id.dart b/lib/core/models/employee_id.dart index 1426b65fd..a6a923e45 100644 --- a/lib/core/models/employee_id.dart +++ b/lib/core/models/employee_id.dart @@ -3,11 +3,9 @@ import 'dart:convert'; // To parse this JSON data, do // // final employeeIdModel = employeeIdModelFromJson(jsonString); -EmployeeIdModel employeeIdModelFromJson(String str) => - EmployeeIdModel.fromJson(json.decode(str)); +EmployeeIdModel employeeIdModelFromJson(String str) => EmployeeIdModel.fromJson(json.decode(str)); -String employeeIdModelToJson(EmployeeIdModel data) => - json.encode(data.toJson()); +String employeeIdModelToJson(EmployeeIdModel data) => json.encode(data.toJson()); class EmployeeIdModel { EmployeeIdModel({ @@ -26,30 +24,22 @@ class EmployeeIdModel { dynamic photo; dynamic classificationType; - factory EmployeeIdModel.fromJson(Map json) => - EmployeeIdModel( + factory EmployeeIdModel.fromJson(Map json) => EmployeeIdModel( employeePreferredDisplayName: - json["Employee Preferred Display Name"] == null - ? "" - : json["Employee Preferred Display Name"], + json["Employee Preferred Display Name"] == null ? "" : json["Employee Preferred Display Name"], employeeId: json["Employee ID"] == null ? "" : json["Employee ID"], department: json["Department"] == null ? "" : json["Department"], barcode: json["Barcode"] == null ? "" : json["Barcode"], photo: json["Photo"], // Keep null as null for proper base64 handling - classificationType: json["Classification Type"] == null - ? "Employee" - : json["Classification Type"], + classificationType: json["Classification Type"] == null ? "Employee" : json["Classification Type"], ); Map toJson() => { - "Employee Preferred Display Name": employeePreferredDisplayName == null - ? "" - : employeePreferredDisplayName, + "Employee Preferred Display Name": employeePreferredDisplayName == null ? "" : employeePreferredDisplayName, "Employee ID": employeeId == null ? "" : employeeId, "Department": department == null ? "" : department, "Barcode": barcode == null ? "" : barcode, "Photo": photo == null ? "" : photo, - "Classification Type": - classificationType == null ? "Employee" : classificationType, + "Classification Type": classificationType == null ? "Employee" : classificationType, }; } diff --git a/lib/core/models/events.dart b/lib/core/models/events.dart index e5f2f28f9..91a3f7bd6 100644 --- a/lib/core/models/events.dart +++ b/lib/core/models/events.dart @@ -6,8 +6,7 @@ import 'dart:convert'; List eventModelFromJson(String str) => List.from(json.decode(str).map((x) => EventModel.fromJson(x))); -String eventModelToJson(List data) => - json.encode(List.from(data.map((x) => x.toJson()))); +String eventModelToJson(List data) => json.encode(List.from(data.map((x) => x.toJson()))); class EventModel { EventModel({ @@ -43,9 +42,7 @@ class EventModel { imageThumb = json["imageThumb"], link = json["link"], id = json["id"], - tags = json["tags"] == null - ? null - : List.from(json["tags"].map((x) => x)), + tags = json["tags"] == null ? null : List.from(json["tags"].map((x) => x)), location = json["location"]; Map toJson() => { diff --git a/lib/core/models/location.dart b/lib/core/models/location.dart index 1d4222970..e6e9a3138 100644 --- a/lib/core/models/location.dart +++ b/lib/core/models/location.dart @@ -1,4 +1,4 @@ -// TODO: verify lat and lon are never null. It doesn't make rational sense why they could be... +// TODO: verify lat and lon are never null. It doesn't make rational sense why they could be... - December 2025 class Coordinates { double? lat; double? lon; diff --git a/lib/core/models/map.dart b/lib/core/models/map.dart index 44f582c10..b5de7c9d3 100644 --- a/lib/core/models/map.dart +++ b/lib/core/models/map.dart @@ -4,11 +4,9 @@ import 'dart:convert'; // // final mapSearchModel = mapSearchModelFromJson(jsonString); List mapSearchModelFromJson(String str) => - List.from( - json.decode(str).map((x) => MapSearchModel.fromJson(x))); + List.from(json.decode(str).map((x) => MapSearchModel.fromJson(x))); -String mapSearchModelToJson(List data) => - json.encode(List.from(data.map((x) => x.toJson()))); +String mapSearchModelToJson(List data) => json.encode(List.from(data.map((x) => x.toJson()))); class MapSearchModel { String? title; diff --git a/lib/core/models/news.dart b/lib/core/models/news.dart index 3e4be34cb..8882d7c60 100644 --- a/lib/core/models/news.dart +++ b/lib/core/models/news.dart @@ -14,8 +14,7 @@ class NewsModel { List? items, }) : items = items ?? []; - NewsModel.fromJson(Map json) - : items = List.from(json["items"].map((x) => Item.fromJson(x))); + NewsModel.fromJson(Map json) : items = List.from(json["items"].map((x) => Item.fromJson(x))); Map toJson() => { "items": List.from(items.map((x) => x.toJson())), diff --git a/lib/core/models/notices.dart b/lib/core/models/notices.dart index 642cf6e13..7d28feab1 100644 --- a/lib/core/models/notices.dart +++ b/lib/core/models/notices.dart @@ -4,11 +4,10 @@ String noticeTitleKey = "notice-title"; String noticeBannerImageKey = "notice-banner-image"; String noticeBannerLinkKey = "notice-banner-link"; -List noticesModelFromJson(String str) => List.from( - json.decode(str).map((x) => NoticesModel.fromJson(x))); +List noticesModelFromJson(String str) => + List.from(json.decode(str).map((x) => NoticesModel.fromJson(x))); -String noticesModelToJson(List data) => - json.encode(List.from(data.map((x) => x.toJson()))); +String noticesModelToJson(List data) => json.encode(List.from(data.map((x) => x.toJson()))); class NoticesModel { String title; @@ -26,9 +25,5 @@ class NoticesModel { imageUrl = json[noticeBannerImageKey], link = json[noticeBannerLinkKey]; - Map toJson() => { - noticeTitleKey: title, - noticeBannerImageKey: imageUrl, - noticeBannerLinkKey: link - }; + Map toJson() => {noticeTitleKey: title, noticeBannerImageKey: imageUrl, noticeBannerLinkKey: link}; } diff --git a/lib/core/models/notifications.dart b/lib/core/models/notifications.dart index 49c884cf3..370fb2b48 100644 --- a/lib/core/models/notifications.dart +++ b/lib/core/models/notifications.dart @@ -14,14 +14,10 @@ class Messages { Messages({required this.messages, this.next}); Messages.fromJson(Map json) - : messages = List.from( - json["messages"].map((x) => MessageElement.fromJson(x))), + : messages = List.from(json["messages"].map((x) => MessageElement.fromJson(x))), next = json["next"]; - Map toJson() => { - "messages": List.from(messages.map((x) => x.toJson())), - "next": next - }; + Map toJson() => {"messages": List.from(messages.map((x) => x.toJson())), "next": next}; } class MessageElement { @@ -63,14 +59,9 @@ class Audience { List? topics; // this is a direct message if it's null Audience.fromJson(Map json) - : topics = json["topics"] != null - ? List.from(json["topics"].map((x) => x)) - : null; + : topics = json["topics"] != null ? List.from(json["topics"].map((x) => x)) : null; - Map toJson() => { - "topics": - topics != null ? List.from(topics!.map((x) => x)) : null - }; + Map toJson() => {"topics": topics != null ? List.from(topics!.map((x) => x)) : null}; } class Message { diff --git a/lib/core/models/notifications_freefood.dart b/lib/core/models/notifications_freefood.dart index 08069053d..470f9521a 100644 --- a/lib/core/models/notifications_freefood.dart +++ b/lib/core/models/notifications_freefood.dart @@ -1,7 +1,6 @@ import 'dart:convert'; -FreeFoodModel freeFoodModelFromJson(String str) => - FreeFoodModel.fromJson(json.decode(str)); +FreeFoodModel freeFoodModelFromJson(String str) => FreeFoodModel.fromJson(json.decode(str)); String freeFoodModelToJson(FreeFoodModel data) => json.encode(data.toJson()); diff --git a/lib/core/models/parking.dart b/lib/core/models/parking.dart index 0141ee9c3..51032ac6c 100644 --- a/lib/core/models/parking.dart +++ b/lib/core/models/parking.dart @@ -3,11 +3,10 @@ import 'dart:convert'; // To parse this JSON data, do // // final parkingModel = parkingModelFromJson(jsonString); -List parkingModelFromJson(String str) => List.from( - json.decode(str).map((x) => ParkingModel.fromJson(x))); +List parkingModelFromJson(String str) => + List.from(json.decode(str).map((x) => ParkingModel.fromJson(x))); -String parkingModelToJson(List data) => - json.encode(List.from(data.map((x) => x.toJson()))); +String parkingModelToJson(List data) => json.encode(List.from(data.map((x) => x.toJson()))); class ParkingModel { String neighborhood; @@ -42,11 +41,8 @@ class ParkingModel { locationContext = json["LocationContext"], locationProvider = json["LocationProvider"], availability = json["Availability"] as Map, - lastUpdated = json["lastUpdated"] == null - ? null - : DateTime.parse(json["LastUpdated"]), - availabilityType = - json["AvailabilityType"] == null ? null : json["AvailabilityType"]; + lastUpdated = json["lastUpdated"] == null ? null : DateTime.parse(json["LastUpdated"]), + availabilityType = json["AvailabilityType"] == null ? null : json["AvailabilityType"]; Map toJson() => { "Neighborhood": neighborhood, diff --git a/lib/core/models/scanner_message.dart b/lib/core/models/scanner_message.dart index b4558e469..93ce1b0d0 100644 --- a/lib/core/models/scanner_message.dart +++ b/lib/core/models/scanner_message.dart @@ -1,21 +1,16 @@ import 'dart:convert'; -ScannerMessageModel scannerMessageModelFromJson(String str) => - ScannerMessageModel.fromJson(json.decode(str)); +ScannerMessageModel scannerMessageModelFromJson(String str) => ScannerMessageModel.fromJson(json.decode(str)); -String scannerMessageModelToJson(ScannerMessageModel data) => - json.encode(data.toJson()); +String scannerMessageModelToJson(ScannerMessageModel data) => json.encode(data.toJson()); class ScannerMessageModel { ScannerMessageModel({this.collectionTime}); String? collectionTime; - factory ScannerMessageModel.fromJson(Map json) => - ScannerMessageModel( - collectionTime: json["Collection Date/Time"] == null - ? "" - : json["Collection Date/Time"], + factory ScannerMessageModel.fromJson(Map json) => ScannerMessageModel( + collectionTime: json["Collection Date/Time"] == null ? "" : json["Collection Date/Time"], ); Map toJson() => { diff --git a/lib/core/models/shuttle.dart b/lib/core/models/shuttle.dart index 116336eec..785768f68 100644 --- a/lib/core/models/shuttle.dart +++ b/lib/core/models/shuttle.dart @@ -2,14 +2,12 @@ import 'dart:convert'; import 'dart:ui'; import 'package:campus_mobile_experimental/core/models/shuttle_stop.dart'; -List? shuttleModelFromJson(String str) => - json.decode(str).map((x) => ShuttleModel.fromJson(x)); +List? shuttleModelFromJson(String str) => json.decode(str).map((x) => ShuttleModel.fromJson(x)); // List.from( // json.decode(str).map((x) => ShuttleModel.fromJson(x))); -String shuttleModelToJson(List data) => - json.encode(List.from(data.map((x) => x.toJson()))); +String shuttleModelToJson(List data) => json.encode(List.from(data.map((x) => x.toJson()))); class ShuttleModel { int? displayOrder; @@ -37,11 +35,9 @@ class ShuttleModel { ShuttleModel.fromJson(Map json) { json.forEach((key, value) => ShuttleModel( - displayOrder: - value["displayOrder"] == null ? null : value["displayOrder"], + displayOrder: value["displayOrder"] == null ? null : value["displayOrder"], url: value["url"] == null ? null : value["lon"], - customerRouteId: - value["customerRouteId"] == null ? null : value["customerRouteId"], + customerRouteId: value["customerRouteId"] == null ? null : value["customerRouteId"], id: value["id"] == null ? null : value["id"], name: value["name"] == null ? null : value["name"], shortName: value["shortName"] == null ? null : value["shortName"], @@ -64,17 +60,13 @@ class ShuttleModel { List ret = []; list.forEach((key, value) { ret.add(ShuttleModel( - displayOrder: - value["displayOrder"] == null ? null : value["displayOrder"], + displayOrder: value["displayOrder"] == null ? null : value["displayOrder"], url: value["url"] == null ? null : value["lon"], - customerRouteId: value["customerRouteId"] == null - ? null - : value["customerRouteId"], + customerRouteId: value["customerRouteId"] == null ? null : value["customerRouteId"], id: value["id"] == null ? null : value["id"], name: value["name"] == null ? null : value["name"], shortName: value["shortName"] == null ? null : value["shortName"], - description: - value["description"] == null ? null : value["description"], + description: value["description"] == null ? null : value["description"], routeType: value["routeType"] == null ? null : value["routeType"], color: value["color"] == null ? null : HexColor(value["color"]), stops: value["stops"] == null @@ -100,18 +92,14 @@ class ShuttleModel { "description": description == null ? null : description, "routeType": routeType == null ? null : routeType, "color": color == null ? null : '#${color!.value.toRadixString(16)}', - "stops": stops == null - ? null - : List.from(stops!.map((x) => x.toJson())) + "stops": stops == null ? null : List.from(stops!.map((x) => x.toJson())) }; } class HexColor extends Color { static int getColorFromHex(String hexColor) { hexColor = hexColor.toUpperCase().replaceAll("#", ""); - if (hexColor.length == 6) { - hexColor = "FF" + hexColor; - } + if (hexColor.length == 6) hexColor = "FF" + hexColor; return int.parse(hexColor, radix: 16); } diff --git a/lib/core/models/speed_test.dart b/lib/core/models/speed_test.dart index fa81524fb..0367a0556 100644 --- a/lib/core/models/speed_test.dart +++ b/lib/core/models/speed_test.dart @@ -47,13 +47,8 @@ class SpeedTestModel { this.uploadSpeed}); factory SpeedTestModel.fromJson( - WifiInfo? wifiInfo, - Map? downloadJson, - Map? uploadJson, - bool isUCSDWifi) { - if (wifiInfo == null) { - return SpeedTestModel(isUCSDWifi: false); - } + WifiInfo? wifiInfo, Map? downloadJson, Map? uploadJson, bool isUCSDWifi) { + if (wifiInfo == null) return SpeedTestModel(isUCSDWifi: false); return SpeedTestModel( isUCSDWifi: isUCSDWifi, uploadUrl: uploadJson!["signed_url"], @@ -64,26 +59,19 @@ class SpeedTestModel { ipAddress: wifiInfo.ipAddress == null ? "" : wifiInfo.ipAddress, macAddress: wifiInfo.macAddress == null ? "" : wifiInfo.macAddress, linkSpeed: wifiInfo.linkSpeed == null ? "" : wifiInfo.linkSpeed, - signalStrength: - wifiInfo.signalStrength == null ? "" : wifiInfo.signalStrength, + signalStrength: wifiInfo.signalStrength == null ? "" : wifiInfo.signalStrength, frequency: wifiInfo.frequency == null ? "" : wifiInfo.frequency, networkID: wifiInfo.networkId == null ? "" : wifiInfo.networkId, - isHiddenSSID: - wifiInfo.isHiddenSSID == null ? "" : wifiInfo.isHiddenSSID, + isHiddenSSID: wifiInfo.isHiddenSSID == null ? "" : wifiInfo.isHiddenSSID, routerIP: wifiInfo.routerIp == null ? "" : wifiInfo.routerIp, channel: wifiInfo.channel == null ? "" : wifiInfo.channel, latitude: 0.0, longitude: 0.0, - timeStamp: DateTime.fromMillisecondsSinceEpoch( - DateTime.now().millisecondsSinceEpoch) - .toString(), + timeStamp: DateTime.fromMillisecondsSinceEpoch(DateTime.now().millisecondsSinceEpoch).toString(), downloadSpeed: 0.0, uploadSpeed: 0.0); } } -SpeedTestModel speedTestModelFromJson( - WifiInfo? wifiInfo, String downloadUrl, String uploadUrl, bool isUCSDWifi) { - return SpeedTestModel.fromJson( - wifiInfo, json.decode(downloadUrl), json.decode(uploadUrl), isUCSDWifi); -} +SpeedTestModel speedTestModelFromJson(WifiInfo? wifiInfo, String downloadUrl, String uploadUrl, bool isUCSDWifi) => + SpeedTestModel.fromJson(wifiInfo, json.decode(downloadUrl), json.decode(uploadUrl), isUCSDWifi); diff --git a/lib/core/models/spot_types.dart b/lib/core/models/spot_types.dart index 4e159d74f..180979082 100644 --- a/lib/core/models/spot_types.dart +++ b/lib/core/models/spot_types.dart @@ -1,7 +1,6 @@ import 'dart:convert'; -SpotTypeModel spotTypeModelFromJson(String str) => - SpotTypeModel.fromJson(json.decode(str)); +SpotTypeModel spotTypeModelFromJson(String str) => SpotTypeModel.fromJson(json.decode(str)); String spotTypeModelToJson(SpotTypeModel data) => json.encode(data.toJson()); @@ -13,15 +12,11 @@ class SpotTypeModel { }); factory SpotTypeModel.fromJson(Map json) => SpotTypeModel( - spots: json["spots"] == null - ? null - : List.from(json["spots"].map((x) => Spot.fromJson(x))), + spots: json["spots"] == null ? null : List.from(json["spots"].map((x) => Spot.fromJson(x))), ); Map toJson() => { - "spots": spots == null - ? null - : List.from(spots!.map((x) => x.toJson())), + "spots": spots == null ? null : List.from(spots!.map((x) => x.toJson())), }; } diff --git a/lib/core/models/student_id_name.dart b/lib/core/models/student_id_name.dart index bd48e4da3..ded7519f7 100644 --- a/lib/core/models/student_id_name.dart +++ b/lib/core/models/student_id_name.dart @@ -3,11 +3,9 @@ import 'dart:convert'; // To parse this JSON data, do // // final studentIdNameModel = studentIdNameModelFromJson(jsonString); -StudentIdNameModel studentIdNameModelFromJson(String str) => - StudentIdNameModel.fromJson(json.decode(str)); +StudentIdNameModel studentIdNameModelFromJson(String str) => StudentIdNameModel.fromJson(json.decode(str)); -String studentIdNameModelToJson(StudentIdNameModel data) => - json.encode(data.toJson()); +String studentIdNameModelToJson(StudentIdNameModel data) => json.encode(data.toJson()); class StudentIdNameModel { String studentId; @@ -28,8 +26,7 @@ class StudentIdNameModel { this.lastUpdatedDate = '', }); - factory StudentIdNameModel.fromJson(Map json) => - StudentIdNameModel( + factory StudentIdNameModel.fromJson(Map json) => StudentIdNameModel( studentId: json["studentId"], firstName: json["firstName"], middleName: json["middleName"], diff --git a/lib/core/models/student_id_photo.dart b/lib/core/models/student_id_photo.dart index 8cd241167..e7afd6d40 100644 --- a/lib/core/models/student_id_photo.dart +++ b/lib/core/models/student_id_photo.dart @@ -3,11 +3,9 @@ import 'dart:convert'; // To parse this JSON data, do // // final studentIdPhotoModel = studentIdPhotoModelFromJson(jsonString); -StudentIdPhotoModel studentIdPhotoModelFromJson(String str) => - StudentIdPhotoModel.fromJson(json.decode(str)); +StudentIdPhotoModel studentIdPhotoModelFromJson(String str) => StudentIdPhotoModel.fromJson(json.decode(str)); -String studentIdPhotoModelToJson(StudentIdPhotoModel data) => - json.encode(data.toJson()); +String studentIdPhotoModelToJson(StudentIdPhotoModel data) => json.encode(data.toJson()); class StudentIdPhotoModel { String studentId; @@ -16,9 +14,7 @@ class StudentIdPhotoModel { StudentIdPhotoModel({this.studentId = '', this.photoUrl = ''}); factory StudentIdPhotoModel.fromJson(Map json) => - StudentIdPhotoModel( - studentId: json["studentId"], photoUrl: json["photoUrl"]); + StudentIdPhotoModel(studentId: json["studentId"], photoUrl: json["photoUrl"]); - Map toJson() => - {"studentId": studentId, "photoUrl": photoUrl}; + Map toJson() => {"studentId": studentId, "photoUrl": photoUrl}; } diff --git a/lib/core/models/student_id_profile.dart b/lib/core/models/student_id_profile.dart index 490dc0d0e..fccbfaf4a 100644 --- a/lib/core/models/student_id_profile.dart +++ b/lib/core/models/student_id_profile.dart @@ -3,11 +3,9 @@ import 'dart:convert'; // To parse this JSON data, do // // final studentIdProfileModel = studentIdProfileModelFromJson(jsonString); -StudentIdProfileModel studentIdProfileModelFromJson(String str) => - StudentIdProfileModel.fromJson(json.decode(str)); +StudentIdProfileModel studentIdProfileModelFromJson(String str) => StudentIdProfileModel.fromJson(json.decode(str)); -String studentIdProfileModelToJson(StudentIdProfileModel data) => - json.encode(data.toJson()); +String studentIdProfileModelToJson(StudentIdProfileModel data) => json.encode(data.toJson()); class StudentIdProfileModel { String studentPid; @@ -35,27 +33,23 @@ class StudentIdProfileModel { this.classificationType = '', this.issueNumber = 0}); - factory StudentIdProfileModel.fromJson(Map json) => - StudentIdProfileModel( - studentPid: json["Student_PID"], - termYear: json["Term_Year"], - studentLevelCurrent: json["Student_Level_Current"] == null - ? "" - : json["Student_Level_Current"], - collegeCurrent: - json["College_Current"] == null ? "" : json["College_Current"], - ugPrimaryMajorCurrent: json["UG_Primary_Major_Current"] == null - ? "" - : json["UG_Primary_Major_Current"], - graduatePrimaryMajorCurrent: - json["Graduate_Primary_Major_Current"] == null - ? "" - : json["Graduate_Primary_Major_Current"], - athleteCurrentCount: json["Athlete_Current_Count"], - cardNumber: json["Card_Number"], - barcode: json["Barcode"], - classificationType: json["Classification_Type"], - issueNumber: json["Issue_Number"]); + factory StudentIdProfileModel.fromJson(Map json) => StudentIdProfileModel( + studentPid: json["Student_PID"], + termYear: json["Term_Year"], + studentLevelCurrent: json["Student_Level_Current"] == null ? "" : json["Student_Level_Current"], + collegeCurrent: json["College_Current"] == null ? "" : json["College_Current"], + ugPrimaryMajorCurrent: json["UG_Primary_Major_Current"] == null ? "" : json["UG_Primary_Major_Current"], + graduatePrimaryMajorCurrent: + json["Graduate_Primary_Major_Current"] == null ? "" : json["Graduate_Primary_Major_Current"], + athleteCurrentCount: json["Athlete_Current_Count"] is int + ? json["Athlete_Current_Count"] + : int.tryParse(json["Athlete_Current_Count"]?.toString() ?? "0") ?? 0, + cardNumber: json["Card_Number"], + barcode: json["Barcode"], + classificationType: json["Classification_Type"], + issueNumber: json["Issue_Number"] is int + ? json["Issue_Number"] + : int.tryParse(json["Issue_Number"]?.toString() ?? "0") ?? 0); Map toJson() => { "Student_PID": studentPid, diff --git a/lib/core/models/term.dart b/lib/core/models/term.dart index 5b3ff0a65..405e949f1 100644 --- a/lib/core/models/term.dart +++ b/lib/core/models/term.dart @@ -3,11 +3,9 @@ import 'dart:convert'; // To parse this JSON data, do // // final academicTermModel = academicTermModelFromJson(jsonString); -AcademicTermModel academicTermModelFromJson(String str) => - AcademicTermModel.fromJson(json.decode(str)); +AcademicTermModel academicTermModelFromJson(String str) => AcademicTermModel.fromJson(json.decode(str)); -String academicTermModelToJson(AcademicTermModel data) => - json.encode(data.toJson()); +String academicTermModelToJson(AcademicTermModel data) => json.encode(data.toJson()); class AcademicTermModel { String? termName; @@ -18,8 +16,7 @@ class AcademicTermModel { this.termCode, }); - factory AcademicTermModel.fromJson(Map json) => - AcademicTermModel( + factory AcademicTermModel.fromJson(Map json) => AcademicTermModel( termName: json["term_name"], termCode: json["term_code"], ); diff --git a/lib/core/models/topics.dart b/lib/core/models/topics.dart index 30610e93a..f6184f44f 100644 --- a/lib/core/models/topics.dart +++ b/lib/core/models/topics.dart @@ -3,11 +3,10 @@ import 'dart:convert'; // To parse this JSON data, do // // final topicsModel = topicsModelFromJson(jsonString); -List topicsModelFromJson(String str) => List.from( - json.decode(str).map((x) => TopicsModel.fromJson(x))); +List topicsModelFromJson(String str) => + List.from(json.decode(str).map((x) => TopicsModel.fromJson(x))); -String topicsModelToJson(List data) => - json.encode(List.from(data.map((x) => x.toJson()))); +String topicsModelToJson(List data) => json.encode(List.from(data.map((x) => x.toJson()))); class TopicsModel { String? audienceId; @@ -20,16 +19,12 @@ class TopicsModel { factory TopicsModel.fromJson(Map json) => TopicsModel( audienceId: json["audienceId"] == null ? null : json["audienceId"], - topics: json["topics"] == null - ? null - : List.from(json["topics"].map((x) => Topic.fromJson(x))), + topics: json["topics"] == null ? null : List.from(json["topics"].map((x) => Topic.fromJson(x))), ); Map toJson() => { "audienceId": audienceId == null ? null : audienceId, - "topics": topics == null - ? null - : List.from(topics!.map((x) => x.toJson())), + "topics": topics == null ? null : List.from(topics!.map((x) => x.toJson())), }; } @@ -44,9 +39,7 @@ class Topic { factory Topic.fromJson(Map json) => Topic( topicId: json["topicId"] == null ? null : json["topicId"], - topicMetadata: json["topicMetadata"] == null - ? null - : TopicMetadata.fromJson(json["topicMetadata"]), + topicMetadata: json["topicMetadata"] == null ? null : TopicMetadata.fromJson(json["topicMetadata"]), ); Map toJson() => { diff --git a/lib/core/models/user_profile.dart b/lib/core/models/user_profile.dart index 2c0588a7f..7480aaeee 100644 --- a/lib/core/models/user_profile.dart +++ b/lib/core/models/user_profile.dart @@ -5,18 +5,16 @@ part 'user_profile.g.dart'; // To parse this JSON data, do // // final userProfileModel = userProfileModelFromJson(jsonString); -UserProfileModel userProfileModelFromJson(String str) => - UserProfileModel.fromJson(json.decode(str)); +UserProfileModel userProfileModelFromJson(String str) => UserProfileModel.fromJson(json.decode(str)); -String userProfileModelToJson(UserProfileModel data) => - json.encode(data.toJson()); +String userProfileModelToJson(UserProfileModel data) => json.encode(data.toJson()); @HiveType(typeId: 2) class UserProfileModel extends HiveObject { Classifications? classifications; int? latestTimeStamp; String? pid; - String? ucsdaffiliation; + String? ucsdAffiliation; String? username; @HiveField(0) @@ -43,7 +41,7 @@ class UserProfileModel extends HiveObject { this.selectedLots, this.selectedOccuspaceLocations, this.subscribedTopics, - this.ucsdaffiliation, + this.ucsdAffiliation, this.username, this.selectedParkingSpots, this.selectedParkingLots, @@ -51,78 +49,50 @@ class UserProfileModel extends HiveObject { this.surveyCompletion, this.selectedVentilationLocations}); - factory UserProfileModel.fromJson(Map json) => - UserProfileModel( - classifications: json["classifications"] == null - ? null - : Classifications.fromJson(json["classifications"]), - latestTimeStamp: - json["latestTimeStamp"] == null ? null : json["latestTimeStamp"], + factory UserProfileModel.fromJson(Map json) => UserProfileModel( + classifications: json["classifications"] == null ? null : Classifications.fromJson(json["classifications"]), + latestTimeStamp: json["latestTimeStamp"] == null ? null : json["latestTimeStamp"], pid: json["pid"] == null ? null : json["pid"], - selectedLots: json["selectedLots"] == null - ? [] - : List.from(json["selectedLots"].map((x) => x)), + selectedLots: json["selectedLots"] == null ? [] : List.from(json["selectedLots"].map((x) => x)), selectedOccuspaceLocations: json["selectedOccuspaceLocations"] == null ? [] - : List.from( - json["selectedOccuspaceLocations"].map((x) => x)), - subscribedTopics: json["subscribedTopics"] == null - ? [] - : List.from(json["subscribedTopics"].map((x) => x)), - ucsdaffiliation: - json["ucsdaffiliation"] == null ? null : json["ucsdaffiliation"], + : List.from(json["selectedOccuspaceLocations"].map((x) => x)), + subscribedTopics: + json["subscribedTopics"] == null ? [] : List.from(json["subscribedTopics"].map((x) => x)), + ucsdAffiliation: json["ucsdaffiliation"] == null ? null : json["ucsdaffiliation"], username: json["username"] == null ? null : json["username"], selectedParkingLots: json["selectedParkingLots"] == null ? Map() - : Map.from(json["selectedParkingLots"] - .map((x, y) => MapEntry(x, y))), + : Map.from(json["selectedParkingLots"].map((x, y) => MapEntry(x, y))), selectedParkingSpots: json["selectedParkingSpots"] == null ? Map() - : Map.from(json["selectedParkingSpots"] - .map((x, y) => MapEntry(x, y))), - selectedStops: json["selectedStops"] == null + : Map.from(json["selectedParkingSpots"].map((x, y) => MapEntry(x, y))), + selectedStops: json["selectedStops"] == null ? [] : List.from(json["selectedStops"].map((x) => x)), + surveyCompletion: + json["surveyCompletion"] == null ? [] : List.from(json["surveyCompletion"].map((x) => x)), + selectedVentilationLocations: json['selectedVentilationLocations'] == null ? [] - : List.from(json["selectedStops"].map((x) => x)), - surveyCompletion: json["surveyCompletion"] == null - ? [] - : List.from(json["surveyCompletion"].map((x) => x)), - selectedVentilationLocations: - json['selectedVentilationLocations'] == null - ? [] - : List.from( - json["selectedVentilationLocations"].map((x) => x)), + : List.from(json["selectedVentilationLocations"].map((x) => x)), ); Map toJson() => { - "classifications": - classifications == null ? null : classifications!.toJson(), + "classifications": classifications == null ? null : classifications!.toJson(), "latestTimeStamp": latestTimeStamp == null ? null : latestTimeStamp, "pid": pid == null ? null : pid, - "selectedLots": selectedLots == null - ? null - : List.from(selectedLots!.map((x) => x)), - "selectedOccuspaceLocations": selectedOccuspaceLocations == null - ? null - : List.from(selectedOccuspaceLocations!.map((x) => x)), - "subscribedTopics": subscribedTopics == null - ? null - : List.from(subscribedTopics!.map((x) => x)), - "ucsdaffiliation": ucsdaffiliation == null ? null : ucsdaffiliation, + "selectedLots": selectedLots == null ? null : List.from(selectedLots!.map((x) => x)), + "selectedOccuspaceLocations": + selectedOccuspaceLocations == null ? null : List.from(selectedOccuspaceLocations!.map((x) => x)), + "subscribedTopics": subscribedTopics == null ? null : List.from(subscribedTopics!.map((x) => x)), + "ucsdaffiliation": ucsdAffiliation == null ? null : ucsdAffiliation, "username": username == null ? null : username, "selectedParkingLots": selectedParkingLots == null ? null - : Map.from(selectedParkingLots! - .map((x, y) => MapEntry(x, y))), + : Map.from(selectedParkingLots!.map((x, y) => MapEntry(x, y))), "selectedParkingSpots": selectedParkingSpots == null ? null - : Map.from(selectedParkingSpots! - .map((x, y) => MapEntry(x, y))), - "selectedStops": selectedStops == null - ? null - : List.from(selectedStops!.map((x) => x)), - "surveyCompletion": surveyCompletion == null - ? null - : List.from(surveyCompletion!.map((x) => x)), + : Map.from(selectedParkingSpots!.map((x, y) => MapEntry(x, y))), + "selectedStops": selectedStops == null ? null : List.from(selectedStops!.map((x) => x)), + "surveyCompletion": surveyCompletion == null ? null : List.from(surveyCompletion!.map((x) => x)), "selectedVentilationLocations": selectedVentilationLocations == null ? null : List.from(selectedVentilationLocations!.map((x) => x)), @@ -138,8 +108,7 @@ class Classifications { this.staff, }); - factory Classifications.fromJson(Map json) => - Classifications( + factory Classifications.fromJson(Map json) => Classifications( student: json["student"] == null ? null : json["student"], staff: json["staff"] == null ? null : json["staff"], ); diff --git a/lib/core/models/user_profile.g.dart b/lib/core/models/user_profile.g.dart index c7a10563e..54c012f96 100644 --- a/lib/core/models/user_profile.g.dart +++ b/lib/core/models/user_profile.g.dart @@ -52,7 +52,5 @@ class UserProfileModelAdapter extends TypeAdapter { @override bool operator ==(Object other) => identical(this, other) || - other is UserProfileModelAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; + other is UserProfileModelAdapter && runtimeType == other.runtimeType && typeId == other.typeId; } diff --git a/lib/core/providers/availability.dart b/lib/core/providers/availability.dart index 9348cf23e..bbaffbd4f 100644 --- a/lib/core/providers/availability.dart +++ b/lib/core/providers/availability.dart @@ -32,21 +32,18 @@ class AvailabilityDataProvider extends ChangeNotifier { for (AvailabilityModel model in _availabilityService.data) { String curName = model.name; RegExpMatch? match = RegExp(r' \(\d+/\d+\)$').firstMatch(curName); - if (match != null) - curName = curName.replaceRange(match.start, match.end, ''); + if (match != null) curName = curName.replaceRange(match.start, match.end, ''); newMapOfLots[model.name] = model; /// if the user is logged out and has not put any preferences, /// show all locations by default - if (userDataProvider - .userProfileModel.selectedOccuspaceLocations!.isEmpty) + final bool hasNoSelectedLocations = userDataProvider.userProfileModel.selectedOccuspaceLocations!.isEmpty; + if (hasNoSelectedLocations) _locationViewState[curName] = true; /// otherwise, LocationViewState should be true for all selectedOccuspaceLocations else { - _locationViewState[curName] = userDataProvider - .userProfileModel.selectedOccuspaceLocations! - .contains(curName); + _locationViewState[curName] = userDataProvider.userProfileModel.selectedOccuspaceLocations!.contains(curName); } } @@ -54,8 +51,7 @@ class AvailabilityDataProvider extends ChangeNotifier { _availabilityModels = newMapOfLots; /// if the user is logged in we want to sync the order of parking lots amongst all devices - reorderLocations( - userDataProvider.userProfileModel.selectedOccuspaceLocations); + reorderLocations(userDataProvider.userProfileModel.selectedOccuspaceLocations); _lastUpdated = DateTime.now(); } else { _error = _availabilityService.error; @@ -113,10 +109,9 @@ class AvailabilityDataProvider extends ChangeNotifier { String? get error => _error; DateTime? get lastUpdated => _lastUpdated; Map get locationViewState => _locationViewState; - List get availabilityModels => makeOrderedList( - userDataProvider.userProfileModel.selectedOccuspaceLocations); + List get availabilityModels => + makeOrderedList(userDataProvider.userProfileModel.selectedOccuspaceLocations); /// get all locations - List locations() => - _availabilityModels.values.map((model) => model.name).toList(); + List locations() => _availabilityModels.values.map((model) => model.name).toList(); } diff --git a/lib/core/providers/cards.dart b/lib/core/providers/cards.dart index b3e263501..f29f06d2e 100644 --- a/lib/core/providers/cards.dart +++ b/lib/core/providers/cards.dart @@ -9,14 +9,13 @@ import 'package:hive/hive.dart'; class CardsDataProvider extends ChangeNotifier { CardsDataProvider() { - CardTitleConstants.titleMap.keys - .forEach((card) => _cardStates[card] = true); + CardTitleConstants.TITLE_MAP.keys.forEach((card) => _cardStates[card] = true); /// temporary fix that prevents the student cards from causing issues on launch - _cardOrder.removeWhere((element) => _studentCards.contains(element)); - _cardStates.removeWhere((key, value) => _studentCards.contains(key)); - _cardOrder.removeWhere((element) => _staffCards.contains(element)); - _cardStates.removeWhere((key, value) => _staffCards.contains(key)); + _cardOrder.removeWhere((element) => _STUDENT_CARDS.contains(element)); + _cardStates.removeWhere((key, value) => _STUDENT_CARDS.contains(key)); + _cardOrder.removeWhere((element) => _STAFF_CARDS.contains(element)); + _cardStates.removeWhere((key, value) => _STAFF_CARDS.contains(key)); } /// STATES @@ -44,8 +43,8 @@ class CardsDataProvider extends ChangeNotifier { List _cardOrder = [ 'student_id', 'employee_id', - 'MyStudentChart', - 'MyUCSDChart', + 'my_student_chart', + 'my_ucsd_chart', 'finals', 'schedule', 'availability', @@ -58,15 +57,15 @@ class CardsDataProvider extends ChangeNotifier { ]; // Native student cards - static const List _studentCards = [ + static const List _STUDENT_CARDS = [ 'finals', 'schedule', 'student_id', ]; // Native staff cards - static const List _staffCards = [ - 'MyUCSDChart', + static const List _STAFF_CARDS = [ + 'my_ucsd_chart', 'staff_info', 'employee_id', ]; @@ -85,14 +84,16 @@ class CardsDataProvider extends ChangeNotifier { // add new cards to the top of the list _availableCards.forEach((card, model) { - if (_studentCards.contains(model) || _staffCards.contains(model)) - return; + final bool isStudentCard = _STUDENT_CARDS.contains(model); + final bool isStaffCard = _STAFF_CARDS.contains(model); + if (isStudentCard || isStaffCard) return; - // add active webcards + // add active web cards if (model.isWebCard) _webCards[card] = model; - if (!_cardOrder.contains(model) && model.cardActive) - _cardOrder.add(card); + final bool isNewCard = !_cardOrder.contains(model); + final bool isActiveCard = model.cardActive; + if (isNewCard && isActiveCard) _cardOrder.add(card); // keep all new cards activated by default _cardStates.putIfAbsent(card, () => true); @@ -137,13 +138,15 @@ class CardsDataProvider extends ChangeNotifier { /// Update the [_cardOrder] stored in state /// overwrite the [_cardOrder] in persistent storage with the model passed in Future updateCardOrder() async { - if (_userDataProvider == null || _userDataProvider!.isInSilentLogin) return; + final bool isUserProviderNull = _userDataProvider == null; + final bool isInSilentLogin = _userDataProvider?.isInSilentLogin ?? false; + if (isUserProviderNull || isInSilentLogin) return; // checks if box is open, creates one if not - _cardOrderBox = await Hive.openBox(DataPersistence.cardOrder); + _cardOrderBox = await Hive.openBox(DataPersistence.CARD_ORDER); // no need to await - data is saved to disk in background - _cardOrderBox.put(DataPersistence.cardOrder, _cardOrder); + _cardOrderBox.put(DataPersistence.CARD_ORDER, _cardOrder); _lastUpdated = DateTime.now(); notifyListeners(); @@ -152,14 +155,16 @@ class CardsDataProvider extends ChangeNotifier { /// Load [_cardOrder] from persistent storage /// Will create persistent storage if no data is found Future _loadCardOrder() async { - if (_userDataProvider == null || _userDataProvider!.isInSilentLogin) return; + final bool isUserProviderNull = _userDataProvider == null; + final bool isInSilentLogin = _userDataProvider?.isInSilentLogin ?? false; + if (isUserProviderNull || isInSilentLogin) return; - _cardOrderBox = await Hive.openBox(DataPersistence.cardOrder); + _cardOrderBox = await Hive.openBox(DataPersistence.CARD_ORDER); - if (_cardOrderBox.get(DataPersistence.cardOrder) == null) - await _cardOrderBox.put(DataPersistence.cardOrder, _cardOrder); + if (_cardOrderBox.get(DataPersistence.CARD_ORDER) == null) + await _cardOrderBox.put(DataPersistence.CARD_ORDER, _cardOrder); else - _cardOrder = _cardOrderBox.get(DataPersistence.cardOrder); + _cardOrder = _cardOrderBox.get(DataPersistence.CARD_ORDER); notifyListeners(); } @@ -167,17 +172,17 @@ class CardsDataProvider extends ChangeNotifier { /// Load [_cardStates] from persistent storage /// Will create persistent storage if no data is found Future _loadCardStates() async { - _cardStateBox = await Hive.openBox(DataPersistence.cardStates); + _cardStateBox = await Hive.openBox(DataPersistence.CARD_STATES); // if no data was found then create the data and save it // by default all cards will be on - if (_cardStateBox.get(DataPersistence.cardStates) == null) { - await _cardStateBox.put(DataPersistence.cardStates, - _cardStates.keys.where((card) => _cardStates[card]!).toList()); + if (_cardStateBox.get(DataPersistence.CARD_STATES) == null) { + await _cardStateBox.put( + DataPersistence.CARD_STATES, _cardStates.keys.where((card) => _cardStates[card]!).toList()); } else { _deactivateAllCards(); } - for (String activeCard in _cardStateBox.get(DataPersistence.cardStates)) { + for (String activeCard in _cardStateBox.get(DataPersistence.CARD_STATES)) { _cardStates[activeCard] = true; } @@ -186,16 +191,17 @@ class CardsDataProvider extends ChangeNotifier { /// Update the [_cardStates] stored on disk Future updateCardStates() async { - if (_userDataProvider == null || _userDataProvider!.isInSilentLogin) return; + final bool isUserProviderNull = _userDataProvider == null; + final bool isInSilentLogin = _userDataProvider?.isInSilentLogin ?? false; + if (isUserProviderNull || isInSilentLogin) return; - var activeCards = - _cardStates.keys.where((card) => _cardStates[card]!).toList(); + var activeCards = _cardStates.keys.where((card) => _cardStates[card]!).toList(); // checks if box is open, creates one if not - _cardStateBox = await Hive.openBox(DataPersistence.cardStates); + _cardStateBox = await Hive.openBox(DataPersistence.CARD_STATES); // no need to await - data is saved to disk in background - _cardStateBox.put(DataPersistence.cardStates, activeCards); + _cardStateBox.put(DataPersistence.CARD_STATES, activeCards); _lastUpdated = DateTime.now(); notifyListeners(); @@ -208,10 +214,10 @@ class CardsDataProvider extends ChangeNotifier { } void activateStudentCards() { - var index = _cardOrder.indexOf('MyStudentChart') + 1; - _cardOrder.insertAll(index, _studentCards.toList()); + var index = _cardOrder.indexOf('my_student_chart') + 1; + _cardOrder.insertAll(index, _STUDENT_CARDS.toList()); - // TODO: test w/o this + // TODO: test w/o this - December 2025 _cardOrder = List.from(_cardOrder.toSet().toList()); updateCardOrder(); @@ -219,13 +225,13 @@ class CardsDataProvider extends ChangeNotifier { } void showAllStudentCards() { - var index = _cardOrder.indexOf('MyStudentChart') + 1; - _cardOrder.insertAll(index, _studentCards.toList()); + var index = _cardOrder.indexOf('my_student_chart') + 1; + _cardOrder.insertAll(index, _STUDENT_CARDS.toList()); - // TODO: test w/o this + // TODO: test w/o this - December 2025 _cardOrder = List.from(_cardOrder.toSet().toList()); - for (String card in _studentCards) { + for (String card in _STUDENT_CARDS) { _cardStates[card] = true; } @@ -234,7 +240,7 @@ class CardsDataProvider extends ChangeNotifier { } void deactivateStudentCards() { - for (String card in _studentCards) { + for (String card in _STUDENT_CARDS) { _cardOrder.remove(card); _cardStates[card] = false; } @@ -243,23 +249,23 @@ class CardsDataProvider extends ChangeNotifier { } void activateStaffCards() { - var index = _cardOrder.indexOf('MyStudentChart') + 1; - _cardOrder.insertAll(index, _staffCards.toList()); + var index = _cardOrder.indexOf('my_student_chart') + 1; + _cardOrder.insertAll(index, _STAFF_CARDS.toList()); - // TODO: test w/o this + // TODO: test w/o this - December 2025 _cardOrder = List.from(_cardOrder.toSet().toList()); updateCardOrder(); updateCardStates(); } void showAllStaffCards() { - var index = _cardOrder.indexOf('MyStudentChart') + 1; - _cardOrder.insertAll(index, _staffCards.toList()); + var index = _cardOrder.indexOf('my_student_chart') + 1; + _cardOrder.insertAll(index, _STAFF_CARDS.toList()); - // TODO: test w/o this + // TODO: test w/o this - December 2025 _cardOrder = List.from(_cardOrder.toSet().toList()); - for (String card in _staffCards) { + for (String card in _STAFF_CARDS) { _cardStates[card] = true; } updateCardOrder(); @@ -267,7 +273,7 @@ class CardsDataProvider extends ChangeNotifier { } void deactivateStaffCards() { - for (String card in _staffCards) { + for (String card in _STAFF_CARDS) { _cardOrder.remove(card); _cardStates[card] = false; } @@ -277,8 +283,9 @@ class CardsDataProvider extends ChangeNotifier { void toggleCard(String card) { try { - if (_availableCards[card]!.isWebCard && _cardStates[card]!) - resetCardHeight(card); + final bool isWebCard = _availableCards[card]!.isWebCard; + final bool isCardActive = _cardStates[card]!; + if (isWebCard && isCardActive) resetCardHeight(card); // Toggle the card state _cardStates[card] = !_cardStates[card]!; diff --git a/lib/core/providers/classes.dart b/lib/core/providers/classes.dart index 1aa63d509..73997a10d 100644 --- a/lib/core/providers/classes.dart +++ b/lib/core/providers/classes.dart @@ -52,31 +52,30 @@ class ClassScheduleDataProvider extends ChangeNotifier { _isLoading = true; _error = null; notifyListeners(); - if (await _classScheduleService.fetchAcademicTerm() && - _userDataProvider.isLoggedIn) { + final bool termFetched = await _classScheduleService.fetchAcademicTerm(); + final bool isLoggedIn = _userDataProvider.isLoggedIn; + if (termFetched && isLoggedIn) { _academicTermModel = _classScheduleService.academicTermModel!; final Map headers = { - 'Authorization': - 'Bearer ${_userDataProvider.authenticationModel.accessToken}' + 'Authorization': 'Bearer ${_userDataProvider.authenticationModel.accessToken}' }; /// erase old model _classScheduleModel = ClassScheduleModel(); /// fetch grad courses - if (await _classScheduleService.fetchGRCourses( - headers, _academicTermModel.termCode!)) { + final bool grCoursesFetched = await _classScheduleService.fetchGRCourses(headers, _academicTermModel.termCode!); + if (grCoursesFetched) { _classScheduleModel = _classScheduleService.grData; } else { _error = _classScheduleService.error.toString(); } /// fetch undergrad courses - if (await _classScheduleService.fetchUNCourses( - headers, _academicTermModel.termCode!)) { + final bool unCoursesFetched = await _classScheduleService.fetchUNCourses(headers, _academicTermModel.termCode!); + if (unCoursesFetched) { if (_classScheduleModel.data != null) { - _classScheduleModel.data! - .addAll(_classScheduleService.unData.data!); + _classScheduleModel.data!.addAll(_classScheduleService.unData.data!); } else { _classScheduleModel = _classScheduleService.unData; } @@ -126,7 +125,7 @@ class ClassScheduleDataProvider extends ChangeNotifier { _lastUpdated = DateTime.now(); } else { - ///TODO: determine what error to show to the user + // TODO: determine what error to show to the user - December 2025 _error = _classScheduleService.error; } _isLoading = false; @@ -154,15 +153,15 @@ class ClassScheduleDataProvider extends ChangeNotifier { sectionData.subjectCode = classData.subjectCode; sectionData.courseCode = classData.courseCode; sectionData.courseTitle = classData.courseTitle; - sectionData.gradeOption = - buildGradeEvaluation(classData.gradeOption ?? ""); + sectionData.gradeOption = buildGradeEvaluation(classData.gradeOption ?? ""); String day = 'OTHER'; if (sectionData.days != null) { day = sectionData.days!; - if (sectionData.specialMtgCode != 'FI' && - sectionData.specialMtgCode != 'MI') { + final bool isNotFinals = sectionData.specialMtgCode != 'FI'; + final bool isNotMidterm = sectionData.specialMtgCode != 'MI'; + if (isNotFinals && isNotMidterm) { _enrolledClasses[day]!.add(sectionData); } else if (sectionData.specialMtgCode == 'FI') { _finals[day]!.add(sectionData); @@ -196,7 +195,9 @@ class ClassScheduleDataProvider extends ChangeNotifier { /// comparator that sorts according to start time of class static int _compare(SectionData a, SectionData b) { - if (a.time == null || b.time == null) return 0; + final bool aTimeIsNull = a.time == null; + final bool bTimeIsNull = b.time == null; + if (aTimeIsNull || bTimeIsNull) return 0; DateTime aStartTime = _getStartTime(a.time!); DateTime bStartTime = _getStartTime(b.time!); @@ -233,11 +234,7 @@ class ClassScheduleDataProvider extends ChangeNotifier { try { /// get weekday and return [List] associated with current weekday List listToReturn = []; - String today = DateFormat('EEEE') - .format(DateTime.now()) - .toString() - .toUpperCase() - .substring(0, 2); + String today = DateFormat('EEEE').format(DateTime.now()).toString().toUpperCase().substring(0, 2); nextDayWithClass = DateFormat('EEEE').format(DateTime.now()).toString(); /// if no classes are scheduled for today then find the next day with classes @@ -248,8 +245,7 @@ class ClassScheduleDataProvider extends ChangeNotifier { .toString() .toUpperCase() .substring(0, 2); - nextDayWithClass = DateFormat('EEEE') - .format(DateTime.now().add(Duration(days: daysToAdd))); + nextDayWithClass = DateFormat('EEEE').format(DateTime.now().add(Duration(days: daysToAdd))); daysToAdd += 1; } diff --git a/lib/core/providers/connectivity.dart b/lib/core/providers/connectivity.dart index 728b0a313..9f91bfe8b 100644 --- a/lib/core/providers/connectivity.dart +++ b/lib/core/providers/connectivity.dart @@ -49,8 +49,8 @@ class InternetConnectivityProvider extends ChangeNotifier { AlertDialogWidget alert = AlertDialogWidget( type: MessageTypeConstants.ERROR, icon: Icons.block_flipped, - title: ConnectivityConstants.offlineTitle, - description: ConnectivityConstants.offlineAlert, + title: ConnectivityConstants.OFFLINE_TITLE, + description: ConnectivityConstants.OFFLINE_ALERT, onClose: () { Navigator.of(context).pop(); }, diff --git a/lib/core/providers/dining.dart b/lib/core/providers/dining.dart index daacd48c3..9dbeaa398 100644 --- a/lib/core/providers/dining.dart +++ b/lib/core/providers/dining.dart @@ -10,8 +10,7 @@ enum Meal { breakfast, lunch, dinner } class DiningDataProvider extends ChangeNotifier { // Set all filter types to true by default DiningDataProvider() { - DiningConstants.payment_filter_types - .forEach((type) => _diningFilterTypeStates[type] = true); + DiningConstants.PAYMENT_FILTER_TYPES.forEach((type) => _diningFilterTypeStates[type] = true); } /// STATES @@ -25,8 +24,7 @@ class DiningDataProvider extends ChangeNotifier { /// MODELS Map _diningModels = {}; // Source of truth - Map _filteredDiningModels = - {}; // Used for displaying filtered results + Map _filteredDiningModels = {}; // Used for displaying filtered results /// SERVICES var _diningService = DiningService(); @@ -97,25 +95,23 @@ class DiningDataProvider extends ChangeNotifier { if (_coordinates == null) return _diningModels.values.toList(); List orderedListOfLots = _diningModels.values.toList(); orderedListOfLots.sort((DiningModel a, DiningModel b) { - if (a.distance != null && b.distance != null) { - return a.distance!.compareTo(b.distance!); - } + final bool aHasDistance = a.distance != null; + final bool bHasDistance = b.distance != null; + if (aHasDistance && bHasDistance) return a.distance!.compareTo(b.distance!); return 0; }); return orderedListOfLots; } void populateDistances() { - if (_coordinates != null && - _coordinates!.lat != null && - _coordinates!.lon != null) { + final bool hasCoordinates = _coordinates != null; + final bool hasLatitude = _coordinates?.lat != null; + final bool hasLongitude = _coordinates?.lon != null; + if (hasCoordinates && hasLatitude && hasLongitude) { for (DiningModel model in _diningModels.values.toList()) { if (model.coordinates != null) { var distance = calculateDistance( - _coordinates!.lat!, - _coordinates!.lon!, - model.coordinates!.lat!, - model.coordinates!.lon!); + _coordinates!.lat!, _coordinates!.lon!, model.coordinates!.lat!, model.coordinates!.lon!); model.distance = distance.toDouble(); } else { model.distance = null; @@ -127,9 +123,7 @@ class DiningDataProvider extends ChangeNotifier { num calculateDistance(double lat1, double lng1, double lat2, double lng2) { var p = 0.017453292519943295; var c = cos; - var a = 0.5 - - c((lat2 - lat1) * p) / 2 + - c(lat1 * p) * c(lat2 * p) * (1 - c((lng2 - lng1) * p)) / 2; + var a = 0.5 - c((lat2 - lat1) * p) / 2 + c(lat1 * p) * c(lat2 * p) * (1 - c((lng2 - lng1) * p)) / 2; return 12742 * asin(sqrt(a)) * 0.621371; } @@ -147,10 +141,9 @@ class DiningDataProvider extends ChangeNotifier { /// RETURNS A List filtered by the selected filter types List get filteredDiningModels { // If all or no filters are selected, then return diningModels (the source of truth) - if (!_diningFilterTypeStates.values.contains(true) || - _diningFilterTypeStates.values.every((f) => f)) { - return diningModels; - } + final bool noFiltersSelected = !_diningFilterTypeStates.values.contains(true); + final bool allFiltersSelected = _diningFilterTypeStates.values.every((f) => f); + if (noFiltersSelected || allFiltersSelected) return diningModels; // Else, return the updated filtered list return _filteredDiningModels.values.toList(); } diff --git a/lib/core/providers/employee_id.dart b/lib/core/providers/employee_id.dart index 31e0b1fc6..3b9ce3810 100644 --- a/lib/core/providers/employee_id.dart +++ b/lib/core/providers/employee_id.dart @@ -23,14 +23,12 @@ class EmployeeIdDataProvider extends ChangeNotifier { _error = null; notifyListeners(); - final Map header = { - 'Authorization': - 'Bearer ${_userDataProvider.authenticationModel.accessToken}' - }; + final Map header = {'Authorization': 'Bearer ${_userDataProvider.authenticationModel.accessToken}'}; /// Verify that user is logged in - if (_userDataProvider.isLoggedIn && - await _employeeIdService.fetchEmployeeIdProfile(header)) + final bool isLoggedIn = _userDataProvider.isLoggedIn; + final bool profileFetched = await _employeeIdService.fetchEmployeeIdProfile(header); + if (isLoggedIn && profileFetched) _employeeIdModel = _employeeIdService.employeeIdModel; // Fetch Profile else _error = _employeeIdService.error.toString(); diff --git a/lib/core/providers/location.dart b/lib/core/providers/location.dart index e75c43bf6..bc6ab1af0 100644 --- a/lib/core/providers/location.dart +++ b/lib/core/providers/location.dart @@ -48,8 +48,7 @@ class LocationDataProvider extends ChangeNotifier { if (permission == LocationPermission.deniedForever) { // Permissions are denied forever, handle appropriately. - return Future.error( - 'Location permissions are permanently denied, we cannot request permissions.'); + return Future.error('Location permissions are permanently denied, we cannot request permissions.'); } // When we reach here, permissions are granted and we can @@ -59,11 +58,9 @@ class LocationDataProvider extends ChangeNotifier { } void _enableListener() { - Geolocator.getPositionStream(locationSettings: locationSettings) - .listen((Position? position) { - if (position == null) error = ErrorConstants.locationFailed; - _locationController - .add(Coordinates(lat: position?.latitude, lon: position?.longitude)); + Geolocator.getPositionStream(locationSettings: locationSettings).listen((Position? position) { + if (position == null) error = ErrorConstants.LOCATION_FAILED; + _locationController.add(Coordinates(lat: position?.latitude, lon: position?.longitude)); }); } diff --git a/lib/core/providers/map.dart b/lib/core/providers/map.dart index 1456b9796..52d633cc1 100644 --- a/lib/core/providers/map.dart +++ b/lib/core/providers/map.dart @@ -41,11 +41,9 @@ class MapsDataProvider extends ChangeNotifier { void addMarker(int listIndex) { final Marker marker = Marker( markerId: MarkerId(_mapSearchModels[listIndex].mkrMarkerid.toString()), - position: LatLng(_mapSearchModels[listIndex].mkrLat!, - _mapSearchModels[listIndex].mkrLong!), - infoWindow: InfoWindow( - title: _mapSearchModels[listIndex].title, - snippet: _mapSearchModels[listIndex].description), + position: LatLng(_mapSearchModels[listIndex].mkrLat!, _mapSearchModels[listIndex].mkrLong!), + infoWindow: + InfoWindow(title: _mapSearchModels[listIndex].title, snippet: _mapSearchModels[listIndex].description), ); _markers.clear(); _markers[marker.markerId] = marker; @@ -55,15 +53,13 @@ class MapsDataProvider extends ChangeNotifier { } void updateMapPosition() { - if (_markers.isNotEmpty && _mapController != null) { - _mapController! - .animateCamera( - CameraUpdate.newLatLng(_markers.values.toList()[0].position)) - .then((_) async { + final bool hasMarkers = _markers.isNotEmpty; + final bool hasController = _mapController != null; + if (hasMarkers && hasController) { + _mapController!.animateCamera(CameraUpdate.newLatLng(_markers.values.toList()[0].position)).then((_) async { await Future.delayed(Duration(seconds: 1)); try { - _mapController! - .showMarkerInfoWindow(_markers.values.toList()[0].markerId); + _mapController!.showMarkerInfoWindow(_markers.values.toList()[0].markerId); } catch (e) {} }); } @@ -71,9 +67,9 @@ class MapsDataProvider extends ChangeNotifier { void reorderLocations() { _mapSearchModels.sort((MapSearchModel a, MapSearchModel b) { - if (a.distance != null && b.distance != null) { - return a.distance!.compareTo(b.distance!); - } + final bool aHasDistance = a.distance != null; + final bool bHasDistance = b.distance != null; + if (aHasDistance && bHasDistance) return a.distance!.compareTo(b.distance!); return 0; }); } @@ -100,13 +96,12 @@ class MapsDataProvider extends ChangeNotifier { _searchHistory.add(query); // ...If it is not, add it... } else { // ...otherwise... - _searchHistory - .remove(query); // ...reorder search history to put it back on top + _searchHistory.remove(query); // ...reorder search history to put it back on top _searchHistory.add(query); } _lastUpdated = DateTime.now(); } else { - ///TODO: determine what error to show to the user + // TODO: determine what error to show to the user - December 2025 _error = _mapSearchService.error; _noResults = true; } @@ -115,15 +110,14 @@ class MapsDataProvider extends ChangeNotifier { } void populateDistances() { - double? latitude = - _coordinates!.lat != null ? _coordinates!.lat : _defaultLat; - double? longitude = - _coordinates!.lon != null ? _coordinates!.lon : _defaultLong; + double? latitude = _coordinates!.lat != null ? _coordinates!.lat : _defaultLat; + double? longitude = _coordinates!.lon != null ? _coordinates!.lon : _defaultLong; if (_coordinates != null) { for (MapSearchModel model in _mapSearchModels) { - if (model.mkrLat != null && model.mkrLong != null) { - var distance = calculateDistance( - latitude!, longitude!, model.mkrLat!, model.mkrLong!); + final bool hasLatitude = model.mkrLat != null; + final bool hasLongitude = model.mkrLong != null; + if (hasLatitude && hasLongitude) { + var distance = calculateDistance(latitude!, longitude!, model.mkrLat!, model.mkrLong!); model.distance = distance as double?; } } @@ -133,9 +127,7 @@ class MapsDataProvider extends ChangeNotifier { num calculateDistance(double lat1, double lng1, double lat2, double lng2) { var p = 0.017453292519943295; var c = cos; - var a = 0.5 - - c((lat2 - lat1) * p) / 2 + - c(lat1 * p) * c(lat2 * p) * (1 - c((lng2 - lng1) * p)) / 2; + var a = 0.5 - c((lat2 - lat1) * p) / 2 + c(lat1 * p) * c(lat2 * p) * (1 - c((lng2 - lng1) * p)) / 2; return 12742 * asin(sqrt(a)) * 0.621371; } diff --git a/lib/core/providers/messages.dart b/lib/core/providers/messages.dart index c25e0ffe6..3289fe444 100644 --- a/lib/core/providers/messages.dart +++ b/lib/core/providers/messages.dart @@ -12,12 +12,10 @@ class MessagesDataProvider extends ChangeNotifier { MessagesDataProvider() { /// DEFAULT STATES notificationScrollController.addListener(() { - var triggerFetchMoreSize = - 0.9 * notificationScrollController.position.maxScrollExtent; + var triggerFetchMoreSize = 0.9 * notificationScrollController.position.maxScrollExtent; - if (notificationScrollController.position.pixels > triggerFetchMoreSize) { - if (!_isLoading && _hasMoreMessagesToLoad) fetchMessages(false); - } + final bool shouldTriggerFetch = notificationScrollController.position.pixels > triggerFetchMoreSize; + if (shouldTriggerFetch) if (!_isLoading && _hasMoreMessagesToLoad) fetchMessages(false); setNotificationsScrollOffset(notificationScrollController.offset); }); } @@ -28,7 +26,7 @@ class MessagesDataProvider extends ChangeNotifier { DateTime? _lastUpdated; int _previousTimestamp = 0; String? _error; - String _statusText = NotificationsConstants.statusFetching; + String _statusText = NotificationsConstants.STATUS_FETCHING; /// MODELS List _messages = []; @@ -38,7 +36,7 @@ class MessagesDataProvider extends ChangeNotifier { final _messageService = MessageService(); final notificationScrollController = ScrollController(); - //Fetch messages + // Fetch messages Future fetchMessages(bool clearMessages) async { _isLoading = true; _error = null; @@ -67,8 +65,7 @@ class MessagesDataProvider extends ChangeNotifier { int timestamp = _previousTimestamp; Map headers = { "accept": "application/json", - "Authorization": - "Bearer " + userDataProvider!.authenticationModel.accessToken!, + "Authorization": "Bearer " + userDataProvider!.authenticationModel.accessToken!, }; if (await _messageService.fetchMyMessagesData(timestamp, headers)) { @@ -77,8 +74,7 @@ class MessagesDataProvider extends ChangeNotifier { makeOrderedMessagesList(); returnedTimestamp = _messageService.messagingModels.next ?? 0; // checks if we have no more messages to paginate through - _hasMoreMessagesToLoad = - !(_previousTimestamp == returnedTimestamp || returnedTimestamp == 0); + _hasMoreMessagesToLoad = !(_previousTimestamp == returnedTimestamp || returnedTimestamp == 0); _lastUpdated = DateTime.now(); _previousTimestamp = returnedTimestamp; _isLoading = false; @@ -94,15 +90,15 @@ class MessagesDataProvider extends ChangeNotifier { notifyListeners(); int returnedTimestamp; - if (await _messageService.fetchTopicData( - _previousTimestamp, userDataProvider!.subscribedTopics!)) { + final bool topicDataFetched = + await _messageService.fetchTopicData(_previousTimestamp, userDataProvider!.subscribedTopics!); + if (topicDataFetched) { List temp = _messageService.messagingModels.messages; updateMessages(temp); makeOrderedMessagesList(); returnedTimestamp = _messageService.messagingModels.next ?? 0; // checks if we have no more messages to paginate through - _hasMoreMessagesToLoad = - !(_previousTimestamp == returnedTimestamp || returnedTimestamp == 0); + _hasMoreMessagesToLoad = !(_previousTimestamp == returnedTimestamp || returnedTimestamp == 0); _lastUpdated = DateTime.now(); _previousTimestamp = returnedTimestamp; _isLoading = false; @@ -118,8 +114,7 @@ class MessagesDataProvider extends ChangeNotifier { void makeOrderedMessagesList() { Map uniqueMessages = Map(); - uniqueMessages = Map.fromIterable(_messages, - key: (message) => message.messageId, value: (message) => message); + uniqueMessages = Map.fromIterable(_messages, key: (message) => message.messageId, value: (message) => message); _messages.clear(); uniqueMessages.forEach((k, v) => _messages.add(v)); _messages.sort((a, b) => b.timestamp.compareTo(a.timestamp)); @@ -127,9 +122,7 @@ class MessagesDataProvider extends ChangeNotifier { void updateMessages(List newMessages) { _messages.addAll(newMessages); - _statusText = _messages.isEmpty - ? NotificationsConstants.statusNoMessages - : NotificationsConstants.statusNone; + _statusText = _messages.isEmpty ? NotificationsConstants.STATUS_NO_MESSAGES : NotificationsConstants.STATUS_NONE; } /// SIMPLE GETTERS diff --git a/lib/core/providers/news.dart b/lib/core/providers/news.dart index 74ca8b3eb..d69247b3c 100644 --- a/lib/core/providers/news.dart +++ b/lib/core/providers/news.dart @@ -22,7 +22,7 @@ class NewsDataProvider extends ChangeNotifier { _newsModels = _newsService.newsModels; _lastUpdated = DateTime.now(); } else { - /// TODO: determine what error to show to the user + // TODO: determine what error to show to the user - December 2025 _error = _newsService.error; } _isLoading = false; diff --git a/lib/core/providers/notices.dart b/lib/core/providers/notices.dart index 87865de59..11f92968b 100644 --- a/lib/core/providers/notices.dart +++ b/lib/core/providers/notices.dart @@ -22,7 +22,7 @@ class NoticesDataProvider extends ChangeNotifier { _noticesModel = _noticesService.noticesModel; _lastUpdated = DateTime.now(); } else { - /// TODO: determine what error to show to the user + // TODO: determine what error to show to the user - December 2025 _error = _noticesService.error; } _isLoading = false; diff --git a/lib/core/providers/notifications.dart b/lib/core/providers/notifications.dart index 008a6263b..5d2378f20 100644 --- a/lib/core/providers/notifications.dart +++ b/lib/core/providers/notifications.dart @@ -67,54 +67,48 @@ class PushNotificationDataProvider extends ChangeNotifier { try { /// Initialize flutter notification settings this.context = context; - const initializationSettingsAndroid = - AndroidInitializationSettings("@drawable/ic_notif_round"); + const initializationSettingsAndroid = AndroidInitializationSettings("@drawable/ic_notif_round"); final initializationSettingsIOS = DarwinInitializationSettings(); - final initializationSettings = InitializationSettings( - android: initializationSettingsAndroid, - iOS: initializationSettingsIOS); + final initializationSettings = + InitializationSettings(android: initializationSettingsAndroid, iOS: initializationSettingsIOS); await flutterLocalNotificationsPlugin.initialize(initializationSettings, onDidReceiveNotificationResponse: selectNotification); - RemoteMessage? initialMessage = - await FirebaseMessaging.instance.getInitialMessage(); + RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); if (initialMessage != null) { - await Provider.of(context, listen: false) - .fetchMessages(true); + await Provider.of(context, listen: false).fetchMessages(true); /// switch to the notifications tab - Provider.of(context, listen: false) - .currentIndex = NavigatorConstants.NotificationsTab; + Provider.of(context, listen: false).currentIndex = + NavigatorConstants.NOTIFICATIONS_TAB; } /// Foreground messaging FirebaseMessaging.onMessage.listen((RemoteMessage message) { /// foreground messaging callback via flutter_local_notifications /// only show message if the message has not been seen before - if (!_receivedMessageIds.contains(message.messageId)) - showNotification(message); + final bool isNewMessage = !_receivedMessageIds.contains(message.messageId); + if (isNewMessage) showNotification(message); // add messageId as it has been shown already _receivedMessageIds.add(message.messageId!); /// Fetch in-app messages - Provider.of(context, listen: false) - .fetchMessages(true); + Provider.of(context, listen: false).fetchMessages(true); }); FirebaseMessaging.onMessageOpenedApp.listen( (RemoteMessage message) { /// Fetch in-app messages - Provider.of(context, listen: false) - .fetchMessages(true); + Provider.of(context, listen: false).fetchMessages(true); /// Set tab bar index to the Notifications tab - Provider.of(context, listen: false) - .currentIndex = NavigatorConstants.NotificationsTab; + Provider.of(context, listen: false).currentIndex = + NavigatorConstants.NOTIFICATIONS_TAB; /// Navigate to Notifications tab - Navigator.of(context).pushNamedAndRemoveUntil( - RoutePaths.BottomNavigationBar, (Route route) => false); + Navigator.of(context) + .pushNamedAndRemoveUntil(RoutePaths.BOTTOM_NAVIGATION_BAR, (Route route) => false); }, ); } on PlatformException { @@ -125,36 +119,32 @@ class PushNotificationDataProvider extends ChangeNotifier { /// Handles notification when selected void selectNotification(NotificationResponse details) { /// Fetch in-app messages - Provider.of(this.context, listen: false) - .fetchMessages(true); + Provider.of(this.context, listen: false).fetchMessages(true); /// Navigate to Notifications tab - Navigator.of(this.context).pushNamedAndRemoveUntil( - RoutePaths.BottomNavigationBar, (Route route) => false); + Navigator.of(this.context) + .pushNamedAndRemoveUntil(RoutePaths.BOTTOM_NAVIGATION_BAR, (Route route) => false); /// Set tab bar index to the Notifications tab - Provider.of(this.context, listen: false) - .currentIndex = NavigatorConstants.NotificationsTab; - Provider.of(context, listen: false) - .changeTitle("Notifications"); + Provider.of(this.context, listen: false).currentIndex = + NavigatorConstants.NOTIFICATIONS_TAB; + Provider.of(context, listen: false).changeTitle("Notifications"); } /// Displays the notification showNotification(RemoteMessage message) async { - const androidPlatformChannelSpecifics = AndroidNotificationDetails( - 'your channel id', 'your channel name', + const androidPlatformChannelSpecifics = AndroidNotificationDetails('your channel id', 'your channel name', icon: '@drawable/ic_notif_round', largeIcon: const DrawableResourceAndroidBitmap('@drawable/app_icon'), importance: Importance.max, priority: Priority.high, showWhen: false); const DarwinNotificationDetails(); - const platformChannelSpecifics = NotificationDetails( - android: androidPlatformChannelSpecifics, - iOS: DarwinNotificationDetails()); - //This is where you put info from firebase - await flutterLocalNotificationsPlugin.show(0, message.notification!.title, - message.notification!.body, platformChannelSpecifics, + const platformChannelSpecifics = + NotificationDetails(android: androidPlatformChannelSpecifics, iOS: DarwinNotificationDetails()); + // This is where you put info from firebase + await flutterLocalNotificationsPlugin.show( + 0, message.notification!.title, message.notification!.body, platformChannelSpecifics, payload: 'This is the payload'); } @@ -166,8 +156,7 @@ class PushNotificationDataProvider extends ChangeNotifier { if (await _notificationService.fetchTopics()) { for (TopicsModel model in _notificationService.topicsModel) { for (Topic topic in model.topics!) { - newTopics[topic.topicId] = - _topicSubscriptionState[topic.topicId] ?? false; + newTopics[topic.topicId] = _topicSubscriptionState[topic.topicId] ?? false; } } _topicSubscriptionState = newTopics; @@ -236,12 +225,10 @@ class PushNotificationDataProvider extends ChangeNotifier { } else { // Get the token for this device String? fcmToken = await _fcm.getToken(); - if (fcmToken != null && - fcmToken.isNotEmpty && - (accessToken?.isNotEmpty ?? false)) { - Map headers = { - 'Authorization': 'Bearer ' + accessToken! - }; + final bool hasFcmToken = fcmToken != null && fcmToken.isNotEmpty; + final bool hasAccessToken = accessToken?.isNotEmpty ?? false; + if (hasFcmToken && hasAccessToken) { + Map headers = {'Authorization': 'Bearer ' + accessToken!}; Map body = {'deviceId': deviceId, 'token': fcmToken}; if ((await _notificationService.postPushToken(headers, body))) { return true; @@ -259,9 +246,9 @@ class PushNotificationDataProvider extends ChangeNotifier { /// Unregisters device from receiving push notifications Future unregisterDevice(String? accessToken) async { String? fcmToken = await _fcm.getToken(); - if (fcmToken != null && - fcmToken.isNotEmpty && - (accessToken?.isNotEmpty ?? false)) { + final bool hasFcmToken = fcmToken != null && fcmToken.isNotEmpty; + final bool hasAccessToken = accessToken?.isNotEmpty ?? false; + if (hasFcmToken && hasAccessToken) { Map headers = {'Authorization': 'Bearer ' + accessToken!}; if ((await _notificationService.deletePushToken(headers, fcmToken))) { return true; @@ -276,8 +263,7 @@ class PushNotificationDataProvider extends ChangeNotifier { } void unsubscribeFromAllTopics() { - _unsubscribeToTopics( - _topicSubscriptionState.keys.whereType().toList()); + _unsubscribeToTopics(_topicSubscriptionState.keys.whereType().toList()); for (String? topic in _topicSubscriptionState.keys) { topicSubscriptionState[topic] = false; } diff --git a/lib/core/providers/notifications_freefood.dart b/lib/core/providers/notifications_freefood.dart index 82b5b0b8d..fc30b9208 100644 --- a/lib/core/providers/notifications_freefood.dart +++ b/lib/core/providers/notifications_freefood.dart @@ -53,8 +53,8 @@ class FreeFoodDataProvider extends ChangeNotifier { Future loadRegisteredEvents() async { var box = await Hive.openBox('freefoodRegisteredEvents'); - if (box.get('freefoodRegisteredEvents') == null) - await box.put('freefoodRegisteredEvents', _registeredEvents); + final bool hasNoSavedEvents = box.get('freefoodRegisteredEvents') == null; + if (hasNoSavedEvents) await box.put('freefoodRegisteredEvents', _registeredEvents); _registeredEvents = box.get('freefoodRegisteredEvents'); notifyListeners(); @@ -79,10 +79,9 @@ class FreeFoodDataProvider extends ChangeNotifier { _messageToCount[id] = _freeFoodModel.body.count; } else { _error = _freeFoodService.error; - if (_error != null && - _error!.contains(ErrorConstants.invalidBearerToken)) { - if (await _freeFoodService.getNewToken()) await fetchCount(id); - } + final bool hasError = _error != null; + final bool isInvalidToken = _error?.contains(ErrorConstants.INVALID_BEARER_TOKEN) ?? false; + if (hasError && isInvalidToken) if (await _freeFoodService.getNewToken()) await fetchCount(id); removeId(id); } _isLoading = false; @@ -101,10 +100,9 @@ class FreeFoodDataProvider extends ChangeNotifier { _messageToMaxCount[id] = _freeFoodModel.body.maxCount; } else { _error = _freeFoodService.error; - if (_error != null && - _error!.contains( - ErrorConstants.invalidBearerToken)) if (await _freeFoodService - .getNewToken()) await fetchMaxCount(id); + final bool hasError = _error != null; + final bool isInvalidToken = _error?.contains(ErrorConstants.INVALID_BEARER_TOKEN) ?? false; + if (hasError && isInvalidToken) if (await _freeFoodService.getNewToken()) await fetchMaxCount(id); removeId(id); } @@ -137,10 +135,9 @@ class FreeFoodDataProvider extends ChangeNotifier { _lastUpdated = DateTime.now(); } else { _error = _freeFoodService.error; - if (_error != null && - _error!.contains(ErrorConstants.invalidBearerToken)) { - if (await _freeFoodService.getNewToken()) await updateCount(id, body); - } + final bool hasError = _error != null; + final bool isInvalidToken = _error?.contains(ErrorConstants.INVALID_BEARER_TOKEN) ?? false; + if (hasError && isInvalidToken) if (await _freeFoodService.getNewToken()) await updateCount(id, body); removeId(id); } @@ -151,8 +148,7 @@ class FreeFoodDataProvider extends ChangeNotifier { } /// SIMPLE SETTERS - set messageDataProvider(MessagesDataProvider value) => - _messageDataProvider = value; + set messageDataProvider(MessagesDataProvider value) => _messageDataProvider = value; bool isLoading(String? id) => id == _curId; /// SIMPLE GETTERS @@ -163,9 +159,9 @@ class FreeFoodDataProvider extends ChangeNotifier { bool isFreeFood(String messageId) => _messageToCount.containsKey(messageId); int? count(String messageId) => _messageToCount[messageId]; bool isOverCount(String messageId) { - if (_messageToCount.containsKey(messageId) && - _messageToMaxCount.containsKey(messageId)) - return _messageToCount[messageId]! > _messageToMaxCount[messageId]!; + final bool hasCount = _messageToCount.containsKey(messageId); + final bool hasMaxCount = _messageToMaxCount.containsKey(messageId); + if (hasCount && hasMaxCount) return _messageToCount[messageId]! > _messageToMaxCount[messageId]!; return false; } } diff --git a/lib/core/providers/parking.dart b/lib/core/providers/parking.dart index 9cd4130ae..3b5145121 100644 --- a/lib/core/providers/parking.dart +++ b/lib/core/providers/parking.dart @@ -41,11 +41,10 @@ class ParkingDataProvider extends ChangeNotifier { if (await _parkingService.fetchParkingLotData()) { if (_userDataProvider.userProfileModel.selectedParkingLots!.isNotEmpty) { - _parkingViewState = _userDataProvider - .userProfileModel.selectedParkingLots! as Map; + _parkingViewState = _userDataProvider.userProfileModel.selectedParkingLots! as Map; } else { for (ParkingModel model in _parkingService.data!) { - if (ParkingDefaults.defaultLots.contains(model.locationId)) + if (ParkingDefaults.DEFAULT_LOTS.contains(model.locationId)) _parkingViewState[model.locationName] = true; else _parkingViewState[model.locationName] = false; @@ -55,21 +54,19 @@ class ParkingDataProvider extends ChangeNotifier { for (ParkingModel model in _parkingService.data!) { newMapOfLots[model.locationName] = model; newMapOfLotStates[model.locationName] = - (_parkingViewState[model.locationName] == null - ? false - : _parkingViewState[model.locationName])!; + (_parkingViewState[model.locationName] == null ? false : _parkingViewState[model.locationName])!; } /// replace old list of lots with new one _parkingModels = newMapOfLots; _parkingViewState = newMapOfLotStates; - //Update number of lots selected + // Update number of lots selected _parkingViewState.forEach((key, value) { if (value) selectedLots++; }); } else { - /// TODO: determine what error to show to the user + // TODO: determine what error to show to the user - December 2025 _error = _parkingService.error; } @@ -87,7 +84,7 @@ class ParkingDataProvider extends ChangeNotifier { } else { // Load default spot types for (Spot spot in _spotTypeModel.spots!) { - if (ParkingDefaults.defaultSpots.contains(spot.spotKey)) + if (ParkingDefaults.DEFAULT_SPOTS.contains(spot.spotKey)) _selectedSpotTypesState[spot.spotKey] = true; else _selectedSpotTypesState[spot.spotKey] = false; @@ -97,8 +94,7 @@ class ParkingDataProvider extends ChangeNotifier { /// this block of code is to ensure we remove any unsupported spot types Map newMapOfSpotTypes = Map(); for (Spot spot in _spotTypeModel.spots!) { - newMapOfSpotTypes[spot.spotKey] = - _selectedSpotTypesState[spot.spotKey] ?? false; + newMapOfSpotTypes[spot.spotKey] = _selectedSpotTypesState[spot.spotKey] ?? false; } _selectedSpotTypesState = newMapOfSpotTypes; @@ -132,8 +128,7 @@ class ParkingDataProvider extends ChangeNotifier { } // Update user profile with selected lots - _userDataProvider.userProfileModel.selectedParkingLots = - _parkingViewState; + _userDataProvider.userProfileModel.selectedParkingLots = _parkingViewState; _userDataProvider.postUserProfile(_userDataProvider.userProfileModel); // Notify listeners immediately @@ -165,8 +160,7 @@ class ParkingDataProvider extends ChangeNotifier { } // Update user profile with selected spots - _userDataProvider.userProfileModel.selectedParkingSpots = - _selectedSpotTypesState; + _userDataProvider.userProfileModel.selectedParkingSpots = _selectedSpotTypesState; _userDataProvider.postUserProfile(_userDataProvider.userProfileModel); // Notify listeners immediately @@ -181,28 +175,28 @@ class ParkingDataProvider extends ChangeNotifier { } } - /// TODO: rewrite and optimize this! + // TODO: rewrite and optimize this! - December 2025 /// Returns the total number of spots open at a given location /// does not filter based on spot type Map getApproxNumOfOpenSpots(String locationId) { Map totalAndOpenSpots = {"Open": 0, "Total": 0}; if (_parkingModels[locationId] != null) { for (dynamic spot in _parkingModels[locationId]!.availability.keys) { - if (_parkingModels[locationId]!.availability[spot]['Open'] != null && - _parkingModels[locationId]!.availability[spot]['Open'] != "") { + final bool hasOpenData = _parkingModels[locationId]!.availability[spot]['Open'] != null; + final bool openDataNotEmpty = _parkingModels[locationId]!.availability[spot]['Open'] != ""; + if (hasOpenData && openDataNotEmpty) { totalAndOpenSpots["Open"] = totalAndOpenSpots["Open"]! + (_parkingModels[locationId]!.availability[spot]['Open'] is String - ? int.parse( - _parkingModels[locationId]!.availability[spot]['Open']) + ? int.parse(_parkingModels[locationId]!.availability[spot]['Open']) : _parkingModels[locationId]!.availability[spot]['Open']); } - if (_parkingModels[locationId]!.availability[spot]['Total'] != null && - _parkingModels[locationId]!.availability[spot]['Total'] != "") { + final bool hasTotalData = _parkingModels[locationId]!.availability[spot]['Total'] != null; + final bool totalDataNotEmpty = _parkingModels[locationId]!.availability[spot]['Total'] != ""; + if (hasTotalData && totalDataNotEmpty) { totalAndOpenSpots["Total"] = totalAndOpenSpots["Total"]! + (_parkingModels[locationId]!.availability[spot]['Total'] is String - ? int.parse( - _parkingModels[locationId]!.availability[spot]['Total']) + ? int.parse(_parkingModels[locationId]!.availability[spot]['Total']) : _parkingModels[locationId]!.availability[spot]['Total']); } } diff --git a/lib/core/providers/shuttle.dart b/lib/core/providers/shuttle.dart index fb52b477f..7e35e87f6 100644 --- a/lib/core/providers/shuttle.dart +++ b/lib/core/providers/shuttle.dart @@ -40,8 +40,7 @@ class ShuttleDataProvider extends ChangeNotifier { fetchedStops = newMapOfStops; /// if the user is logged in we want to sync the order of parking lots amongst all devices - if (userDataProvider != null && !reloading) - reorderStops(userDataProvider!.userProfileModel.selectedStops); + if (userDataProvider != null && !reloading) reorderStops(userDataProvider!.userProfileModel.selectedStops); // get closest stop to current user await calculateClosestStop(); @@ -51,7 +50,7 @@ class ShuttleDataProvider extends ChangeNotifier { notifyListeners(); } - // TODO: unused function. If needed, rewrite w/ proper nullability + // TODO: unused function. If needed, rewrite w/ proper nullability - December 2025 // List makeOrderedList(List? order) { // if (order == null) { // return []; @@ -98,19 +97,17 @@ class ShuttleDataProvider extends ChangeNotifier { Future calculateClosestStop() async { // make sure we have users location before we do any calculations - if (_userCoords == null || - _userCoords!.lon == null || - _userCoords!.lat == null) { - return; - } + final bool hasCoordsObject = _userCoords != null; + final bool hasLongitude = _userCoords?.lon != null; + final bool hasLatitude = _userCoords?.lat != null; + if (!hasCoordsObject || !hasLongitude || !hasLatitude) return; for (ShuttleStopModel shuttleStop in _shuttleService.data) { double stopLat = shuttleStop.lat, stopLong = shuttleStop.lon; - if (getHaversineDistance( - _userCoords!.lat, _userCoords!.lon, stopLat, stopLong) < - closestDistance) { - closestDistance = getHaversineDistance( - _userCoords!.lat, _userCoords!.lon, stopLat, stopLong); + final double distanceToStop = getHaversineDistance(_userCoords!.lat, _userCoords!.lon, stopLat, stopLong); + final bool isCloser = distanceToStop < closestDistance; + if (isCloser) { + closestDistance = getHaversineDistance(_userCoords!.lat, _userCoords!.lon, stopLat, stopLong); _closestStop = shuttleStop; } } @@ -118,23 +115,18 @@ class ShuttleDataProvider extends ChangeNotifier { } double getHaversineDistance(lat1, lon1, double lat2, double lon2) { - var R = 6371; // Radius of the earth in km + var r = 6371; // Radius of the earth in km var dLat = deg2rad(lat2 - lat1)!; // deg2rad below var dLon = deg2rad(lon2 - lon1)!; var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + - Math.cos(deg2rad(lat1)!) * - Math.cos(deg2rad(lat2)!) * - Math.sin(dLon / 2) * - Math.sin(dLon / 2); + Math.cos(deg2rad(lat1)!) * Math.cos(deg2rad(lat2)!) * Math.sin(dLon / 2) * Math.sin(dLon / 2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - var d = R * c; // Distance in km + var d = r * c; // Distance in km return d; } Future getArrivalInformation() async { - if (_closestStop != null) - arrivalsToRender[_closestStop!.id] = - await fetchArrivalInformation(_closestStop!.id); + if (_closestStop != null) arrivalsToRender[_closestStop!.id] = await fetchArrivalInformation(_closestStop!.id); for (ShuttleStopModel stop in stopsToRender) { arrivalsToRender[stop.id] = await fetchArrivalInformation(stop.id); @@ -143,8 +135,7 @@ class ShuttleDataProvider extends ChangeNotifier { } Future> fetchArrivalInformation(int stopID) async { - List output = - await _shuttleService.getArrivingInformation(stopID); + List output = await _shuttleService.getArrivingInformation(stopID); output.sort((a, b) => a.secondsToArrival.compareTo(b.secondsToArrival)); return output; } @@ -162,18 +153,19 @@ class ShuttleDataProvider extends ChangeNotifier { ShuttleStopModel? get closestStop => _closestStop; List get stopsToRender { var stopsToRenderList = []; - if (fetchedStops != null) - for (var i = 0; - i < userDataProvider!.userProfileModel.selectedStops!.length; - i++) { + final bool hasStops = fetchedStops != null; + final bool hasSelectedStops = userDataProvider?.userProfileModel.selectedStops != null; + if (hasStops && hasSelectedStops) { + for (var i = 0; i < userDataProvider!.userProfileModel.selectedStops!.length; i++) { int stopID = userDataProvider!.userProfileModel.selectedStops![i]!; - if (fetchedStops![stopID] != null) - stopsToRenderList.add(fetchedStops![stopID]!); + if (fetchedStops![stopID] != null) stopsToRenderList.add(fetchedStops![stopID]!); } + } return stopsToRenderList; } Map get stopsNotSelected { + if (fetchedStops == null) return {}; var output = new Map.from(fetchedStops!); for (ShuttleStopModel? stop in stopsToRender) { output.remove(stop!.id); diff --git a/lib/core/providers/speed_test.dart b/lib/core/providers/speed_test.dart index eb26c69b0..1c05d477f 100644 --- a/lib/core/providers/speed_test.dart +++ b/lib/core/providers/speed_test.dart @@ -13,7 +13,7 @@ import 'package:path_provider/path_provider.dart'; class SpeedTestProvider extends ChangeNotifier { SpeedTestProvider() { - /// TODO: probably is a bug! Async functions should not be be run in the constructor + // TODO: probably is a bug! Async functions should not be be run in the constructor - December 2025 init(); } @@ -81,7 +81,9 @@ class SpeedTestProvider extends ChangeNotifier { downloadSpeedTest().then((value) { _timer.reset(); uploadSpeedTest().then((value) { - if (_percentUploaded == 1.0 && _percentDownloaded == 1.0) { + final bool uploadComplete = _percentUploaded == 1.0; + final bool downloadComplete = _percentDownloaded == 1.0; + if (uploadComplete && downloadComplete) { _speedTestDone = true; notifyListeners(); } @@ -96,24 +98,23 @@ class SpeedTestProvider extends ChangeNotifier { // if not on UCSD wifi OR the file above does not exist, // we should not upload the speed test results // instead, stop the timer and exit the function - if (isUCSDWiFi != true || !temp.existsSync()) { + final bool isNotUCSDWiFi = isUCSDWiFi != true; + final bool fileDoesNotExist = !temp.existsSync(); + if (isNotUCSDWiFi || fileDoesNotExist) { _timer.stop(); notifyListeners(); return; } var tempDownload = temp.readAsBytesSync(); - var formData = new FormData.fromMap( - {"file": MultipartFile.fromBytes(tempDownload, filename: "temp.html")}); + var formData = new FormData.fromMap({"file": MultipartFile.fromBytes(tempDownload, filename: "temp.html")}); notifyListeners(); try { _cancelTokenUpload = new CancelToken(); _timer.start(); await dio.put(_speedTestModel!.uploadUrl!, - data: formData, - onSendProgress: _progressCallbackUpload, - cancelToken: _cancelTokenUpload); + data: formData, onSendProgress: _progressCallbackUpload, cancelToken: _cancelTokenUpload); } catch (e) { print(e); } finally { @@ -130,8 +131,7 @@ class SpeedTestProvider extends ChangeNotifier { _cancelTokenDownload = new CancelToken(); _timer.start(); await dio.download(_speedTestModel!.downloadUrl!, (tempDownload.path), - onReceiveProgress: _progressCallbackDownload, - cancelToken: _cancelTokenDownload); + onReceiveProgress: _progressCallbackDownload, cancelToken: _cancelTokenDownload); } catch (e) { print(e); } finally { @@ -203,59 +203,42 @@ class SpeedTestProvider extends ChangeNotifier { "Latitude": _coordinates.lat.toString(), "Longitude": _coordinates.lon.toString(), "TimeStamp": _speedTestModel!.timeStamp, - "DownloadSpeed": lastSpeed != null - ? lastSpeed.toStringAsPrecision(3) - : _speedDownload!.toStringAsPrecision(3), + "DownloadSpeed": lastSpeed != null ? lastSpeed.toStringAsPrecision(3) : _speedDownload!.toStringAsPrecision(3), "UploadSpeed": _speedUpload!.toStringAsPrecision(3), }; sentSuccessfully = true; sendLogs(wiFiLog); - return sentSuccessfully; //Due to failed submission or not connected to wifi + return sentSuccessfully; // Due to failed submission or not connected to wifi } Future sendLogs(Map? log) async { final mobileLoggerApiWifi = mobileLoggerApi + "?type=WIFI"; - offloadDataHeader = { - 'Authorization': - 'Bearer ${_userDataProvider.authenticationModel.accessToken}' - }; + offloadDataHeader = {'Authorization': 'Bearer ${_userDataProvider.authenticationModel.accessToken}'}; if (_userDataProvider.isLoggedIn) { - if (offloadDataHeader == null) { - offloadDataHeader = { - 'Authorization': - 'Bearer ${_userDataProvider.authenticationModel.accessToken}' - }; - } + if (offloadDataHeader == null) + offloadDataHeader = {'Authorization': 'Bearer ${_userDataProvider.authenticationModel.accessToken}'}; // Send to offload API try { - NetworkHelper.authorizedPost( - mobileLoggerApiWifi, offloadDataHeader, json.encode(log)) - .then((value) { + NetworkHelper.authorizedPost(mobileLoggerApiWifi, offloadDataHeader, json.encode(log)).then((value) { return value; }); } catch (exception) { - if (exception.toString().contains(ErrorConstants.invalidBearerToken)) { + if (exception.toString().contains(ErrorConstants.INVALID_BEARER_TOKEN)) { _userDataProvider.silentLogin(); - offloadDataHeader = { - 'Authorization': - 'Bearer ${_userDataProvider.authenticationModel.accessToken}' - }; - NetworkHelper.authorizedPost( - mobileLoggerApiWifi, offloadDataHeader, json.encode(log)); + offloadDataHeader = {'Authorization': 'Bearer ${_userDataProvider.authenticationModel.accessToken}'}; + NetworkHelper.authorizedPost(mobileLoggerApiWifi, offloadDataHeader, json.encode(log)); } } } else { try { NetworkHelper.getNewToken(headers).then((value) { - NetworkHelper.authorizedPost( - mobileLoggerApiWifi, headers, json.encode(log)); + NetworkHelper.authorizedPost(mobileLoggerApiWifi, headers, json.encode(log)); }); } catch (exception) { NetworkHelper.getNewToken(headers).then((value) { - NetworkHelper.authorizedPost( - mobileLoggerApiWifi, headers, json.encode(log)); + NetworkHelper.authorizedPost(mobileLoggerApiWifi, headers, json.encode(log)); }); } } @@ -263,17 +246,11 @@ class SpeedTestProvider extends ChangeNotifier { Future reportIssue() async { final mobileLoggerApiWifiReport = mobileLoggerApi + "?type=WIFIREPORT"; - offloadDataHeader = { - 'Authorization': - 'Bearer ${_userDataProvider.authenticationModel.accessToken}' - }; + offloadDataHeader = {'Authorization': 'Bearer ${_userDataProvider.authenticationModel.accessToken}'}; wiFiLog = { - "userId": (_userDataProvider.userProfileModel.pid) == null - ? "" - : _userDataProvider.userProfileModel.pid, - "userLogin": (_userDataProvider.userProfileModel.username) == null - ? "" - : _userDataProvider.userProfileModel.username!, + "userId": (_userDataProvider.userProfileModel.pid) == null ? "" : _userDataProvider.userProfileModel.pid, + "userLogin": + (_userDataProvider.userProfileModel.username) == null ? "" : _userDataProvider.userProfileModel.username!, "Platform": _speedTestModel!.platform, "SSID": _speedTestModel!.ssid, "BSSID": _speedTestModel!.bssid, @@ -294,37 +271,26 @@ class SpeedTestProvider extends ChangeNotifier { }; if (_userDataProvider.isLoggedIn) { - if (offloadDataHeader == null) { - offloadDataHeader = { - 'Authorization': - 'Bearer ${_userDataProvider.authenticationModel.accessToken}' - }; - } + if (offloadDataHeader == null) + offloadDataHeader = {'Authorization': 'Bearer ${_userDataProvider.authenticationModel.accessToken}'}; // Send to offload API try { - NetworkHelper.authorizedPost( - mobileLoggerApiWifiReport, offloadDataHeader, json.encode(wiFiLog)); + NetworkHelper.authorizedPost(mobileLoggerApiWifiReport, offloadDataHeader, json.encode(wiFiLog)); } catch (exception) { - if (exception.toString().contains(ErrorConstants.invalidBearerToken)) { + if (exception.toString().contains(ErrorConstants.INVALID_BEARER_TOKEN)) { _userDataProvider.silentLogin(); - offloadDataHeader = { - 'Authorization': - 'Bearer ${_userDataProvider.authenticationModel.accessToken}' - }; - NetworkHelper.authorizedPost(mobileLoggerApiWifiReport, - offloadDataHeader, json.encode(wiFiLog)); + offloadDataHeader = {'Authorization': 'Bearer ${_userDataProvider.authenticationModel.accessToken}'}; + NetworkHelper.authorizedPost(mobileLoggerApiWifiReport, offloadDataHeader, json.encode(wiFiLog)); } } } else { try { NetworkHelper.getNewToken(headers).then((value) { - NetworkHelper.authorizedPost( - mobileLoggerApiWifiReport, headers, json.encode(wiFiLog)); + NetworkHelper.authorizedPost(mobileLoggerApiWifiReport, headers, json.encode(wiFiLog)); }); } catch (exception) { NetworkHelper.getNewToken(headers).then((value) { - NetworkHelper.authorizedPost( - mobileLoggerApiWifiReport, headers, json.encode(wiFiLog)); + NetworkHelper.authorizedPost(mobileLoggerApiWifiReport, headers, json.encode(wiFiLog)); }); } } @@ -333,8 +299,7 @@ class SpeedTestProvider extends ChangeNotifier { /// SIMPLE SETTERS /// This setter is only used in provider to supply an updated Coordinates object set coordinates(Coordinates value) => _coordinates = value; - set userDataProvider(UserDataProvider userDataProvider) => - _userDataProvider = userDataProvider; + set userDataProvider(UserDataProvider userDataProvider) => _userDataProvider = userDataProvider; set speed(double? lastSpeed) => _speedDownload = lastSpeed; double _convertToMbps(double speed) => speed / 125000; diff --git a/lib/core/providers/student_id.dart b/lib/core/providers/student_id.dart index ca7733c9f..63fdfe828 100644 --- a/lib/core/providers/student_id.dart +++ b/lib/core/providers/student_id.dart @@ -33,8 +33,7 @@ class StudentIdDataProvider extends ChangeNotifier { if (_userDataProvider.isLoggedIn) { /// Initialize header final Map header = { - 'Authorization': - 'Bearer ${_userDataProvider.authenticationModel.accessToken}' + 'Authorization': 'Bearer ${_userDataProvider.authenticationModel.accessToken}' }; /// Fetch Name diff --git a/lib/core/providers/user.dart b/lib/core/providers/user.dart index 605e5e971..4bf026871 100644 --- a/lib/core/providers/user.dart +++ b/lib/core/providers/user.dart @@ -82,8 +82,7 @@ class UserDataProvider extends ChangeNotifier { /// Load [AuthenticationModel] from persistent storage /// Will create persistent storage if no data is found Future _loadSavedAuthenticationModel() async { - var authBox = - await Hive.openBox('AuthenticationModel'); + var authBox = await Hive.openBox('AuthenticationModel'); AuthenticationModel temp = AuthenticationModel.fromJson({}); // Check to see if we have added the authentication model into the box already if (authBox.get('AuthenticationModel') == null) { @@ -100,14 +99,12 @@ class UserDataProvider extends ChangeNotifier { Future _loadSavedUserProfile() async { var userBox = await Hive.openBox('UserProfileModel'); // Create new user from temp profile - UserProfileModel tempUserProfile = - await _createNewUser(UserProfileModel.fromJson({})); - if (userBox.get('UserProfileModel') == null) - await userBox.put('UserProfileModel', tempUserProfile); + UserProfileModel tempUserProfile = await _createNewUser(UserProfileModel.fromJson({})); + final bool hasNoStoredProfile = userBox.get('UserProfileModel') == null; + if (hasNoStoredProfile) await userBox.put('UserProfileModel', tempUserProfile); tempUserProfile = userBox.get('UserProfileModel')!; _userProfileModel = tempUserProfile; - _subscribeToPushNotificationTopics( - _userProfileModel.subscribedTopics!.whereType().toList()); + _subscribeToPushNotificationTopics(_userProfileModel.subscribedTopics!.whereType().toList()); notifyListeners(); } @@ -117,13 +114,11 @@ class UserDataProvider extends ChangeNotifier { await storage.write(key: 'encrypted_password', value: encryptedPassword); /// Get encrypted password that has been saved to device - Future _getEncryptedPasswordFromDevice() => - storage.read(key: 'encrypted_password'); + Future _getEncryptedPasswordFromDevice() => storage.read(key: 'encrypted_password'); /// Save username to device // void _saveUsernameToDevice(String username) => storage.write(key: 'username', value: username); - Future _saveUsernameToDevice(String username) async => - await storage.write(key: 'username', value: username); + Future _saveUsernameToDevice(String username) async => await storage.write(key: 'username', value: username); /// Get username from device Future getUsernameFromDevice() => storage.read(key: 'username'); @@ -135,14 +130,12 @@ class UserDataProvider extends ChangeNotifier { void _deletePasswordFromDevice() => storage.delete(key: 'password'); /// Encrypt given username and password and store on device - Future _encryptAndSaveCredentials( - String username, String password) async { + Future _encryptAndSaveCredentials(String username, String password) async { final pkString = dotenv.get('USER_CREDENTIALS_PUBLIC_KEY'); final rsaParser = RSAKeyParser(); final pc.RSAPublicKey publicKey = rsaParser.parse(pkString) as RSAPublicKey; var cipher = OAEPEncoding(pc.AsymmetricBlockCipher('RSA')); - pc.AsymmetricKeyParameter keyParametersPublic = - new pc.PublicKeyParameter(publicKey); + pc.AsymmetricKeyParameter keyParametersPublic = new pc.PublicKeyParameter(publicKey); cipher.init(true, keyParametersPublic); Uint8List output = cipher.process(utf8.encode(password)); var base64EncodedText = base64.encode(output); @@ -158,7 +151,9 @@ class UserDataProvider extends ChangeNotifier { _isLoading = true; notifyListeners(); - if (username.isNotEmpty && password.isNotEmpty) { + final bool hasUsername = username.isNotEmpty; + final bool hasPassword = password.isNotEmpty; + if (hasUsername && hasPassword) { await _encryptAndSaveCredentials(username, password); if (await silentLogin()) { @@ -194,23 +189,19 @@ class UserDataProvider extends ChangeNotifier { /// Allow silentLogin if username, pw are set, and the user is not logged in if (username != null && encryptedPassword != null) { - final String base64EncodedWithEncryptedPassword = - base64.encode(utf8.encode(username + ':' + encryptedPassword)); + final String base64EncodedWithEncryptedPassword = base64.encode(utf8.encode(username + ':' + encryptedPassword)); resetHomeScrollOffset(); resetAllCardHeights(); resetNotificationsScrollOffset(); - if (await _authenticationService - .silentLogin(base64EncodedWithEncryptedPassword)) { + final bool silentLoginSuccessful = await _authenticationService.silentLogin(base64EncodedWithEncryptedPassword); + if (silentLoginSuccessful) { await updateAuthenticationModel(_authenticationService.data!); await fetchUserProfile(); var _cardsDataProvider = CardsDataProvider(); - _cardsDataProvider - .updateAvailableCards(_userProfileModel.ucsdaffiliation); - _subscribeToPushNotificationTopics( - List.from(userProfileModel.subscribedTopics!)); - _pushNotificationDataProvider - .registerDevice(_authenticationService.data!.accessToken); + _cardsDataProvider.updateAvailableCards(_userProfileModel.ucsdAffiliation); + _subscribeToPushNotificationTopics(List.from(userProfileModel.subscribedTopics!)); + _pushNotificationDataProvider.registerDevice(_authenticationService.data!.accessToken); await analytics.logEvent(name: 'loggedIn'); _isInSilentLogin = false; notifyListeners(); @@ -232,8 +223,7 @@ class UserDataProvider extends ChangeNotifier { resetHomeScrollOffset(); resetAllCardHeights(); resetNotificationsScrollOffset(); - _pushNotificationDataProvider - .unregisterDevice(_authenticationModel.accessToken); + _pushNotificationDataProvider.unregisterDevice(_authenticationModel.accessToken); updateAuthenticationModel(AuthenticationModel.fromJson({})); updateUserProfileModel(await _createNewUser(UserProfileModel.fromJson({}))); _deletePasswordFromDevice(); @@ -271,42 +261,33 @@ class UserDataProvider extends ChangeNotifier { if (isLoggedIn) { /// we fetch the user data now - final Map headers = { - 'Authorization': 'Bearer ' + _authenticationModel.accessToken! - }; + final Map headers = {'Authorization': 'Bearer ' + _authenticationModel.accessToken!}; if (await _userProfileService.downloadUserProfile(headers)) { /// if the user profile has no ucsd affiliation then we know the user is new /// so create a new profile and upload to DB using [postUserProfile] var newModel = _userProfileService.userProfileModel; - if (newModel.ucsdaffiliation == null) { + if (newModel.ucsdAffiliation == null) { newModel = await _createNewUser(newModel); await postUserProfile(newModel); } else { newModel.username = await getUsernameFromDevice(); - newModel.ucsdaffiliation = _authenticationModel.ucsdaffiliation; + newModel.ucsdAffiliation = _authenticationModel.ucsdaffiliation; newModel.pid = _authenticationModel.pid; - List castSubscriptions = - newModel.subscribedTopics!.cast(); + List castSubscriptions = newModel.subscribedTopics!.cast(); newModel.subscribedTopics = castSubscriptions.toSet().toList(); final studentPattern = RegExp('[BGJMU]'); final staffPattern = RegExp('[E]'); - if ((newModel.ucsdaffiliation ?? "").contains(studentPattern)) { - newModel - ..classifications = - Classifications.fromJson({'student': true, 'staff': false}); - } else if ((newModel.ucsdaffiliation ?? "").contains(staffPattern)) { - newModel - ..classifications = - Classifications.fromJson({'staff': true, 'student': false}); + if ((newModel.ucsdAffiliation ?? "").contains(studentPattern)) { + newModel..classifications = Classifications.fromJson({'student': true, 'staff': false}); + } else if ((newModel.ucsdAffiliation ?? "").contains(staffPattern)) { + newModel..classifications = Classifications.fromJson({'staff': true, 'student': false}); } else { - newModel.classifications = - Classifications.fromJson({'student': false, 'staff': false}); + newModel.classifications = Classifications.fromJson({'student': false, 'staff': false}); } await updateUserProfileModel(newModel); - _pushNotificationDataProvider - .subscribeToTopics(newModel.subscribedTopics!.cast()); + _pushNotificationDataProvider.subscribeToTopics(newModel.subscribedTopics!.cast()); } } else { _error = _userProfileService.error; @@ -338,27 +319,22 @@ class UserDataProvider extends ChangeNotifier { await _pushNotificationDataProvider.fetchTopicsList(); try { profile.username = await getUsernameFromDevice(); - profile.ucsdaffiliation = _authenticationModel.ucsdaffiliation; + profile.ucsdAffiliation = _authenticationModel.ucsdaffiliation; profile.pid = _authenticationModel.pid; profile.subscribedTopics = _pushNotificationDataProvider.publicTopics(); final studentPattern = RegExp('[BGJMU]'); final staffPattern = RegExp('[E]'); - if ((profile.ucsdaffiliation ?? "").contains(studentPattern)) { + if ((profile.ucsdAffiliation ?? "").contains(studentPattern)) { profile - ..classifications = - Classifications.fromJson({'student': true, 'staff': false}) - ..subscribedTopics! - .addAll(_pushNotificationDataProvider.studentTopics()); - } else if ((profile.ucsdaffiliation ?? "").contains(staffPattern)) { + ..classifications = Classifications.fromJson({'student': true, 'staff': false}) + ..subscribedTopics!.addAll(_pushNotificationDataProvider.studentTopics()); + } else if ((profile.ucsdAffiliation ?? "").contains(staffPattern)) { profile - ..classifications = - Classifications.fromJson({'staff': true, 'student': false}) - ..subscribedTopics! - .addAll(_pushNotificationDataProvider.staffTopics()); + ..classifications = Classifications.fromJson({'staff': true, 'student': false}) + ..subscribedTopics!.addAll(_pushNotificationDataProvider.staffTopics()); } else { - profile.classifications = - Classifications.fromJson({'student': false, 'staff': false}); + profile.classifications = Classifications.fromJson({'student': false, 'staff': false}); } } catch (e) { print(e.toString()); @@ -378,15 +354,13 @@ class UserDataProvider extends ChangeNotifier { /// check if user is logged in if (_authenticationModel.isLoggedIn(_authenticationService.lastUpdated)) { - final Map headers = { - 'Authorization': "Bearer " + _authenticationModel.accessToken! - }; + final Map headers = {'Authorization': "Bearer " + _authenticationModel.accessToken!}; /// we only want to push data that is not null var tempJson = Map(); for (var key in profile.toJson().keys) { - if (profile.toJson()[key] != null) - tempJson[key] = profile.toJson()[key]; + final bool hasValue = profile.toJson()[key] != null; + if (hasValue) tempJson[key] = profile.toJson()[key]; } if (await _userProfileService.uploadUserProfile(headers, tempJson)) { _error = null; @@ -402,8 +376,7 @@ class UserDataProvider extends ChangeNotifier { } /// SIMPLE SETTERS - set pushNotificationDataProvider(PushNotificationDataProvider value) => - _pushNotificationDataProvider = value; + set pushNotificationDataProvider(PushNotificationDataProvider value) => _pushNotificationDataProvider = value; /// SIMPLE GETTERS get isLoading => _isLoading; @@ -414,6 +387,6 @@ class UserDataProvider extends ChangeNotifier { UserProfileModel get userProfileModel => _userProfileModel; AuthenticationModel get authenticationModel => _authenticationModel; - /// TODO: fix this after UserProfileModel's nullability is fixed + // TODO: fix this after UserProfileModel's nullability is fixed - December 2025 List? get subscribedTopics => _userProfileModel.subscribedTopics; } diff --git a/lib/core/services/authentication.dart b/lib/core/services/authentication.dart index d189a7c97..2e5890786 100644 --- a/lib/core/services/authentication.dart +++ b/lib/core/services/authentication.dart @@ -23,8 +23,8 @@ class AuthenticationService { /// fetch data /// MODIFIED TO USE EXPONENTIAL RETRY - var response = await NetworkHelper.authorizedPublicPost( - dotenv.get('AUTH_SERVICE_API_ENDPOINT'), authServiceHeaders, null); + var response = + await NetworkHelper.authorizedPublicPost(dotenv.get('AUTH_SERVICE_API_ENDPOINT'), authServiceHeaders, null); /// check to see if response has an error if (response['errorMessage'] != null) throw (response['errorMessage']); @@ -35,7 +35,7 @@ class AuthenticationService { _lastUpdated = DateTime.now(); return true; } catch (e) { - /// TODO: handle errors thrown by the network class for different types of error responses + // TODO: handle errors thrown by the network class for different types of error responses - December 2025 _error = e.toString(); return false; } @@ -51,8 +51,8 @@ class AuthenticationService { /// fetch data /// MODIFIED TO USE EXPONENTIAL RETRY - var response = await NetworkHelper.authorizedPost( - dotenv.get('AUTH_SERVICE_API_ENDPOINT'), authServiceHeaders, null); + var response = + await NetworkHelper.authorizedPost(dotenv.get('AUTH_SERVICE_API_ENDPOINT'), authServiceHeaders, null); /// check to see if response has an error if (response['errorMessage'] != null) throw (response['errorMessage']); @@ -63,7 +63,7 @@ class AuthenticationService { _lastUpdated = DateTime.now(); return true; } catch (e) { - /// TODO: handle errors thrown by the network class for different types of error responses + // TODO: handle errors thrown by the network class for different types of error responses - December 2025 _error = e.toString(); return false; } diff --git a/lib/core/services/availability.dart b/lib/core/services/availability.dart index ab6d6ce86..42fb2e03b 100644 --- a/lib/core/services/availability.dart +++ b/lib/core/services/availability.dart @@ -25,8 +25,7 @@ class AvailabilityService { _isLoading = true; try { /// fetch data - String _response = await NetworkHelper.authorizedFetch( - dotenv.get('AVAILABILITY_API_ENDPOINT'), headers); + String _response = await NetworkHelper.authorizedFetch(dotenv.get('AVAILABILITY_API_ENDPOINT'), headers); /// parse data final data = availabilityStatusFromJson(_response); @@ -35,9 +34,7 @@ class AvailabilityService { } catch (e) { /// if the authorized fetch failed we know we have to refresh the /// token for this service - if (e.toString().contains("401")) { - if (await NetworkHelper.getNewToken(headers)) return await fetchData(); - } + if (e.toString().contains("401")) if (await NetworkHelper.getNewToken(headers)) return await fetchData(); _error = e.toString(); return false; } finally { diff --git a/lib/core/services/barcode.dart b/lib/core/services/barcode.dart index 82f30c43d..0cf04d13d 100644 --- a/lib/core/services/barcode.dart +++ b/lib/core/services/barcode.dart @@ -8,14 +8,14 @@ class BarcodeService { bool? _isLoading; String? _error; - Future uploadResults( - Map headers, Map body) async { + Future uploadResults(Map headers, Map body) async { _error = null; _isLoading = true; try { - final response = await NetworkHelper.authorizedPost( - dotenv.get('BARCODE_SERVICE_ENDPOINT'), headers, body); - if (response != null && validateUploadResults(body, response)) + final response = await NetworkHelper.authorizedPost(dotenv.get('BARCODE_SERVICE_ENDPOINT'), headers, body); + final bool hasResponse = response != null; + final bool isValidUpload = validateUploadResults(body, response); + if (hasResponse && isValidUpload) return true; else throw (response.toString()); @@ -36,8 +36,7 @@ class BarcodeService { - The stored value returned by ScanData API matches the value that has actually been scanned - The student has a non-empty Account ID, User ID, or Employee ID */ - bool validateUploadResults( - Map submit, Map response) { + bool validateUploadResults(Map submit, Map response) { try { return (submit["barcode"] == response["SCAN_CODE_ID"]); } catch (e) { diff --git a/lib/core/services/cards.dart b/lib/core/services/cards.dart index 68f851626..47af94057 100644 --- a/lib/core/services/cards.dart +++ b/lib/core/services/cards.dart @@ -22,17 +22,13 @@ class CardsService { /// API Manager Service try { - String cardListEndpoint = - dotenv.get('CARD_LIST_ENDPOINT') + ucsdAffiliation; - String _response = - await NetworkHelper.authorizedFetch(cardListEndpoint, headers); + String cardListEndpoint = dotenv.get('CARD_LIST_ENDPOINT') + ucsdAffiliation; + String _response = await NetworkHelper.authorizedFetch(cardListEndpoint, headers); _cardsModel = cardsModelFromJson(_response); return true; } catch (e) { - if (e.toString().contains("401")) { - if (await NetworkHelper.getNewToken(headers)) - return await fetchCards(ucsdAffiliation); - } + if (e.toString().contains("401")) if (await NetworkHelper.getNewToken(headers)) + return await fetchCards(ucsdAffiliation); _error = e.toString(); return false; } finally { diff --git a/lib/core/services/classes.dart b/lib/core/services/classes.dart index a597180b6..a4fa5b3e9 100644 --- a/lib/core/services/classes.dart +++ b/lib/core/services/classes.dart @@ -21,10 +21,7 @@ class ClassScheduleService { try { /// fetch data String _response = await NetworkHelper.authorizedFetch( - dotenv.get('MY_ACADEMIC_HISTORY_API_ENDPOINT') + - '?academic_level=UN&term_code=' + - term, - headers); + dotenv.get('MY_ACADEMIC_HISTORY_API_ENDPOINT') + '?academic_level=UN&term_code=' + term, headers); /// parse data _unData = classScheduleModelFromJson(_response); @@ -43,10 +40,7 @@ class ClassScheduleService { try { /// fetch data String _response = await NetworkHelper.authorizedFetch( - dotenv.get('MY_ACADEMIC_HISTORY_API_ENDPOINT') + - '?academic_level=GR&term_code=' + - term, - headers); + dotenv.get('MY_ACADEMIC_HISTORY_API_ENDPOINT') + '?academic_level=GR&term_code=' + term, headers); /// parse data _grData = classScheduleModelFromJson(_response); @@ -63,8 +57,7 @@ class ClassScheduleService { _error = null; _isLoading = true; try { - String _response = await NetworkHelper.fetchData( - dotenv.get('ACADEMIC_TERM_API_ENDPOINT')); + String _response = await NetworkHelper.fetchData(dotenv.get('ACADEMIC_TERM_API_ENDPOINT')); _academicTermModel = academicTermModelFromJson(_response); return true; } catch (e) { diff --git a/lib/core/services/dining.dart b/lib/core/services/dining.dart index eabdc79ba..52b89dc61 100644 --- a/lib/core/services/dining.dart +++ b/lib/core/services/dining.dart @@ -26,8 +26,8 @@ class DiningService { _isLoading = true; try { /// fetch data - String _response = await NetworkHelper.authorizedFetch( - dotenv.get('DINING_BASE_ENDPOINT') + '/locations', headers); + String _response = + await NetworkHelper.authorizedFetch(dotenv.get('DINING_BASE_ENDPOINT') + '/locations', headers); /// parse data final data = diningModelFromJson(_response); @@ -37,7 +37,8 @@ class DiningService { /// if the authorized fetch failed we know we have to refresh the /// token for this service if (e.toString().contains("401")) { - if (await NetworkHelper.getNewToken(headers)) return await fetchData(); + final bool tokenRefreshed = await NetworkHelper.getNewToken(headers); + if (tokenRefreshed) return await fetchData(); } _error = e.toString(); return false; @@ -61,7 +62,8 @@ class DiningService { // /// if the authorized fetch failed we know we have to refresh the // /// token for this service // if (e.toString().contains("401")) { - // if (await NetworkHelper.getNewToken(headers)) return await fetchMenu(id); + // final bool tokenRefreshed = await NetworkHelper.getNewToken(headers); + // if (tokenRefreshed) return await fetchMenu(id); // } // _error = e.toString(); // return false; diff --git a/lib/core/services/employee_id.dart b/lib/core/services/employee_id.dart index d3d4d5a5e..2132f081d 100644 --- a/lib/core/services/employee_id.dart +++ b/lib/core/services/employee_id.dart @@ -17,8 +17,7 @@ class EmployeeIdService { _isLoading = true; try { /// fetch data - String _response = await NetworkHelper.authorizedFetch( - dotenv.get('MY_EMPLOYEE_PROFILE_API_ENDPOINT'), headers); + String _response = await NetworkHelper.authorizedFetch(dotenv.get('MY_EMPLOYEE_PROFILE_API_ENDPOINT'), headers); _employeeIdModel = employeeIdModelFromJson(_response); return true; diff --git a/lib/core/services/events.dart b/lib/core/services/events.dart index aa99233f0..86a012957 100644 --- a/lib/core/services/events.dart +++ b/lib/core/services/events.dart @@ -24,17 +24,14 @@ class EventsService { _isLoading = true; try { /// fetch data - String _response = await NetworkHelper.authorizedFetch( - dotenv.get('EVENTS_ENDPOINT'), headers); + String _response = await NetworkHelper.authorizedFetch(dotenv.get('EVENTS_ENDPOINT'), headers); /// parse data final data = eventModelFromJson(_response); _data = data; return true; } catch (e) { - if (e.toString().contains("401")) { - if (await NetworkHelper.getNewToken(headers)) return await fetchData(); - } + if (e.toString().contains("401")) if (await NetworkHelper.getNewToken(headers)) return await fetchData(); _error = e.toString(); return false; } finally { diff --git a/lib/core/services/map.dart b/lib/core/services/map.dart index a904459a4..17d001974 100644 --- a/lib/core/services/map.dart +++ b/lib/core/services/map.dart @@ -14,8 +14,8 @@ class MapSearchService { _isLoading = true; try { /// fetch data - String? _response = await NetworkHelper.fetchData( - dotenv.get('MAP_BASE_ENDPOINT') + '?query=' + location + '®ion=0'); + String? _response = + await NetworkHelper.fetchData(dotenv.get('MAP_BASE_ENDPOINT') + '?query=' + location + '®ion=0'); if (_response != 'null') { /// parse data final data = mapSearchModelFromJson(_response!); diff --git a/lib/core/services/messages.dart b/lib/core/services/messages.dart index 6c7f6d3c9..9083e95f4 100644 --- a/lib/core/services/messages.dart +++ b/lib/core/services/messages.dart @@ -10,16 +10,14 @@ class MessageService { String? _error; late Messages _data; - Future fetchMyMessagesData( - int timestamp, Map authHeaders) async { + Future fetchMyMessagesData(int timestamp, Map authHeaders) async { _error = null; _isLoading = true; try { /// fetch data String _response = await NetworkHelper.authorizedFetch( - dotenv.get('MY_MESSAGES_API_ENDPOINT') + timestamp.toString(), - authHeaders); + dotenv.get('MY_MESSAGES_API_ENDPOINT') + timestamp.toString(), authHeaders); /// parse data final data = messagesFromJson(_response); @@ -40,10 +38,8 @@ class MessageService { var timestampEndpoint = '&start=' + timestamp.toString(); try { /// fetch data - String _response = await NetworkHelper.fetchData( - dotenv.get('TOPICS_API_ENDPOINT') + - topicsEndpoint + - timestampEndpoint); + String _response = + await NetworkHelper.fetchData(dotenv.get('TOPICS_API_ENDPOINT') + topicsEndpoint + timestampEndpoint); /// parse data final data = messagesFromJson(_response); diff --git a/lib/core/services/news.dart b/lib/core/services/news.dart index 72f7fb6fd..f4515d625 100644 --- a/lib/core/services/news.dart +++ b/lib/core/services/news.dart @@ -22,18 +22,13 @@ class NewsService { _isLoading = true; try { /// fetch data - String _response = await (NetworkHelper.authorizedFetch( - dotenv.get('NEWS_ENDPOINT'), headers)); + String _response = await (NetworkHelper.authorizedFetch(dotenv.get('NEWS_ENDPOINT'), headers)); /// parse data _newsModels = newsModelFromJson(_response); return true; } catch (e) { - if (e.toString().contains("401")) { - if (await NetworkHelper.getNewToken(headers)) { - return await fetchData(); - } - } + if (e.toString().contains("401")) if (await NetworkHelper.getNewToken(headers)) return await fetchData(); _error = e.toString(); return false; } finally { diff --git a/lib/core/services/notices.dart b/lib/core/services/notices.dart index 3133ee70c..edd7c50c5 100644 --- a/lib/core/services/notices.dart +++ b/lib/core/services/notices.dart @@ -17,13 +17,12 @@ class NoticesService { _isLoading = true; try { /// fetch data - String _response = - await NetworkHelper.fetchData(dotenv.get('NOTICES_ENDPOINT')); + String _response = await NetworkHelper.fetchData(dotenv.get('NOTICES_ENDPOINT')); /// parse data _noticesModel = noticesModelFromJson(_response); - // TODO: remove dummy data and call notices endpoint when available + // TODO: remove dummy data and call notices endpoint when available - December 2025 // _noticesModel = noticesModelFromJson('[{"notice-title": "Coronavirus Information","notice-banner-image": "https://mobile.ucsd.edu/feeds/_resources/media/promo-banners/covid-19-app-20-03-04.png","notice-banner-link": "https://go.ucsd.edu/38nb0Pf"}]'); return true; } catch (e) { diff --git a/lib/core/services/notifications.dart b/lib/core/services/notifications.dart index b97f68772..8107f4033 100644 --- a/lib/core/services/notifications.dart +++ b/lib/core/services/notifications.dart @@ -16,8 +16,7 @@ class NotificationService { _error = null; _isLoading = true; try { - String? response = await NetworkHelper.fetchData( - dotenv.get('NOTIFICATIONS_TOPICS_ENDPOINT')); + String? response = await NetworkHelper.fetchData(dotenv.get('NOTIFICATIONS_TOPICS_ENDPOINT')); if (response != null) { _topicsModel = topicsModelFromJson(response); return true; @@ -35,8 +34,8 @@ class NotificationService { Future postPushToken(Map headers, body) async { try { - String? response = await NetworkHelper.authorizedPost( - dotenv.get('NOTIFICATIONS_ENDPOINT') + '/register', headers, body); + String? response = + await NetworkHelper.authorizedPost(dotenv.get('NOTIFICATIONS_ENDPOINT') + '/register', headers, body); if (response == 'Success') { return true; } else { @@ -49,12 +48,11 @@ class NotificationService { } } - Future deletePushToken( - Map headers, String token) async { + Future deletePushToken(Map headers, String token) async { token = Uri.encodeComponent(token); try { - String? response = await NetworkHelper.authorizedDelete( - dotenv.get('NOTIFICATIONS_ENDPOINT') + '/token/' + token, headers); + String? response = + await NetworkHelper.authorizedDelete(dotenv.get('NOTIFICATIONS_ENDPOINT') + '/token/' + token, headers); if (response == 'Success') { return true; } else { diff --git a/lib/core/services/notifications_freefood.dart b/lib/core/services/notifications_freefood.dart index 2d843bfc1..11ae71039 100644 --- a/lib/core/services/notifications_freefood.dart +++ b/lib/core/services/notifications_freefood.dart @@ -22,11 +22,7 @@ class FreeFoodService { try { /// fetch data var _response = await NetworkHelper.authorizedFetch( - dotenv.get('NOTIFICATIONS_GOING_ENDPOINT') + - 'events/' + - id + - '/rsvpCount', - headers); + dotenv.get('NOTIFICATIONS_GOING_ENDPOINT') + 'events/' + id + '/rsvpCount', headers); /// parse data final data = freeFoodModelFromJson(_response); @@ -35,11 +31,7 @@ class FreeFoodService { } catch (e) { /// if the authorized fetch failed we know we have to refresh the /// token for this service - if (e.toString().contains("401")) { - if (await NetworkHelper.getNewToken(headers)) { - return await fetchData(id); - } - } + if (e.toString().contains("401")) if (await NetworkHelper.getNewToken(headers)) return await fetchData(id); _error = e.toString(); return false; } finally { @@ -51,10 +43,7 @@ class FreeFoodService { _error = null; _isLoading = true; try { - String _url = dotenv.get('NOTIFICATIONS_GOING_ENDPOINT') + - 'events/' + - id + - '/rsvpLimit'; + String _url = dotenv.get('NOTIFICATIONS_GOING_ENDPOINT') + 'events/' + id + '/rsvpLimit'; /// fetch data var _response = await NetworkHelper.authorizedFetch(_url, headers); @@ -66,11 +55,7 @@ class FreeFoodService { } catch (e) { /// if the authorized fetch failed we know we have to refresh the /// token for this service - if (e.toString().contains("401")) { - if (await NetworkHelper.getNewToken(headers)) { - return await fetchMaxCount(id); - } - } + if (e.toString().contains("401")) if (await NetworkHelper.getNewToken(headers)) return await fetchMaxCount(id); _error = e.toString(); return false; } finally { @@ -90,10 +75,8 @@ class FreeFoodService { } catch (e) { /// if the authorized fetch failed we know we have to refresh the /// token for this service - if (e.toString().contains("401")) { - if (await NetworkHelper.getNewToken(headers)) - return await updateCount(id, body); - } + if (e.toString().contains("401")) if (await NetworkHelper.getNewToken(headers)) + return await updateCount(id, body); _error = e.toString(); return false; } finally { diff --git a/lib/core/services/parking.dart b/lib/core/services/parking.dart index fbf0e541e..9065df2a4 100644 --- a/lib/core/services/parking.dart +++ b/lib/core/services/parking.dart @@ -24,8 +24,8 @@ class ParkingService { _isLoading = true; try { /// fetch data - String _response = await (NetworkHelper.authorizedFetch( - dotenv.get('PARKING_SERVICE_API_ENDPOINT') + "/status", headers)); + String _response = + await (NetworkHelper.authorizedFetch(dotenv.get('PARKING_SERVICE_API_ENDPOINT') + "/status", headers)); /// parse data _data = parkingModelFromJson(_response); @@ -33,10 +33,8 @@ class ParkingService { } catch (e) { /// if the authorized fetch failed we know we have to refresh the /// token for this service - if (e.toString().contains("401")) { - if (await NetworkHelper.getNewToken(headers)) - return await fetchParkingLotData(); - } + if (e.toString().contains("401")) if (await NetworkHelper.getNewToken(headers)) + return await fetchParkingLotData(); _error = e.toString(); return false; } finally { diff --git a/lib/core/services/scanner_message.dart b/lib/core/services/scanner_message.dart index bee0f9ea1..32f9e9816 100644 --- a/lib/core/services/scanner_message.dart +++ b/lib/core/services/scanner_message.dart @@ -17,8 +17,7 @@ class ScannerMessageService { _isLoading = true; try { /// fetch data - String _response = await NetworkHelper.authorizedFetch( - dotenv.get('SCANNER_MESSAGE_ENDPOINT'), headers); + String _response = await NetworkHelper.authorizedFetch(dotenv.get('SCANNER_MESSAGE_ENDPOINT'), headers); /// parse data _scannerMessageModel = scannerMessageModelFromJson(_response); diff --git a/lib/core/services/shuttle.dart b/lib/core/services/shuttle.dart index 889ad12a8..5d2f2079f 100644 --- a/lib/core/services/shuttle.dart +++ b/lib/core/services/shuttle.dart @@ -26,8 +26,7 @@ class ShuttleService { _isLoading = true; try { /// fetch data - String _response = await (NetworkHelper.authorizedFetch( - dotenv.get('SHUTTLE_API_ENDPOINT'), headers)); + String _response = await (NetworkHelper.authorizedFetch(dotenv.get('SHUTTLE_API_ENDPOINT'), headers)); /// parse data var data = shuttleStopModelFromJson(_response); @@ -36,9 +35,7 @@ class ShuttleService { } catch (e) { /// if the authorized fetch failed we know we have to refresh the /// token for this service - if (e.toString().contains("401")) { - if (await NetworkHelper.getNewToken(headers)) return await fetchData(); - } + if (e.toString().contains("401")) if (await NetworkHelper.getNewToken(headers)) return await fetchData(); _error = e.toString(); return false; } finally { @@ -51,8 +48,8 @@ class ShuttleService { _isLoading = true; try { /// fetch data - String _response = await (NetworkHelper.authorizedFetch( - dotenv.get('SHUTTLE_API_ENDPOINT') + "/$stopId/arrivals", headers)); + String _response = + await (NetworkHelper.authorizedFetch(dotenv.get('SHUTTLE_API_ENDPOINT') + "/$stopId/arrivals", headers)); /// parse data final arrivingData = getArrivingShuttles(_response); @@ -60,10 +57,8 @@ class ShuttleService { } catch (e) { /// if the authorized fetch failed we know we have to refresh the /// token for this service - if (e.toString().contains("401")) { - if (await NetworkHelper.getNewToken(headers)) - return await getArrivingInformation(stopId); - } + if (e.toString().contains("401")) if (await NetworkHelper.getNewToken(headers)) + return await getArrivingInformation(stopId); _error = e.toString(); return []; } finally { diff --git a/lib/core/services/speed_test.dart b/lib/core/services/speed_test.dart index 6b5b2f81a..e521897a9 100644 --- a/lib/core/services/speed_test.dart +++ b/lib/core/services/speed_test.dart @@ -29,14 +29,10 @@ class SpeedTestService { try { if (Platform.isAndroid) { AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; - if (!androidInfo.isPhysicalDevice) { - return true; - } + if (!androidInfo.isPhysicalDevice) return true; } else if (Platform.isIOS) { IosDeviceInfo iosInfo = await deviceInfo.iosInfo; - if (!iosInfo.isPhysicalDevice) { - return true; - } + if (!iosInfo.isPhysicalDevice) return true; } } catch (exception) { print(exception.toString()); @@ -50,15 +46,13 @@ class SpeedTestService { try { await NetworkHelper.getNewToken(headers); // Get download & upload urls - String? _downloadResponse = await NetworkHelper.authorizedFetch( - dotenv.get('SPEED_TEST_DOWNLOAD_ENDPOINT'), headers); - String? _uploadResponse = await NetworkHelper.authorizedFetch( - dotenv.get('SPEED_TEST_UPLOAD_ENDPOINT'), headers); + String? _downloadResponse = + await NetworkHelper.authorizedFetch(dotenv.get('SPEED_TEST_DOWNLOAD_ENDPOINT'), headers); + String? _uploadResponse = await NetworkHelper.authorizedFetch(dotenv.get('SPEED_TEST_UPLOAD_ENDPOINT'), headers); /// parse data await fetchNetworkDiagnostics().then((WifiInfo? data) { - _speedTestModel = speedTestModelFromJson( - data, _downloadResponse!, _uploadResponse!, data != null); + _speedTestModel = speedTestModelFromJson(data, _downloadResponse!, _uploadResponse!, data != null); }); return true; } catch (exception) { @@ -74,8 +68,9 @@ class SpeedTestService { Future fetchNetworkDiagnostics() async { _isLoading = true; // Check connected to wifi - if (!(await _connectivity.checkConnectivity()) - .contains(ConnectivityResult.wifi)) { + final List connectivity = await _connectivity.checkConnectivity(); + final bool isNotOnWiFi = !connectivity.contains(ConnectivityResult.wifi); + if (isNotOnWiFi) { _isLoading = false; return null; } diff --git a/lib/core/services/spot_types.dart b/lib/core/services/spot_types.dart index e267090ce..316ceb140 100644 --- a/lib/core/services/spot_types.dart +++ b/lib/core/services/spot_types.dart @@ -24,8 +24,7 @@ class SpotTypesService { _isLoading = true; try { /// fetch data - String _response = - await NetworkHelper.fetchData(dotenv.get('SPOT_TYPES_ENDPOINT')); + String _response = await NetworkHelper.fetchData(dotenv.get('SPOT_TYPES_ENDPOINT')); _spotTypeModel = spotTypeModelFromJson(_response); return true; } catch (e) { diff --git a/lib/core/services/student_id.dart b/lib/core/services/student_id.dart index 556bd2159..0e6c40b69 100644 --- a/lib/core/services/student_id.dart +++ b/lib/core/services/student_id.dart @@ -18,14 +18,13 @@ class StudentIdService { StudentIdPhotoModel _studentIdPhotoModel = StudentIdPhotoModel(); StudentIdProfileModel _studentIdProfileModel = StudentIdProfileModel(); - //Removed term (not used) + // Removed term (not used) Future fetchStudentIdName(Map headers) async { _error = null; _isLoading = true; try { /// fetch data - String _response = await NetworkHelper.authorizedFetch( - myStudentContactApiUrl + '/display_name', headers); + String _response = await NetworkHelper.authorizedFetch(myStudentContactApiUrl + '/display_name', headers); /// parse data _studentIdNameModel = studentIdNameModelFromJson(_response); @@ -44,8 +43,7 @@ class StudentIdService { _isLoading = true; try { /// fetch data - String _response = await NetworkHelper.authorizedFetch( - myStudentContactApiUrl + '/photo', headers); + String _response = await NetworkHelper.authorizedFetch(myStudentContactApiUrl + '/photo', headers); /// parse data _studentIdPhotoModel = studentIdPhotoModelFromJson(_response); @@ -64,8 +62,7 @@ class StudentIdService { _isLoading = true; try { /// fetch data - String _response = await NetworkHelper.authorizedFetch( - myStudentProfileApiUrl + '/profile', headers); + String _response = await NetworkHelper.authorizedFetch(myStudentProfileApiUrl + '/profile', headers); _studentIdProfileModel = studentIdProfileModelFromJson(_response); return true; diff --git a/lib/core/services/user.dart b/lib/core/services/user.dart index a0b97c459..a46f3e3ff 100644 --- a/lib/core/services/user.dart +++ b/lib/core/services/user.dart @@ -18,8 +18,8 @@ class UserProfileService { _error = null; _isLoading = true; try { - _userProfileModel = userProfileModelFromJson( - await NetworkHelper.authorizedFetch(_endpoint + '/profile', headers)); + _userProfileModel = + userProfileModelFromJson(await NetworkHelper.authorizedFetch(_endpoint + '/profile', headers)); return true; } catch (e) { _error = e.toString(); @@ -29,16 +29,13 @@ class UserProfileService { } } - Future uploadUserProfile( - Map headers, Map body) async { + Future uploadUserProfile(Map headers, Map body) async { _error = null; _isLoading = true; try { - final response = await NetworkHelper.authorizedPost( - _endpoint + '/profile', headers, createAttributeValueJson(body)); - return response.toString() == 'Success' - ? true - : throw response.toString(); + final response = + await NetworkHelper.authorizedPost(_endpoint + '/profile', headers, createAttributeValueJson(body)); + return response.toString() == 'Success' ? true : throw response.toString(); } catch (e) { _error = e.toString(); } finally { @@ -51,8 +48,7 @@ class UserProfileService { /// required json format: /// [{'attribute': 'name of column to edit in db, 'value': 'value to put in db'}] /// if attribute does not exists in db then it will be created - List> createAttributeValueJson( - Map json) { + List> createAttributeValueJson(Map json) { List> correctlyFormattedData = []; json.forEach((key, value) { correctlyFormattedData.add({"attribute": key, "value": value}); diff --git a/lib/core/utils/maps.dart b/lib/core/utils/maps.dart index 20685b122..db9b95ab4 100644 --- a/lib/core/utils/maps.dart +++ b/lib/core/utils/maps.dart @@ -1,16 +1,13 @@ import 'dart:math' as Math; double getHaversineDistance(lat1, lon1, lat2, lon2) { - var R = 6371; // Radius of the earth in km + var r = 6371; // Radius of the earth in km var dLat = deg2rad(lat2 - lat1); // deg2rad below var dLon = deg2rad(lon2 - lon1); var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + - Math.cos(deg2rad(lat1)) * - Math.cos(deg2rad(lat2)) * - Math.sin(dLon / 2) * - Math.sin(dLon / 2); + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - var d = R * c; // Distance in km + var d = r * c; // Distance in km return d; } diff --git a/lib/core/utils/webview.dart b/lib/core/utils/webview.dart index e36c2b2c8..273087f51 100644 --- a/lib/core/utils/webview.dart +++ b/lib/core/utils/webview.dart @@ -4,8 +4,7 @@ import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:webview_flutter/webview_flutter.dart'; -void reloadWebView(String url, WebViewController controller) => - controller.loadRequest(Uri.parse(url)); +void reloadWebView(String url, WebViewController controller) => controller.loadRequest(Uri.parse(url)); void openLink(String url) async { try { @@ -16,8 +15,7 @@ void openLink(String url) async { } double validateHeight(context, height) { - double maxHeight = - MediaQuery.of(context).size.height * 0.6; // Limit to 60% of screen height + double maxHeight = MediaQuery.of(context).size.height * 0.6; // Limit to 60% of screen height if (height == null || height < cardContentMinHeight) { height = cardContentMinHeight; } else if (height > maxHeight) { diff --git a/lib/core/wrappers/push_notifications.dart b/lib/core/wrappers/push_notifications.dart index 392e95739..5207e718c 100644 --- a/lib/core/wrappers/push_notifications.dart +++ b/lib/core/wrappers/push_notifications.dart @@ -7,16 +7,14 @@ class PushNotificationWrapper extends StatefulWidget { final Widget child; @override - _PushNotificationWrapperState createState() => - _PushNotificationWrapperState(); + _PushNotificationWrapperState createState() => _PushNotificationWrapperState(); } class _PushNotificationWrapperState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - Provider.of(context) - .initPlatformState(context); + Provider.of(context).initPlatformState(context); } @override diff --git a/lib/main.dart b/lib/main.dart index 64f6831ba..cd80ff554 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,8 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:campus_mobile_experimental/app_constants.dart'; import 'package:campus_mobile_experimental/app_provider.dart'; -import 'package:campus_mobile_experimental/app_router.dart' - as campusMobileRouter; +import 'package:campus_mobile_experimental/app_router.dart' as campusMobileRouter; import 'package:campus_mobile_experimental/app_styles.dart'; import 'package:campus_mobile_experimental/core/models/authentication.dart'; import 'package:campus_mobile_experimental/core/models/user_profile.dart'; @@ -32,8 +31,7 @@ void main() async { final mapsImplementation = GoogleMapsFlutterPlatform.instance; if (mapsImplementation is GoogleMapsFlutterAndroid) { WidgetsFlutterBinding.ensureInitialized(); - await mapsImplementation - .initializeWithRenderer(AndroidMapRenderer.latest); + await mapsImplementation.initializeWithRenderer(AndroidMapRenderer.latest); } // dotenv loading @@ -59,7 +57,7 @@ Future initializeHive() async { Future initializeApp() async { final prefs = await SharedPreferences.getInstance(); - // TODO: fix this. We don't need two different persistent flags... + // TODO: fix this. We don't need two different persistent flags... - December 2025 if (prefs.getBool('first_run') ?? true) { await clearSecuredStorage(); await clearHiveStorage(); @@ -76,13 +74,12 @@ Future clearSecuredStorage() async { await storage.deleteAll(); } -// TODO: refactor this to load multiple futures in one statement +// TODO: refactor this to load multiple futures in one statement - December 2025 Future clearHiveStorage() async { - await (await Hive.openBox(DataPersistence.cardStates)).deleteFromDisk(); - await (await Hive.openBox(DataPersistence.cardOrder)).deleteFromDisk(); - await (await Hive.openBox(DataPersistence.AuthenticationModel)) - .deleteFromDisk(); - await (await Hive.openBox(DataPersistence.UserProfileModel)).deleteFromDisk(); + await (await Hive.openBox(DataPersistence.CARD_STATES)).deleteFromDisk(); + await (await Hive.openBox(DataPersistence.CARD_ORDER)).deleteFromDisk(); + await (await Hive.openBox(DataPersistence.AUTHENTICATION_MODEL)).deleteFromDisk(); + await (await Hive.openBox(DataPersistence.USER_PROFILE_MODEL)).deleteFromDisk(); } class CampusMobile extends StatelessWidget { @@ -149,17 +146,13 @@ class CampusMobile extends StatelessWidget { debugShowCheckedModeBanner: true, title: 'UC San Diego', theme: lightTheme.copyWith( - colorScheme: - lightTheme.colorScheme.copyWith(secondary: darkAccentColor), + colorScheme: lightTheme.colorScheme.copyWith(secondary: darkAccentColor), ), darkTheme: darkTheme.copyWith( - colorScheme: - darkTheme.colorScheme.copyWith(secondary: lightAccentColor), + colorScheme: darkTheme.colorScheme.copyWith(secondary: lightAccentColor), ), themeMode: ThemeMode.system, - initialRoute: showOnboardingScreen - ? RoutePaths.OnboardingLogin - : RoutePaths.BottomNavigationBar, + initialRoute: showOnboardingScreen ? RoutePaths.ONBOARDING_LOGIN : RoutePaths.BOTTOM_NAVIGATION_BAR, onGenerateRoute: campusMobileRouter.Router.generateRoute, navigatorObservers: [observer], builder: (context, child) { diff --git a/lib/ui/availability/availability_card.dart b/lib/ui/availability/availability_card.dart index e9653f2bc..deb07cc37 100644 --- a/lib/ui/availability/availability_card.dart +++ b/lib/ui/availability/availability_card.dart @@ -36,19 +36,22 @@ class _AvailabilityCardState extends State { Widget build(BuildContext context) { return CardContainer( active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), - reload: () => _availabilityDataProvider.fetchAvailability(), + hide: () => Provider.of(context, listen: false).toggleCard(cardId), + reload: () { + setState(() { + _currentPage = 0; + }); + _controller.animateToPage(0, duration: Duration(milliseconds: 300), curve: Curves.easeInOut); + _availabilityDataProvider.fetchAvailability(); + }, isLoading: _availabilityDataProvider.isLoading, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[cardId]!, errorText: _availabilityDataProvider.error, - child: () => - buildAvailabilityCard(_availabilityDataProvider.availabilityModels), + child: () => buildAvailabilityCard(_availabilityDataProvider.availabilityModels), actionButtons: [ ActionLink( buttonText: 'MANAGE LOCATIONS', - onPressed: () => - Navigator.pushNamed(context, RoutePaths.ManageAvailabilityView)) + onPressed: () => Navigator.pushNamed(context, RoutePaths.MANAGE_AVAILABILITY_VIEW)) ], ); } @@ -61,11 +64,9 @@ class _AvailabilityCardState extends State { if (model != null) { String curName = model.name; RegExpMatch? match = multiPager.firstMatch(curName); - if (match != null) - curName = curName.replaceRange(match.start, match.end, ''); - if (_availabilityDataProvider.locationViewState[curName]!) { - locationsList.add(AvailabilityDisplay(model: model)); - } + if (match != null) curName = curName.replaceRange(match.start, match.end, ''); + final bool shouldShowLocation = _availabilityDataProvider.locationViewState[curName]!; + if (shouldShowLocation) locationsList.add(AvailabilityDisplay(model: model)); } } @@ -121,9 +122,8 @@ class _AvailabilityCardState extends State { dotsCount: locationsList.length, decorator: DotsDecorator( color: dotsUnselectedColor, - activeColor: Theme.of(context).brightness == Brightness.dark - ? dotsSelectedColorDark - : dotsSelectedColorLight, + activeColor: + Theme.of(context).brightness == Brightness.dark ? dotsSelectedColorDark : dotsSelectedColorLight, activeSize: const Size(22.0, 22.0), size: const Size(10.0, 10.0), ), diff --git a/lib/ui/availability/availability_detail_view.dart b/lib/ui/availability/availability_detail_view.dart index bcd5ce574..e6b7f5873 100644 --- a/lib/ui/availability/availability_detail_view.dart +++ b/lib/ui/availability/availability_detail_view.dart @@ -20,10 +20,7 @@ class AvailabilityDetailedView extends StatelessWidget { list.add(ListTile( title: Text( "${subLocation.name}", - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - fontSize: 24, - fontWeight: FontWeight.bold), + style: TextStyle(color: Theme.of(context).colorScheme.secondary, fontSize: 24, fontWeight: FontWeight.bold), ), )); @@ -34,17 +31,14 @@ class AvailabilityDetailedView extends StatelessWidget { ListTile( title: Text( "${floor.name}", - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - fontSize: LOCATION_FONT_SIZE), + style: TextStyle(color: Theme.of(context).colorScheme.secondary, fontSize: LOCATION_FONT_SIZE), ), subtitle: Column( children: [ Align( alignment: Alignment.centerLeft, child: Text( - (100 * percentAvailability(floor)).toInt().toString() + - '% Busy', + (100 * percentAvailability(floor)).toInt().toString() + '% Busy', )), Align( alignment: Alignment.centerLeft, @@ -74,9 +68,7 @@ class AvailabilityDetailedView extends StatelessWidget { final dividedFloorTiles = ListTile.divideTiles( context: context, tiles: floorTiles, - color: Theme.of(context).brightness == Brightness.dark - ? listTileDividerColorDark - : listTileDividerColorLight, + color: Theme.of(context).brightness == Brightness.dark ? listTileDividerColorDark : listTileDividerColorLight, ).toList(); list.addAll(dividedFloorTiles); @@ -90,8 +82,7 @@ class AvailabilityDetailedView extends StatelessWidget { ); } - num percentAvailability(Floor subLocationFloor) => - subLocationFloor.percentage; + num percentAvailability(Floor subLocationFloor) => subLocationFloor.percentage; setIndicatorColor(num percentage) { if (percentage >= .75) diff --git a/lib/ui/availability/availability_display.dart b/lib/ui/availability/availability_display.dart index 0c4760396..dc12766db 100644 --- a/lib/ui/availability/availability_display.dart +++ b/lib/ui/availability/availability_display.dart @@ -55,7 +55,7 @@ class AvailabilityDisplay extends StatelessWidget { if (subLocation.floors.isNotEmpty) { Navigator.pushNamed( context, - RoutePaths.AvailabilityDetailedView, + RoutePaths.AVAILABILITY_DETAILED_VIEW, arguments: subLocation, ); } @@ -77,10 +77,8 @@ class AvailabilityDisplay extends StatelessWidget { ? textButtonSmallDark : textButtonSmallLight) : (Theme.of(context).brightness == Brightness.dark - ? descriptiveTextSmallDark.copyWith( - fontSize: 22.0) - : descriptiveTextSmallLight.copyWith( - fontSize: 22.0)), + ? descriptiveTextSmallDark.copyWith(fontSize: 22.0) + : descriptiveTextSmallLight.copyWith(fontSize: 22.0)), ), SizedBox(height: 4), Text( @@ -99,24 +97,20 @@ class AvailabilityDisplay extends StatelessWidget { Icon( Icons.arrow_forward_ios, size: 35, - color: Theme.of(context).brightness == Brightness.dark - ? linkColorLight - : linkColorDark, + color: Theme.of(context).brightness == Brightness.dark ? linkColorLight : linkColorDark, ), ], ), ], ), - SizedBox(height: 6), SizedBox( - height: 12, + height: 6, width: double.infinity, child: ClipRRect( borderRadius: BorderRadius.circular(BORDER_RADIUS), child: LinearProgressIndicator( - value: (percentAvailability(subLocation) <= 0.01) - ? 0.01 - : percentAvailability(subLocation).toDouble(), + value: + (percentAvailability(subLocation) <= 0.01) ? 0.01 : percentAvailability(subLocation).toDouble(), backgroundColor: Colors.grey[BACKGROUND_GREY_SHADE], valueColor: AlwaysStoppedAnimation( setIndicatorColor(percentAvailability(subLocation)), @@ -130,16 +124,15 @@ class AvailabilityDisplay extends StatelessWidget { ); }).toList(); - return Flexible( + return Expanded( child: Scrollbar( child: ListView( physics: NeverScrollableScrollPhysics(), children: ListTile.divideTiles( tiles: locations, context: context, - color: Theme.of(context).brightness == Brightness.dark - ? listTileDividerColorDark - : listTileDividerColorLight, + color: + Theme.of(context).brightness == Brightness.dark ? listTileDividerColorDark : listTileDividerColorLight, ).toList(), ), ), diff --git a/lib/ui/availability/manage_availability_view.dart b/lib/ui/availability/manage_availability_view.dart index d7600fb36..d1cfae141 100644 --- a/lib/ui/availability/manage_availability_view.dart +++ b/lib/ui/availability/manage_availability_view.dart @@ -24,9 +24,8 @@ class _ManageAvailabilityViewState extends State { return ReorderableListView( header: Padding( padding: const EdgeInsets.only(top: 10), - child: Text("Hold and drag to reorder", - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall), + child: + Text("Hold and drag to reorder", textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodySmall), ), children: createList(context), onReorder: _onReorder, @@ -35,18 +34,13 @@ class _ManageAvailabilityViewState extends State { void _onReorder(int oldIndex, int newIndex) { final multiPager = RegExp(r' \((\d+)/(\d+)\)$'); - List newOrder = - _availabilityDataProvider.availabilityModels; + List newOrder = _availabilityDataProvider.availabilityModels; List extraPages = []; // -----Must remove pages after head of multi pagers and reinsert later to avoid reordering errors----- for (AvailabilityModel? item in newOrder) { RegExpMatch? match = multiPager.firstMatch(item!.name); - if (match != null) { - if (match.group(1) != "1") { - extraPages.add(item); - } - } + if (match != null) if (match.group(1) != "1") extraPages.add(item); } for (AvailabilityModel? item in extraPages) { newOrder.remove(item); @@ -65,14 +59,12 @@ class _ManageAvailabilityViewState extends State { RegExpMatch? match = multiPager.firstMatch(orderedLocationNames[index]!); if (match != null) { if (match.group(1) == "1") { - String baseName = orderedLocationNames[index]! - .replaceRange(match.start, match.end, ''); + String baseName = orderedLocationNames[index]!.replaceRange(match.start, match.end, ''); var curPageIndex = 2; var maxPageIndex = int.parse(match.group(2)!); while (curPageIndex <= maxPageIndex) { index++; - orderedLocationNames.insert( - index, baseName + " ($curPageIndex/$maxPageIndex)"); + orderedLocationNames.insert(index, baseName + " ($curPageIndex/$maxPageIndex)"); curPageIndex++; } } else { @@ -88,13 +80,11 @@ class _ManageAvailabilityViewState extends State { List list = []; Set existingKeys = {}; final multiPager = RegExp(r' \(\d+/\d+\)$'); - for (AvailabilityModel? model - in _availabilityDataProvider.availabilityModels) { + for (AvailabilityModel? model in _availabilityDataProvider.availabilityModels) { if (model != null) { var curName = model.name; RegExpMatch? match = multiPager.firstMatch(curName); - if (match != null) - curName = curName.replaceRange(match.start, match.end, ''); + if (match != null) curName = curName.replaceRange(match.start, match.end, ''); if (existingKeys.contains(curName)) continue; existingKeys.add(curName); list.add(Card( @@ -108,21 +98,17 @@ class _ManageAvailabilityViewState extends State { ), leading: Icon( Icons.drag_handle, - color: Theme.of(context).brightness == Brightness.dark - ? linkTextColorDark - : linkTextColorLight, + color: Theme.of(context).brightness == Brightness.dark ? linkTextColorDark : linkTextColorLight, ), trailing: Transform.scale( scale: 0.9, // Adjust the scale as needed child: Switch.adaptive( - value: Provider.of(context) - .locationViewState[curName]!, + value: Provider.of(context).locationViewState[curName]!, // activeColor: Theme.of(context).buttonColor, activeColor: toggleActiveColor, thumbColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - return Colors.white; - } + final bool isSelected = states.contains(WidgetState.selected); + if (isSelected) return Colors.white; return null; }), onChanged: (_) { diff --git a/lib/ui/classes/classes_card.dart b/lib/ui/classes/classes_card.dart index a7ea5168a..b1b5af590 100644 --- a/lib/ui/classes/classes_card.dart +++ b/lib/ui/classes/classes_card.dart @@ -18,18 +18,16 @@ class ClassScheduleCard extends StatelessWidget { Widget build(BuildContext context) { return CardContainer( active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), + hide: () => Provider.of(context, listen: false).toggleCard(cardId), reload: () { - if (Provider.of(context, listen: false) - .isLoading) + final bool isLoading = Provider.of(context, listen: false).isLoading; + if (isLoading) return null; else - Provider.of(context, listen: false) - .fetchData(); + Provider.of(context, listen: false).fetchData(); }, isLoading: Provider.of(context).isLoading, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[cardId]!, errorText: Provider.of(context).error, child: () => buildClassScheduleCard( context, @@ -42,19 +40,15 @@ class ClassScheduleCard extends StatelessWidget { ActionButton( buttonText: 'VIEW ALL CLASSES', onPressed: () { - Navigator.pushNamed(context, RoutePaths.ClassScheduleViewAll); + Navigator.pushNamed(context, RoutePaths.CLASS_SCHEDULE_VIEW_ALL); }, ), ], ); } - Widget buildClassScheduleCard( - BuildContext context, - List courseData, - int selectedCourse, - DateTime lastUpdated, - String nextDayWithClasses) { + Widget buildClassScheduleCard(BuildContext context, List courseData, int selectedCourse, + DateTime lastUpdated, String nextDayWithClasses) { try { final section = courseData[selectedCourse]; return Padding( @@ -75,16 +69,13 @@ class ClassScheduleCard extends StatelessWidget { style: TextStyle( fontSize: 22.0, color: - Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, fontWeight: FontWeight.w500, ), ), SizedBox(height: 8), // CSE 141L - buildClassCode(context, - '${section.subjectCode} ${section.courseCode}'), + buildClassCode(context, '${section.subjectCode} ${section.courseCode}'), SizedBox(height: 3), // Laboratory buildClassType(context, section.meetingType!), @@ -93,8 +84,7 @@ class ClassScheduleCard extends StatelessWidget { buildTimeRow(context, section.days!, section.time), SizedBox(height: 8), // Classroom Location: - buildLocationRow( - context, '${section.building} ${section.room}'), + buildLocationRow(context, '${section.building} ${section.room}'), SizedBox(height: 8), // Evaluation Option: buildGradeEvaluationRow(context, section.gradeOption), @@ -120,10 +110,8 @@ class ClassScheduleCard extends StatelessWidget { Flexible( flex: 4, // Adjusts width of Right Hand Side child: Column( - mainAxisAlignment: - MainAxisAlignment.start, // Aligns content to the top - crossAxisAlignment: - CrossAxisAlignment.start, // Align text to the left + mainAxisAlignment: MainAxisAlignment.start, // Aligns content to the top + crossAxisAlignment: CrossAxisAlignment.start, // Align text to the left children: [ UpcomingCoursesList(), ], @@ -159,9 +147,7 @@ class ClassScheduleCard extends StatelessWidget { fontSize: 17.0, fontFamily: 'Refrigerator Deluxe', letterSpacing: 1.2, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, fontWeight: FontWeight.w900)); } @@ -173,9 +159,8 @@ class ClassScheduleCard extends StatelessWidget { classType, style: TextStyle( fontSize: 17, - color: Theme.of(context).brightness == Brightness.light - ? descriptiveTextColorLight - : descriptiveTextColorDark, + color: + Theme.of(context).brightness == Brightness.light ? descriptiveTextColorLight : descriptiveTextColorDark, fontWeight: FontWeight.w400, ), ), @@ -190,9 +175,7 @@ class ClassScheduleCard extends StatelessWidget { children: [ Icon( Icons.access_time, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, size: 34, ), SizedBox(width: 10), @@ -213,9 +196,7 @@ class ClassScheduleCard extends StatelessWidget { Text((day ?? 'TBA') + ' @ ' + (time ?? 'TBA'), style: TextStyle( fontSize: 15, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, fontWeight: FontWeight.w700)) ], ), @@ -231,9 +212,7 @@ class ClassScheduleCard extends StatelessWidget { children: [ Icon( Icons.location_city, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, size: 34, ), SizedBox(width: 10), @@ -256,9 +235,7 @@ class ClassScheduleCard extends StatelessWidget { style: TextStyle( fontSize: 15, letterSpacing: 0.8, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, fontWeight: FontWeight.w700), ), ], @@ -274,9 +251,7 @@ class ClassScheduleCard extends StatelessWidget { child: Row( children: [ Icon(Icons.check_box_outlined, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, size: 34), SizedBox(width: 10), Column( @@ -297,9 +272,7 @@ class ClassScheduleCard extends StatelessWidget { gradeEvaluation, style: TextStyle( fontSize: 16, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, fontWeight: FontWeight.w700), ), ], diff --git a/lib/ui/classes/classes_list.dart b/lib/ui/classes/classes_list.dart index a6227367f..b1f63d798 100644 --- a/lib/ui/classes/classes_list.dart +++ b/lib/ui/classes/classes_list.dart @@ -20,32 +20,21 @@ class ClassList extends StatelessWidget { Provider.of(context) .enrolledClasses .addAll(Provider.of(context).midterms); - Provider.of(context) - .enrolledClasses - .keys - .forEach( + Provider.of(context).enrolledClasses.keys.forEach( (key) { - if (Provider.of(context) - .enrolledClasses[key]! - .isNotEmpty) { + final bool hasClasses = Provider.of(context).enrolledClasses[key]!.isNotEmpty; + if (hasClasses) { list.add(SliverStickyHeader( header: buildWeekDayHeader(context, key), sliver: SliverList( delegate: SliverChildBuilderDelegate((context, index) { if (key == 'MI') { return buildMidterm( - Provider.of(context) - .enrolledClasses[key]! - .elementAt(index)); + Provider.of(context).enrolledClasses[key]!.elementAt(index)); } return buildClass( - Provider.of(context) - .enrolledClasses[key]! - .elementAt(index)); - }, - childCount: Provider.of(context) - .enrolledClasses[key]! - .length), + Provider.of(context).enrolledClasses[key]!.elementAt(index)); + }, childCount: Provider.of(context).enrolledClasses[key]!.length), ), )); } @@ -78,9 +67,7 @@ class ClassList extends StatelessWidget { Widget buildWeekDayHeader(BuildContext context, String weekday) { weekday = abbrevToFullWeekday(weekday); return Container( - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : descriptiveTextColorLight, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : descriptiveTextColorLight, child: Padding( padding: const EdgeInsets.all(12.0), child: Text( @@ -103,15 +90,13 @@ class ClassList extends StatelessWidget { children: [ Text( sectionData.subjectCode! + ' ' + sectionData.courseCode!, - style: - TextStyle(fontWeight: FontWeight.bold, fontSize: 17.0), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 17.0), ), Text( sectionData.courseTitle!, style: TextStyle(fontSize: 17.0), ), - Text(sectionData.instructorName!, - style: TextStyle(fontSize: 17.0)), + Text(sectionData.instructorName!, style: TextStyle(fontSize: 17.0)), Padding( padding: const EdgeInsets.only(top: 5.0), child: Row(children: [ @@ -163,8 +148,7 @@ class ClassList extends StatelessWidget { children: [ Text( sectionData.subjectCode! + ' ' + sectionData.courseCode!, - style: - TextStyle(fontWeight: FontWeight.bold, fontSize: 17.0), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 17.0), ), Text( sectionData.courseTitle!, @@ -175,10 +159,7 @@ class ClassList extends StatelessWidget { Padding( padding: const EdgeInsets.only(top: 5.0), child: Row(children: [ - Text(abbrevToFullWeekday(sectionData.days) + - ", " + - formatDate(sectionData.date)! + - ' from '), + Text(abbrevToFullWeekday(sectionData.days) + ", " + formatDate(sectionData.date)! + ' from '), TimeRangeWidget( time: sectionData.time!, ) @@ -192,15 +173,11 @@ class ClassList extends StatelessWidget { } String? formatDate(String? date) { - if (date == null) { - return null; - } + if (date == null) return null; String month = date.substring(5, 7); String day = date.substring(8); - if (day.startsWith("0")) { - day = day.substring(1); - } + if (day.startsWith("0")) day = day.substring(1); switch (month) { case "01": diff --git a/lib/ui/classes/upcoming_classes.dart b/lib/ui/classes/upcoming_classes.dart index f0b523c79..e5bd68bab 100644 --- a/lib/ui/classes/upcoming_classes.dart +++ b/lib/ui/classes/upcoming_classes.dart @@ -7,24 +7,20 @@ import 'package:campus_mobile_experimental/app_styles.dart'; class UpcomingCoursesList extends StatelessWidget { @override Widget build(BuildContext context) { - List data = - Provider.of(context).upcomingCourses; - int? selectedCourseIndex = - Provider.of(context).selectedCourse; + List data = Provider.of(context).upcomingCourses; + int? selectedCourseIndex = Provider.of(context).selectedCourse; return buildListOfCourses(context, data, selectedCourseIndex); } // Right Hand Side of Classes Card // - Widget buildListOfCourses( - BuildContext context, List data, int? selectedCourse) { + Widget buildListOfCourses(BuildContext context, List data, int? selectedCourse) { // Builds Today's Schedule using buildTile List listOfCourses = List.generate( data.length * 2 - 1, // Adjust length to account for dividers (int index) { if (index.isEven) { // Show a tile at even indexes - int itemIndex = - index ~/ 2; // Convert index back to original data index + int itemIndex = index ~/ 2; // Convert index back to original data index return buildTile(itemIndex, selectedCourse, data[itemIndex], context); } else { ///////////////// Horizontal Division /////////////////// @@ -45,9 +41,7 @@ class UpcomingCoursesList extends StatelessWidget { 'Today\'s Schedule', style: TextStyle( fontSize: 22.0, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, fontWeight: FontWeight.w500, ), ), @@ -60,15 +54,12 @@ class UpcomingCoursesList extends StatelessWidget { ); } - Widget buildTile( - int index, int? selectedCourse, SectionData data, BuildContext context) { + Widget buildTile(int index, int? selectedCourse, SectionData data, BuildContext context) { bool isSelected = index == selectedCourse; return ListTile( dense: true, contentPadding: EdgeInsets.symmetric(horizontal: 0), - onTap: () => - Provider.of(context, listen: false) - .selectCourse(index), + onTap: () => Provider.of(context, listen: false).selectCourse(index), title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/ui/common/action_button.dart b/lib/ui/common/action_button.dart index 51f67ea84..a61387716 100644 --- a/lib/ui/common/action_button.dart +++ b/lib/ui/common/action_button.dart @@ -15,8 +15,7 @@ class ActionButton extends StatelessWidget { Widget build(BuildContext context) { return TextButton( style: ButtonStyle( - backgroundColor: - WidgetStatePropertyAll(actionButtonBackgroundColor), + backgroundColor: WidgetStatePropertyAll(actionButtonBackgroundColor), shape: WidgetStatePropertyAll( RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8)), diff --git a/lib/ui/common/alert_dialog_widget.dart b/lib/ui/common/alert_dialog_widget.dart index eced89131..ab61b2b3f 100644 --- a/lib/ui/common/alert_dialog_widget.dart +++ b/lib/ui/common/alert_dialog_widget.dart @@ -65,8 +65,7 @@ class AlertDialogWidget extends StatelessWidget { contentPadding: EdgeInsets.fromLTRB(0, 5, 0, 20), backgroundColor: errorAlertDialogLightTheme.colorScheme.surface, shape: RoundedRectangleBorder( - side: - BorderSide(color: errorAlertDialogLightTheme.colorScheme.primary), + side: BorderSide(color: errorAlertDialogLightTheme.colorScheme.primary), borderRadius: BorderRadius.all(Radius.circular(5.0)), ), title: Row( diff --git a/lib/ui/common/base64_image_widget.dart b/lib/ui/common/base64_image_widget.dart index 2050ae88a..90ce46b7f 100644 --- a/lib/ui/common/base64_image_widget.dart +++ b/lib/ui/common/base64_image_widget.dart @@ -21,11 +21,10 @@ class Base64ImageWidget extends StatelessWidget { @override Widget build(BuildContext context) { // Check if base64String is valid and not empty - if (base64String == null || - base64String!.isEmpty || - base64String!.trim().isEmpty) { - return _buildPlaceholder(); - } + final bool isNull = base64String == null; + final bool isEmpty = base64String?.isEmpty ?? true; + final bool isTrimmedEmpty = base64String?.trim().isEmpty ?? true; + if (isNull || isEmpty || isTrimmedEmpty) return _buildPlaceholder(); try { // Clean the base64 string (remove any whitespace) diff --git a/lib/ui/common/card_container.dart b/lib/ui/common/card_container.dart index ff669f74b..f5272e456 100644 --- a/lib/ui/common/card_container.dart +++ b/lib/ui/common/card_container.dart @@ -37,8 +37,7 @@ class CardContainer extends StatelessWidget { Widget build(BuildContext context) { if (active) { return Card( - margin: EdgeInsets.only( - top: 0.0, right: 0.0, bottom: cardMargin * 1.5, left: 0.0), + margin: EdgeInsets.only(top: 0.0, right: 0.0, bottom: cardMargin * 1.5, left: 0.0), elevation: 4, shadowColor: Colors.black, semanticContainer: false, @@ -49,15 +48,12 @@ class CardContainer extends StatelessWidget { width: 0.5, ), ), - color: Theme.of(context).brightness == Brightness.dark - ? darkPrimaryBgColor - : lightAccentColor, + color: Theme.of(context).brightness == Brightness.dark ? darkPrimaryBgColor : lightAccentColor, child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ ListTile( - contentPadding: - EdgeInsets.only(top: 0.0, right: 8.0, bottom: 0.0, left: 8.0), + contentPadding: EdgeInsets.only(top: 0.0, right: 8.0, bottom: 0.0, left: 8.0), visualDensity: VisualDensity(horizontal: 0, vertical: 0), title: Text( titleText, @@ -67,11 +63,8 @@ class CardContainer extends StatelessWidget { ), buildBody(context), Padding( - padding: - const EdgeInsets.only(top: 16, right: 0, bottom: 16, left: 8), - child: actionButtons != null - ? Row(children: actionButtons!) - : Container(), + padding: const EdgeInsets.only(top: 16, right: 0, bottom: 16, left: 8), + child: actionButtons != null ? Row(children: actionButtons!) : Container(), ), footer ?? Container(), ], @@ -146,9 +139,7 @@ class CardContainer extends StatelessWidget { ); } else if (titleText == "PARKING") { double _maxHeight = 320; - if (MediaQuery.of(context).size.width > 600) { - _maxHeight = 800; - } + if (MediaQuery.of(context).size.width > 600) _maxHeight = 800; return Container( width: double.infinity, constraints: BoxConstraints(minHeight: 320, maxHeight: _maxHeight), @@ -171,8 +162,8 @@ class CardContainer extends StatelessWidget { children: [ buildMenuOptions( { - CardMenuOptionConstants.reloadCard: reload, - CardMenuOptionConstants.hideCard: hide, + CardMenuOptionConstants.RELOAD_CARD: reload, + CardMenuOptionConstants.HIDE_CARD: hide, }, ), ], @@ -203,17 +194,16 @@ class CardContainer extends StatelessWidget { offset: Offset(6, -3), child: Icon(Icons.more_vert, color: dotsUnselectedColor), ), - onChanged: (String? selectedMenuItem) => - onMenuItemPressed(selectedMenuItem), + onChanged: (String? selectedMenuItem) => onMenuItemPressed(selectedMenuItem), ); } void onMenuItemPressed(String? selectedMenuItem) { switch (selectedMenuItem) { - case CardMenuOptionConstants.reloadCard: + case CardMenuOptionConstants.RELOAD_CARD: reload(); break; - case CardMenuOptionConstants.hideCard: + case CardMenuOptionConstants.HIDE_CARD: hide(); break; default: diff --git a/lib/ui/common/container_view.dart b/lib/ui/common/container_view.dart index f468021d0..06d90b6dd 100644 --- a/lib/ui/common/container_view.dart +++ b/lib/ui/common/container_view.dart @@ -13,9 +13,7 @@ class ContainerView extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: PreferredSize( - preferredSize: Size.fromHeight(50), - child: Provider.of(context).appBar), + appBar: PreferredSize(preferredSize: Size.fromHeight(50), child: Provider.of(context).appBar), body: child, ); } diff --git a/lib/ui/common/directions_helper.dart b/lib/ui/common/directions_helper.dart index 5428827cc..fa6c7b4bd 100644 --- a/lib/ui/common/directions_helper.dart +++ b/lib/ui/common/directions_helper.dart @@ -20,8 +20,7 @@ class DirectionsHelper { } // Try Google Maps app URL scheme (works on both iOS and Android) - final googleMapsAppUrl = - dotenv.get('GOOGLE_MAPS_APP_URL') + '$lat,$lon&directionsmode=walking'; + final googleMapsAppUrl = dotenv.get('GOOGLE_MAPS_APP_URL') + '$lat,$lon&directionsmode=walking'; if (await canLaunchUrl(googleMapsAppUrl as Uri)) { await launchUrl( googleMapsAppUrl as Uri, diff --git a/lib/ui/common/event_time.dart b/lib/ui/common/event_time.dart index f80e19934..df57a63c1 100644 --- a/lib/ui/common/event_time.dart +++ b/lib/ui/common/event_time.dart @@ -59,11 +59,7 @@ class EventDateTime extends StatelessWidget { if (sameDay) { if (!unspecifiedTime) { - return Text(startMonthDay + - ', ' + - startTime + - ' - ' + - endTime); // ex: Jan. 1, 8:00 AM - 12:00 PM + return Text(startMonthDay + ', ' + startTime + ' - ' + endTime); // ex: Jan. 1, 8:00 AM - 12:00 PM } else { return Text(startMonthDay); // ex: Jan. 1 } @@ -77,8 +73,7 @@ class EventDateTime extends StatelessWidget { ', ' + endTime); // ex: Jan. 1, 8:00 AM - Jan. 2, 12:00 PM } else { - return Text( - startMonthDay + ' - ' + endMonthDay); // ex: Jan. 1 - Jan. 2 + return Text(startMonthDay + ' - ' + endMonthDay); // ex: Jan. 1 - Jan. 2 } } } catch (e) { @@ -96,10 +91,8 @@ class EventTileDateTime extends StatelessWidget { Widget build(BuildContext context) { try { // Separate dates from times - var startMonthDayYear = - DateFormat.yMMMMd('en_US').format(data.startDate.toLocal()); - var endMonthDayYear = - DateFormat.yMMMMd('en_US').format(data.endDate.toLocal()); + var startMonthDayYear = DateFormat.yMMMMd('en_US').format(data.startDate.toLocal()); + var endMonthDayYear = DateFormat.yMMMMd('en_US').format(data.endDate.toLocal()); var startTime = DateFormat.jm().format(data.startDate.toLocal()); var endTime = DateFormat.jm().format(data.endDate.toLocal()); @@ -114,33 +107,25 @@ class EventTileDateTime extends StatelessWidget { ); // Ex. June 11, 2021 } else { // if not the same date, check if the same year - var startYear = startMonthDayYear.substring( - startMonthDayYear.indexOf(',') + 2, startMonthDayYear.length); - var endYear = endMonthDayYear.substring( - endMonthDayYear.indexOf(',') + 2, endMonthDayYear.length); + var startYear = startMonthDayYear.substring(startMonthDayYear.indexOf(',') + 2, startMonthDayYear.length); + var endYear = endMonthDayYear.substring(endMonthDayYear.indexOf(',') + 2, endMonthDayYear.length); if (startYear == endYear) { // if the same year, check if the same month - var startMonth = - startMonthDayYear.substring(0, startMonthDayYear.indexOf(' ')); - var endMonth = - endMonthDayYear.substring(0, endMonthDayYear.indexOf(' ')); + var startMonth = startMonthDayYear.substring(0, startMonthDayYear.indexOf(' ')); + var endMonth = endMonthDayYear.substring(0, endMonthDayYear.indexOf(' ')); if (startMonth == endMonth) { // if different date in the same month and year - var startDay = startMonthDayYear.substring( - startMonthDayYear.indexOf(' ') + 1, - startMonthDayYear.indexOf(',')); - var endDay = endMonthDayYear.substring( - endMonthDayYear.indexOf(' ') + 1, endMonthDayYear.indexOf(',')); + var startDay = + startMonthDayYear.substring(startMonthDayYear.indexOf(' ') + 1, startMonthDayYear.indexOf(',')); + var endDay = endMonthDayYear.substring(endMonthDayYear.indexOf(' ') + 1, endMonthDayYear.indexOf(',')); date = Text( startMonth + ' ' + startDay + ' - ' + endDay + ', ' + startYear, style: TextStyle(fontSize: 12), ); // Ex. September 11 - 26, 2021 } else { // if different month in the same year - var startMonthDay = - startMonthDayYear.substring(0, startMonthDayYear.indexOf(',')); - var endMonthDay = - endMonthDayYear.substring(0, endMonthDayYear.indexOf(',')); + var startMonthDay = startMonthDayYear.substring(0, startMonthDayYear.indexOf(',')); + var endMonthDay = endMonthDayYear.substring(0, endMonthDayYear.indexOf(',')); date = Text( startMonthDay + ' - ' + endMonthDay + ', ' + startYear, style: TextStyle(fontSize: 12), diff --git a/lib/ui/common/HexColor.dart b/lib/ui/common/hex_color.dart similarity index 100% rename from lib/ui/common/HexColor.dart rename to lib/ui/common/hex_color.dart diff --git a/lib/ui/common/image_loader.dart b/lib/ui/common/image_loader.dart index 4a446f411..04c96f1f8 100644 --- a/lib/ui/common/image_loader.dart +++ b/lib/ui/common/image_loader.dart @@ -27,21 +27,18 @@ class ImageLoader extends StatelessWidget { width: fullSize ? null : width, height: fullSize ? null : height, fit: fit, - loadingBuilder: (BuildContext context, Widget child, - ImageChunkEvent? loadingProgress) { + loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) { if (loadingProgress == null) return child; return Center( child: CircularProgressIndicator( color: Theme.of(context).colorScheme.secondary, value: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! + ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null, ), ); }, - errorBuilder: - (BuildContext context, Object error, StackTrace? stackTrace) { + errorBuilder: (BuildContext context, Object error, StackTrace? stackTrace) { return Container( width: 0, height: 0, diff --git a/lib/ui/common/last_updated_widget.dart b/lib/ui/common/last_updated_widget.dart index fed3355fd..ac54bc013 100644 --- a/lib/ui/common/last_updated_widget.dart +++ b/lib/ui/common/last_updated_widget.dart @@ -20,12 +20,12 @@ class LastUpdatedWidget extends StatelessWidget { static String determineText(DateTime time) { Duration difference = DateTime.now().difference(time); - if (difference.compareTo(Duration(seconds: 120)) <= 0) - return 'a few seconds ago'; - if (difference.compareTo(Duration(minutes: 60)) <= 0) - return '${difference.inMinutes} minutes ago'; - if (difference.compareTo(Duration(hours: 48)) <= 0) - return '${difference.inHours} hours ago'; + final bool isWithinTwoMinutes = difference.compareTo(Duration(seconds: 120)) <= 0; + final bool isWithinOneHour = difference.compareTo(Duration(minutes: 60)) <= 0; + final bool isWithinTwoDays = difference.compareTo(Duration(hours: 48)) <= 0; + if (isWithinTwoMinutes) return 'a few seconds ago'; + if (isWithinOneHour) return '${difference.inMinutes} minutes ago'; + if (isWithinTwoDays) return '${difference.inHours} hours ago'; return '${difference.inDays} days ago'; } } diff --git a/lib/ui/common/linkify_with_catch.dart b/lib/ui/common/linkify_with_catch.dart index b8656a736..1c65cdcd5 100644 --- a/lib/ui/common/linkify_with_catch.dart +++ b/lib/ui/common/linkify_with_catch.dart @@ -14,11 +14,7 @@ class LinkifyWithCatch extends StatelessWidget { final bool looseUrl; const LinkifyWithCatch( - {Key? key, - required this.text, - this.style, - this.textAlign = TextAlign.start, - this.looseUrl = false}) + {Key? key, required this.text, this.style, this.textAlign = TextAlign.start, this.looseUrl = false}) : super(key: key); @override diff --git a/lib/ui/common/time_range_widget.dart b/lib/ui/common/time_range_widget.dart index ca7bb8d5b..8f1774335 100644 --- a/lib/ui/common/time_range_widget.dart +++ b/lib/ui/common/time_range_widget.dart @@ -18,9 +18,7 @@ class TimeRangeWidget extends StatelessWidget { Widget build(BuildContext context) { return Text( getStartTime(context) + ' - ' + getStopTime(context), - style: Theme.of(context).brightness == Brightness.dark - ? descriptiveTextSmallDark - : descriptiveTextSmallLight, + style: Theme.of(context).brightness == Brightness.dark ? descriptiveTextSmallDark : descriptiveTextSmallLight, ); } diff --git a/lib/ui/common/webview_container.dart b/lib/ui/common/webview_container.dart index 95d86c63f..7493b3671 100644 --- a/lib/ui/common/webview_container.dart +++ b/lib/ui/common/webview_container.dart @@ -45,8 +45,7 @@ class WebViewContainer extends StatefulWidget { _WebViewContainerState createState() => _WebViewContainerState(); } -class _WebViewContainerState extends State - with AutomaticKeepAliveClientMixin { +class _WebViewContainerState extends State with AutomaticKeepAliveClientMixin { /// STATES bool active = false; double _contentHeight = cardContentMinHeight; @@ -68,18 +67,17 @@ class _WebViewContainerState extends State @override void initState() { super.initState(); - hide = () => Provider.of(context, listen: false) - .toggleCard(widget.cardId); + hide = () => Provider.of(context, listen: false).toggleCard(widget.cardId); _webViewController = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) - //open link + // open link ..addJavaScriptChannel( 'OpenLink', onMessageReceived: (JavaScriptMessage m) { openLink(m.message); }, ) - //set height (with debouncing for performance) + // set height (with debouncing for performance) ..addJavaScriptChannel( 'SetHeight', onMessageReceived: (JavaScriptMessage message) { @@ -93,14 +91,11 @@ class _WebViewContainerState extends State final newHeight = double.tryParse(message.message); if (newHeight != null && newHeight > 0) { final validatedHeight = validateHeight(context, newHeight); - print( - 'WebView height: requested=${newHeight.toInt()}px, validated=${validatedHeight.toInt()}px'); + print('WebView height: requested=${newHeight.toInt()}px, validated=${validatedHeight.toInt()}px'); setState(() { _contentHeight = validatedHeight; - if (widget.onWidgetSizeChange != null) { - widget.onWidgetSizeChange!(Size( - MediaQuery.of(context).size.width, _contentHeight)); - } + if (widget.onWidgetSizeChange != null) + widget.onWidgetSizeChange!(Size(MediaQuery.of(context).size.width, _contentHeight)); }); } } @@ -120,33 +115,25 @@ class _WebViewContainerState extends State // Check if widget is still mounted // Perform heavy operations asynchronously to avoid blocking UI Future.microtask(() { - final mapsProvider = - Provider.of(context, listen: false); - final navProvider = Provider.of( - context, - listen: false); - final appBarProvider = - Provider.of(context, listen: false); + final mapsProvider = Provider.of(context, listen: false); + final navProvider = Provider.of(context, listen: false); + final appBarProvider = Provider.of(context, listen: false); mapsProvider.searchBarController.text = message.message; mapsProvider.fetchLocations(); - navProvider.currentIndex = NavigatorConstants.MapTab; + navProvider.currentIndex = NavigatorConstants.MAP_TAB; appBarProvider.changeTitle("Maps"); }); } }); }, ) - //refresh token + // refresh token ..addJavaScriptChannel( 'RefreshToken', onMessageReceived: (JavaScriptMessage message) async { - if (!Provider.of(context, listen: false) - .isLoggedIn) { - if (await _userDataProvider.silentLogin()) { - _webViewController.reload(); - } - } + final bool isNotLoggedIn = !Provider.of(context, listen: false).isLoggedIn; + if (isNotLoggedIn) if (await _userDataProvider.silentLogin()) _webViewController.reload(); }, ) // javascript channel for redirecting the user to a new webcard URL @@ -161,9 +148,7 @@ class _WebViewContainerState extends State ..setNavigationDelegate( NavigationDelegate( onPageFinished: (url) { - if (widget.onPageFinished != null) { - widget.onPageFinished!(); - } + if (widget.onPageFinished != null) widget.onPageFinished!(); }, ), ); @@ -187,8 +172,7 @@ class _WebViewContainerState extends State if (active) { return Card( - margin: EdgeInsets.only( - top: 0.0, right: 0.0, bottom: cardMargin * 1.5, left: 0.0), + margin: EdgeInsets.only(top: 0.0, right: 0.0, bottom: cardMargin * 1.5, left: 0.0), elevation: 4, shadowColor: Colors.black, semanticContainer: false, @@ -199,15 +183,12 @@ class _WebViewContainerState extends State width: 0.5, ), ), - color: Theme.of(context).brightness == Brightness.dark - ? darkPrimaryBgColor - : lightAccentColor, + color: Theme.of(context).brightness == Brightness.dark ? darkPrimaryBgColor : lightAccentColor, child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ ListTile( - contentPadding: EdgeInsets.only( - top: 0.0, right: 6.0, bottom: 0.0, left: 12.0), + contentPadding: EdgeInsets.only(top: 0.0, right: 6.0, bottom: 0.0, left: 12.0), visualDensity: VisualDensity(horizontal: 0, vertical: 0), title: Text( widget.titleText, @@ -218,9 +199,7 @@ class _WebViewContainerState extends State buildBody(context), Padding( padding: const EdgeInsets.symmetric(horizontal: 0), - child: widget.actionButtons != null - ? Row(children: widget.actionButtons!) - : Container(), + child: widget.actionButtons != null ? Row(children: widget.actionButtons!) : Container(), ), ], ), @@ -257,8 +236,8 @@ class _WebViewContainerState extends State mainAxisSize: MainAxisSize.min, children: [ buildMenuOptions({ - CardMenuOptionConstants.reloadCard: _webViewController.reload, - CardMenuOptionConstants.hideCard: hide, + CardMenuOptionConstants.RELOAD_CARD: _webViewController.reload, + CardMenuOptionConstants.HIDE_CARD: hide, }), ], ); @@ -286,19 +265,18 @@ class _WebViewContainerState extends State offset: Offset(6, -3), child: Icon(Icons.more_vert, color: dotsUnselectedColor), ), - onChanged: (String? selectedMenuItem) => - onMenuItemPressed(selectedMenuItem), + onChanged: (String? selectedMenuItem) => onMenuItemPressed(selectedMenuItem), ); } void onMenuItemPressed(String? selectedMenuItem) { switch (selectedMenuItem) { - case CardMenuOptionConstants.reloadCard: + case CardMenuOptionConstants.RELOAD_CARD: // _webViewController?.loadUrl(webCardUrl); _webViewController.loadRequest(Uri.parse(webCardUrl)); resetCardHeight(widget.cardId); break; - case CardMenuOptionConstants.hideCard: + case CardMenuOptionConstants.HIDE_CARD: hide(); resetCardHeight(widget.cardId); break; @@ -311,9 +289,7 @@ class _WebViewContainerState extends State // to the webViewController's url, and loads in the new url if so void checkWebURL() async { String? currentUrl = await _webViewController.currentUrl(); - if (webCardUrl != currentUrl) { - _webViewController.loadRequest(Uri.parse(webCardUrl)); - } + if (webCardUrl != currentUrl) _webViewController.loadRequest(Uri.parse(webCardUrl)); } @override diff --git a/lib/ui/dining/dining_busyness_bar.dart b/lib/ui/dining/dining_busyness_bar.dart index 37e2ac485..c98d9a3e9 100644 --- a/lib/ui/dining/dining_busyness_bar.dart +++ b/lib/ui/dining/dining_busyness_bar.dart @@ -37,9 +37,8 @@ class DiningBusynessBar extends StatelessWidget { // Only display the subLocation where subLocation.name == diningModel.name final matchingSubLocation = busynessDiningHallModel.subLocations - .where((subLocation) => - diningModel.name.contains(subLocation.name) || - subLocation.name.contains(diningModel.name)) + .where( + (subLocation) => diningModel.name.contains(subLocation.name) || subLocation.name.contains(diningModel.name)) .toList(); if (matchingSubLocation.isEmpty) { @@ -59,9 +58,7 @@ class DiningBusynessBar extends StatelessWidget { children: [ Text( '${(100 * percentAvailability(subLocation)).toInt()}% Busy', - style: Theme.of(context).brightness == Brightness.light - ? textSmallMoreInfoLight - : textSmallMoreInfoDark, + style: Theme.of(context).brightness == Brightness.light ? textSmallMoreInfoLight : textSmallMoreInfoDark, ), SizedBox(width: 12), Expanded( @@ -70,9 +67,8 @@ class DiningBusynessBar extends StatelessWidget { child: ClipRRect( borderRadius: BorderRadius.circular(BORDER_RADIUS), child: LinearProgressIndicator( - value: (percentAvailability(subLocation) <= 0.01) - ? 0.01 - : percentAvailability(subLocation).toDouble(), + value: + (percentAvailability(subLocation) <= 0.01) ? 0.01 : percentAvailability(subLocation).toDouble(), backgroundColor: Colors.grey[BACKGROUND_GREY_SHADE], valueColor: AlwaysStoppedAnimation( setIndicatorColor(percentAvailability(subLocation)), @@ -92,9 +88,7 @@ class DiningBusynessBar extends StatelessWidget { children: ListTile.divideTiles( tiles: locations, context: context, - color: Theme.of(context).brightness == Brightness.dark - ? listTileDividerColorDark - : listTileDividerColorLight, + color: Theme.of(context).brightness == Brightness.dark ? listTileDividerColorDark : listTileDividerColorLight, ).toList(), ), ); diff --git a/lib/ui/dining/dining_card.dart b/lib/ui/dining/dining_card.dart index c42ed84d7..1f3834fc6 100644 --- a/lib/ui/dining/dining_card.dart +++ b/lib/ui/dining/dining_card.dart @@ -15,26 +15,21 @@ class DiningCard extends StatelessWidget { Widget build(BuildContext context) { return CardContainer( active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), - reload: () => Provider.of(context, listen: false) - .fetchDiningLocations(), + hide: () => Provider.of(context, listen: false).toggleCard(cardId), + reload: () => Provider.of(context, listen: false).fetchDiningLocations(), isLoading: Provider.of(context).isLoading, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[cardId]!, errorText: Provider.of(context).error, - child: () => buildDiningCard( - Provider.of(context).diningModels), + child: () => buildDiningCard(Provider.of(context).diningModels), actionButtons: [ ActionButton( buttonText: 'VIEW ALL DINING OPTIONS', onPressed: () { // Only navigate if not loading and no error - final provider = - Provider.of(context, listen: false); - if (!provider.isLoading && provider.error == null) { - Navigator.pushNamed( - context, RoutePaths.DiningViewAllDiningOptions); - } + final provider = Provider.of(context, listen: false); + final bool isNotLoading = !provider.isLoading; + final bool hasNoError = provider.error == null; + if (isNotLoading && hasNoError) Navigator.pushNamed(context, RoutePaths.DINING_VIEW_ALL_DINING_OPTIONS); }) ], ); diff --git a/lib/ui/dining/dining_detail_view.dart b/lib/ui/dining/dining_detail_view.dart index 07fb25188..a5e69e55e 100644 --- a/lib/ui/dining/dining_detail_view.dart +++ b/lib/ui/dining/dining_detail_view.dart @@ -41,11 +41,9 @@ class _DiningDetailViewState extends State { } // Contains all the widgets that make up the Dining detail view. - List buildDetailView( - BuildContext context, prefix0.DiningModel diningModel) { + List buildDetailView(BuildContext context, prefix0.DiningModel diningModel) { // Get availability for all dining halls - List availabilityModels = _availabilityDataProvider - .availabilityModels + List availabilityModels = _availabilityDataProvider.availabilityModels .where((m) => m != null && m.name.contains("Dining Halls")) .toList(); var busynessDiningHallModelOne = availabilityModels.firstWhere( @@ -57,6 +55,18 @@ class _DiningDetailViewState extends State { orElse: () => null, ); var specials = diningModel.specials; + final bool hasSpecialHours = diningModel.specialHours != null; + final bool hasSpecialsTitle = specials?.specialTitle?.isNotEmpty == true; + // Move variable declarations outside the list + final bool hasModelOne = busynessDiningHallModelOne != null; + final bool modelOneMatches = hasModelOne && + busynessDiningHallModelOne.subLocations + .any((child) => child.name.contains(diningModel.name) || diningModel.name.contains(child.name)); + final bool hasModelTwo = busynessDiningHallModelTwo != null; + final bool modelTwoMatches = hasModelTwo && + busynessDiningHallModelTwo.subLocations + .any((child) => child.name.contains(diningModel.name) || diningModel.name.contains(child.name)); + return [ Row( children: [ @@ -77,9 +87,7 @@ class _DiningDetailViewState extends State { ) : Icon(Icons.restaurant, size: 56, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2), + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2), SizedBox(width: 8), Flexible( child: Column( @@ -109,31 +117,21 @@ class _DiningDetailViewState extends State { ], ), // Availability Bars if applicable (Canyon Vista, Club Med, Pines, 64 Degrees, Cafe Ventanas) - if ((busynessDiningHallModelOne != null && - busynessDiningHallModelOne.subLocations.any((child) => - child.name.contains(diningModel.name) || - diningModel.name.contains(child.name))) || - (busynessDiningHallModelTwo != null && - busynessDiningHallModelTwo.subLocations.any((child) => - child.name.contains(diningModel.name) || - diningModel.name.contains(child.name)))) + if (modelOneMatches || modelTwoMatches) DiningBusynessBar( diningModel: diningModel, busynessDiningHallModel: (busynessDiningHallModelOne != null && - busynessDiningHallModelOne.subLocations.any((child) => - child.name.contains(diningModel.name) || - diningModel.name.contains(child.name))) + busynessDiningHallModelOne.subLocations + .any((child) => child.name.contains(diningModel.name) || diningModel.name.contains(child.name))) ? busynessDiningHallModelOne : busynessDiningHallModelTwo!, ), // Vendor hours buildHours(context, diningModel), // Vendor Special Hours - if (diningModel.specialHours != null) - buildSpecialHours(context, diningModel), + if (hasSpecialHours) buildSpecialHours(context, diningModel), // Specials Field - if (specials?.specialTitle?.isNotEmpty == true) - buildSpecialsField(context, diningModel), + if (hasSpecialsTitle) buildSpecialsField(context, diningModel), // Vendor Payment Options buildPaymentOptions(context, diningModel), // Vendor Location @@ -155,9 +153,9 @@ class _DiningDetailViewState extends State { ], ), ), - // TODO: removed the menu on March 25, 2025 - //SizedBox(height: 20), - //buildMenu(context, model), + // TODO: removed the menu on March 25, 2025 - December 2025 + // SizedBox(height: 20), + // buildMenu(context, model), ]; } @@ -184,21 +182,18 @@ class _DiningDetailViewState extends State { Widget buildDivider(BuildContext context) { return Divider( height: 10, - color: Theme.of(context).brightness == Brightness.dark - ? listTileDividerColorDark - : listTileDividerColorLight, + color: Theme.of(context).brightness == Brightness.dark ? listTileDividerColorDark : listTileDividerColorLight, ); } ///////////// Special Hours Section ///////////// Widget buildSpecialHours(BuildContext context, prefix0.DiningModel model) { var specialHoursDuration = ""; - if (model.specialHours?.specialHoursValidFrom != null && - model.specialHours?.specialHoursValidTo != null) { - specialHoursDuration = model.specialHours!.specialHoursValidFrom! + - " to " + - model.specialHours!.specialHoursValidTo! + - "\n"; + final bool hasSpecialHoursFrom = model.specialHours?.specialHoursValidFrom != null; + final bool hasSpecialHoursTo = model.specialHours?.specialHoursValidTo != null; + if (hasSpecialHoursFrom && hasSpecialHoursTo) { + specialHoursDuration = + model.specialHours!.specialHoursValidFrom! + " to " + model.specialHours!.specialHoursValidTo! + "\n"; } return Container( child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -224,7 +219,7 @@ class _DiningDetailViewState extends State { } ///////////// Specials Field ///////////// - // TODO: Implement the expiration if we decide to go in that direction + // TODO: Implement the expiration if we decide to go in that direction - December 2025 // (maybe the API updates automatically, and when fetched, the old promotion will be null // so we don't really have to code an expiration. Only time will tell... Widget buildSpecialsField(BuildContext context, prefix0.DiningModel model) { @@ -257,9 +252,7 @@ class _DiningDetailViewState extends State { ///////////// Payment Options Section ///////////// Widget buildPaymentOptions(BuildContext context, prefix0.DiningModel model) { String options = model.paymentOptions.join(', '); - if (options.trim().isEmpty) { - return SizedBox.shrink(); - } + if (options.trim().isEmpty) return SizedBox.shrink(); return Container( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -281,9 +274,10 @@ class _DiningDetailViewState extends State { ///////////// Location Section ///////////// Widget buildDirectionsButton(BuildContext context, prefix0.DiningModel model) { - if (model.coordinates != null && - model.coordinates!.lat != null && - model.coordinates!.lon != null) { + final bool hasCoordinates = model.coordinates != null; + final bool hasLatitude = hasCoordinates && model.coordinates!.lat != null; + final bool hasLongitude = hasCoordinates && model.coordinates!.lon != null; + if (hasCoordinates && hasLatitude && hasLongitude) { return TextButton( child: Row( children: [ @@ -291,9 +285,7 @@ Widget buildDirectionsButton(BuildContext context, prefix0.DiningModel model) { 'Get Directions', style: linkTextDark.copyWith( fontSize: 18.0, - color: Theme.of(context).brightness == Brightness.light - ? linkColorLight - : linkColorDark, + color: Theme.of(context).brightness == Brightness.light ? linkColorLight : linkColorDark, ), ), SizedBox(width: 6), @@ -301,21 +293,14 @@ Widget buildDirectionsButton(BuildContext context, prefix0.DiningModel model) { children: [ Icon( Icons.directions_walk, - color: Theme.of(context).brightness == Brightness.light - ? linkColorLight - : linkColorDark, + color: Theme.of(context).brightness == Brightness.light ? linkColorLight : linkColorDark, size: 32, ), model.distance != null - ? Text( - num.parse(model.distance!.toStringAsFixed(1)).toString() + - ' mi', + ? Text(num.parse(model.distance!.toStringAsFixed(1)).toString() + ' mi', style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontSize: 18.0, - color: - Theme.of(context).brightness == Brightness.light - ? linkColorLight - : linkColorDark, + color: Theme.of(context).brightness == Brightness.light ? linkColorLight : linkColorDark, )) : Text('--', style: TextStyle(color: linkColorLight)), ], @@ -324,8 +309,7 @@ Widget buildDirectionsButton(BuildContext context, prefix0.DiningModel model) { ), onPressed: () async { try { - await DirectionsHelper.openDirections( - model.coordinates!.lat!, model.coordinates!.lon!); + await DirectionsHelper.openDirections(model.coordinates!.lat!, model.coordinates!.lon!); } catch (e) { // an error occurred, do nothing debugPrint('Error opening directions: $e'); @@ -338,14 +322,15 @@ Widget buildDirectionsButton(BuildContext context, prefix0.DiningModel model) { } else { return Padding( padding: const EdgeInsets.only(top: 12.0), - child: Text('Directions not available.', - style: Theme.of(context).textTheme.bodySmall)); + child: Text('Directions not available.', style: Theme.of(context).textTheme.bodySmall)); } } ///////////// Website and Menu Section ///////////// Widget buildWebsiteButton(BuildContext context, prefix0.DiningModel model) { - if (model.url != null && model.url != '') { + final bool hasUrl = model.url != null; + final bool isUrlNotEmpty = hasUrl && model.url != ''; + if (hasUrl && isUrlNotEmpty) { return TextButton( child: Text('Visit Website'), onPressed: () { @@ -366,7 +351,9 @@ Widget buildWebsiteButton(BuildContext context, prefix0.DiningModel model) { } Widget buildMenuButton(BuildContext context, prefix0.DiningModel model) { - if (model.menuWebsite != null && model.menuWebsite!.isNotEmpty) { + final bool hasMenuWebsite = model.menuWebsite != null; + final bool isMenuWebsiteNotEmpty = hasMenuWebsite && model.menuWebsite!.isNotEmpty; + if (hasMenuWebsite && isMenuWebsiteNotEmpty) { return TextButton( child: Row( children: [ @@ -394,8 +381,7 @@ Widget buildMenuButton(BuildContext context, prefix0.DiningModel model) { } class HoursOfDay extends StatelessWidget { - const HoursOfDay({Key? key, required this.day, required this.model}) - : super(key: key); + const HoursOfDay({Key? key, required this.day, required this.model}) : super(key: key); final int day; final prefix0.DiningModel model; @@ -404,9 +390,7 @@ class HoursOfDay extends StatelessWidget { // Extract the hours for the given day from the model var result = extractDayHours(day, model); var theDay = result['day']; - var hoursText = (result['hours'] == "Invalid Date-Invalid Date") - ? "Unknown Hours" - : result['hours']; + var hoursText = (result['hours'] == "Invalid Date-Invalid Date") ? "Unknown Hours" : result['hours']; // Determine the hours' text style // final TextStyle hoursTextStyle = day == DateTime.now().weekday // ? TextStyle( @@ -419,9 +403,7 @@ class HoursOfDay extends StatelessWidget { final TextStyle hoursTextStyle = day == DateTime.now().weekday ? Theme.of(context).textTheme.bodySmall!.copyWith( fontWeight: FontWeight.w700, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2) + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2) : Theme.of(context).textTheme.bodySmall!; // Check if this is the current day @@ -440,7 +422,7 @@ class HoursOfDay extends StatelessWidget { width: 0, height: 0, child: OverflowBox( - maxWidth: 10, //enough width and height to hold the dot + maxWidth: 10, // enough width and height to hold the dot maxHeight: 10, alignment: Alignment.centerLeft, child: Transform.translate( @@ -458,9 +440,7 @@ class HoursOfDay extends StatelessWidget { ? Theme.of(context).textTheme.bodySmall!.copyWith( fontWeight: FontWeight.w700, color: - Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2) + Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2) : Theme.of(context).textTheme.bodySmall, ), ], @@ -492,46 +472,38 @@ Map extractDayHours(int day, prefix0.DiningModel? model) { switch (day) { case 1: theDay = 'Monday'; - theHours = - model!.regularHours.mon == null ? 'Closed' : model.regularHours.mon; + theHours = model!.regularHours.mon == null ? 'Closed' : model.regularHours.mon; break; case 2: theDay = 'Tuesday'; - theHours = - model!.regularHours.tue == null ? 'Closed' : model.regularHours.tue; + theHours = model!.regularHours.tue == null ? 'Closed' : model.regularHours.tue; break; case 3: theDay = 'Wednesday'; - theHours = - model!.regularHours.wed == null ? 'Closed' : model.regularHours.wed; + theHours = model!.regularHours.wed == null ? 'Closed' : model.regularHours.wed; break; case 4: theDay = 'Thursday'; - theHours = - model!.regularHours.thu == null ? 'Closed' : model.regularHours.thu; + theHours = model!.regularHours.thu == null ? 'Closed' : model.regularHours.thu; break; case 5: theDay = 'Friday'; - theHours = - model!.regularHours.fri == null ? 'Closed' : model.regularHours.fri; + theHours = model!.regularHours.fri == null ? 'Closed' : model.regularHours.fri; break; case 6: theDay = 'Saturday'; - theHours = - model!.regularHours.sat == null ? 'Closed' : model.regularHours.sat; + theHours = model!.regularHours.sat == null ? 'Closed' : model.regularHours.sat; break; case 7: theDay = 'Sunday'; - theHours = - model!.regularHours.sun == null ? 'Closed' : model.regularHours.sun; + theHours = model!.regularHours.sun == null ? 'Closed' : model.regularHours.sun; break; } /*As of 05/05/2020, API may return 'Closed-Closed' as a value. If it does, correct it to look right.*/ if (theHours == 'Closed-Closed') theHours = 'Closed'; // Determine the hours' text - final String hoursText = (theHours != null && theHours.contains('Closed')) || - theHours == 'Open 24/7' + final String hoursText = (theHours != null && theHours.contains('Closed')) || theHours == 'Open 24/7' ? theHours! : (formattedTimeRange(theHours) ?? theHours!); @@ -565,9 +537,7 @@ class GreenDot extends StatelessWidget { if (currHours.contains('-')) { try { final timeStrings = currHours.split('-'); - if (timeStrings.length != 2) { - return Colors.grey; // Invalid format - } + if (timeStrings.length != 2) return Colors.grey; // Invalid format // Parse the start and end times final now = TimeOfDay.now(); @@ -583,13 +553,12 @@ class GreenDot extends StatelessWidget { // Adjust for overnight hours int adjustedEndTime = endTime; - if (endTime < startTime) { - adjustedEndTime += 24 * 60; // Add a day in minutes - } + if (endTime < startTime) adjustedEndTime += 24 * 60; // Add a day in minutes // Determine if current time is within the range - if (currentTimeInMinutes >= startTime && - currentTimeInMinutes < adjustedEndTime) { + final bool isAfterStartTime = currentTimeInMinutes >= startTime; + final bool isBeforeEndTime = currentTimeInMinutes < adjustedEndTime; + if (isAfterStartTime && isBeforeEndTime) { return Colors.green; } else { return Colors.red; @@ -608,7 +577,9 @@ class GreenDot extends StatelessWidget { final amPmRegex = RegExp(r'(\d+):(\d+)\s*(AM|PM)', caseSensitive: false); final match = amPmRegex.firstMatch(timeString); - if (match != null && match.groupCount >= 3) { + final bool hasMatch = match != null; + final bool hasEnoughGroups = hasMatch && match.groupCount >= 3; + if (hasMatch && hasEnoughGroups) { int hour = int.parse(match.group(1)!); final int minute = int.parse(match.group(2)!); final String amPm = match.group(3)!.toUpperCase(); diff --git a/lib/ui/dining/dining_filter_view.dart b/lib/ui/dining/dining_filter_view.dart index c0c709d1f..e3d829f29 100644 --- a/lib/ui/dining/dining_filter_view.dart +++ b/lib/ui/dining/dining_filter_view.dart @@ -33,8 +33,7 @@ class DiningFilterView extends StatelessWidget { child: ListView( children: ListTile.divideTiles( context: context, - tiles: createList(context, - DiningConstants.payment_filter_types, diningProvider), + tiles: createList(context, DiningConstants.PAYMENT_FILTER_TYPES, diningProvider), color: Theme.of(context).brightness == Brightness.dark ? listTileDividerColorDark : listTileDividerColorLight, @@ -48,12 +47,11 @@ class DiningFilterView extends StatelessWidget { } // Creates a list of tiles containing filter types with switches - List createList(BuildContext context, List typesAvailable, - DiningDataProvider diningProvider) { + List createList(BuildContext context, List typesAvailable, DiningDataProvider diningProvider) { List filterTypesList = []; // For each filter type available, create a ListTile with a switch // ```type``` is the filter type's name (i.e. "Triton Cash") - // See DiningConstants.payment_filter_types for all available filter types + // See DiningConstants.PAYMENT_FILTER_TYPES for all available filter types for (String type in typesAvailable) { filterTypesList.add( Padding( @@ -76,17 +74,16 @@ class DiningFilterView extends StatelessWidget { scale: 0.9, child: Switch.adaptive( // Each filter type's switch is 'on' or 'off' based on the filter type's state - value: diningProvider.diningFilterTypeStates[ - type]!, // Remember that ```type``` is a string key (i.e. "Triton Cash") + value: diningProvider + .diningFilterTypeStates[type]!, // Remember that ```type``` is a string key (i.e. "Triton Cash") onChanged: (_) { // On changed, this calls the provider function that "toggles" the filter type's state // i.e. if "Triton Cash" was on (true), it makes it off (false). diningProvider.toggleFilterType(type); }, thumbColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - return Colors.white; - } + final bool isSelected = states.contains(WidgetState.selected); + if (isSelected) return Colors.white; return null; }), activeColor: toggleActiveColor, diff --git a/lib/ui/dining/dining_list.dart b/lib/ui/dining/dining_list.dart index 3fd49a610..cf14a40ca 100644 --- a/lib/ui/dining/dining_list.dart +++ b/lib/ui/dining/dining_list.dart @@ -1,7 +1,6 @@ import 'package:campus_mobile_experimental/app_constants.dart'; import 'package:campus_mobile_experimental/app_styles.dart'; -import 'package:campus_mobile_experimental/core/models/dining.dart' - as dining_model; +import 'package:campus_mobile_experimental/core/models/dining.dart' as dining_model; import 'package:campus_mobile_experimental/core/providers/dining.dart'; import 'package:campus_mobile_experimental/ui/common/container_view.dart'; import 'package:campus_mobile_experimental/ui/common/directions_helper.dart'; @@ -21,8 +20,7 @@ class DiningList extends StatelessWidget { @override Widget build(BuildContext context) { // Using Provider's filteredDiningModels so that the list respects the filters - List data = - Provider.of(context).filteredDiningModels; + List data = Provider.of(context).filteredDiningModels; if (data.isEmpty) { return ContainerView( child: ListView( @@ -42,8 +40,7 @@ class DiningList extends StatelessWidget { return buildDiningList(data, context); } - Widget buildDiningList( - List listOfDiners, BuildContext context) { + Widget buildDiningList(List listOfDiners, BuildContext context) { final List diningTiles = []; /// check to see if we want to display only a limited number of elements @@ -92,17 +89,13 @@ class DiningList extends StatelessWidget { ); } - Widget textClosed(BuildContext context, - {String? nextOpenDay, String? nextOpenTime}) { + Widget textClosed(BuildContext context, {String? nextOpenDay, String? nextOpenTime}) { String closedText = 'Closed'; - if (nextOpenDay != null && nextOpenTime != null) { - closedText += '. Opens $nextOpenDay at $nextOpenTime.'; - } + if (nextOpenDay != null && nextOpenTime != null) closedText += '. Opens $nextOpenDay at $nextOpenTime.'; return Text(closedText, style: Theme.of(context).textTheme.bodySmall); } - Widget getHoursForToday( - dining_model.RegularHours hours, BuildContext context) { + Widget getHoursForToday(dining_model.RegularHours hours, BuildContext context) { int weekday = DateTime.now().weekday; String? dayHours; @@ -111,71 +104,52 @@ class DiningList extends StatelessWidget { if (hours.mon != null) dayHours = hours.mon; else - return textClosed(context, - nextOpenDay: findNextOpenDay(hours), - nextOpenTime: findNextOpenTime(hours)); + return textClosed(context, nextOpenDay: findNextOpenDay(hours), nextOpenTime: findNextOpenTime(hours)); break; case 2: if (hours.tue != null) dayHours = hours.tue; else - return textClosed(context, - nextOpenDay: findNextOpenDay(hours), - nextOpenTime: findNextOpenTime(hours)); + return textClosed(context, nextOpenDay: findNextOpenDay(hours), nextOpenTime: findNextOpenTime(hours)); break; case 3: if (hours.wed != null) dayHours = hours.wed; else - return textClosed(context, - nextOpenDay: findNextOpenDay(hours), - nextOpenTime: findNextOpenTime(hours)); + return textClosed(context, nextOpenDay: findNextOpenDay(hours), nextOpenTime: findNextOpenTime(hours)); break; case 4: if (hours.thu != null) dayHours = hours.thu; else - return textClosed(context, - nextOpenDay: findNextOpenDay(hours), - nextOpenTime: findNextOpenTime(hours)); + return textClosed(context, nextOpenDay: findNextOpenDay(hours), nextOpenTime: findNextOpenTime(hours)); break; case 5: if (hours.fri != null) dayHours = hours.fri; else - return textClosed(context, - nextOpenDay: findNextOpenDay(hours), - nextOpenTime: findNextOpenTime(hours)); + return textClosed(context, nextOpenDay: findNextOpenDay(hours), nextOpenTime: findNextOpenTime(hours)); break; case 6: if (hours.sat != null) dayHours = hours.sat; else - return textClosed(context, - nextOpenDay: findNextOpenDay(hours), - nextOpenTime: findNextOpenTime(hours)); + return textClosed(context, nextOpenDay: findNextOpenDay(hours), nextOpenTime: findNextOpenTime(hours)); break; case 7: if (hours.sun != null) dayHours = hours.sun; else - return textClosed(context, - nextOpenDay: findNextOpenDay(hours), - nextOpenTime: findNextOpenTime(hours)); + return textClosed(context, nextOpenDay: findNextOpenDay(hours), nextOpenTime: findNextOpenTime(hours)); break; default: - return textClosed(context, - nextOpenDay: findNextOpenDay(hours), - nextOpenTime: findNextOpenTime(hours)); + return textClosed(context, nextOpenDay: findNextOpenDay(hours), nextOpenTime: findNextOpenTime(hours)); } if (RegExp(r"\b[0-9]{2}").allMatches(dayHours!).length != 2) { if (dayHours == 'Closed-Closed') - return textClosed(context, - nextOpenDay: findNextOpenDay(hours), - nextOpenTime: findNextOpenTime(hours)); + return textClosed(context, nextOpenDay: findNextOpenDay(hours), nextOpenTime: findNextOpenTime(hours)); if (dayHours == 'Invalid Date-Invalid Date') - return Text("Unknown hours", - style: Theme.of(context).textTheme.bodySmall); + return Text("Unknown hours", style: Theme.of(context).textTheme.bodySmall); return Text(dayHours, style: Theme.of(context).textTheme.bodySmall); } @@ -185,9 +159,8 @@ class DiningList extends StatelessWidget { style: TextStyle( fontSize: 17.0, fontWeight: FontWeight.w400, - color: Theme.of(context).brightness == Brightness.light - ? descriptiveTextColorLight - : descriptiveTextColorDark), + color: + Theme.of(context).brightness == Brightness.light ? descriptiveTextColorLight : descriptiveTextColorDark), ); } @@ -215,17 +188,13 @@ class DiningList extends StatelessWidget { ) : Icon(Icons.restaurant, size: 32, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2), + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2), ), // Vendor Name title: Text( data.name, textAlign: TextAlign.start, - style: Theme.of(context).brightness == Brightness.dark - ? textButtonSmallDark - : textButtonSmallLight, + style: Theme.of(context).brightness == Brightness.dark ? textButtonSmallDark : textButtonSmallLight, ), // Vendor Hours subtitle: Padding( @@ -236,23 +205,20 @@ class DiningList extends StatelessWidget { trailing: buildIconWithDistance(data, context), onTap: () { // if (data.id != null) Provider.of(context, listen: false).fetchDiningMenu(data.id!); - Navigator.pushNamed(context, RoutePaths.DiningOptionDetailView, - arguments: data); + Navigator.pushNamed(context, RoutePaths.DINING_OPTION_DETAIL_VIEW, arguments: data); }, ); } // Builds the Right side of the ListTile containing the icon and distance - Widget buildIconWithDistance( - dining_model.DiningModel data, BuildContext context) { + Widget buildIconWithDistance(dining_model.DiningModel data, BuildContext context) { return TextButton( style: TextButton.styleFrom( foregroundColor: linkColorLight, ), onPressed: () async { try { - await DirectionsHelper.openDirections( - data.coordinates!.lat!, data.coordinates!.lon!); + await DirectionsHelper.openDirections(data.coordinates!.lat!, data.coordinates!.lon!); } catch (e) { // an error occurred, do nothing } @@ -261,20 +227,11 @@ class DiningList extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.directions_walk, - size: 28, - color: Theme.of(context).brightness == Brightness.light - ? linkColorLight - : linkColorDark), + size: 28, color: Theme.of(context).brightness == Brightness.light ? linkColorLight : linkColorDark), Text( - data.distance != null - ? (num.parse(data.distance!.toStringAsFixed(1)).toString() + - ' mi') - : '--', + data.distance != null ? (num.parse(data.distance!.toStringAsFixed(1)).toString() + ' mi') : '--', style: TextStyle( - fontSize: 13, - color: Theme.of(context).brightness == Brightness.light - ? linkColorLight - : linkColorDark), + fontSize: 13, color: Theme.of(context).brightness == Brightness.light ? linkColorLight : linkColorDark), ), ], ), @@ -311,30 +268,18 @@ String? findNextOpenDay(dining_model.RegularHours hours) { value = hours.sun; break; } - if (value != null && value != 'Closed-Closed') { - return days[nextDay][0].toUpperCase() + days[nextDay].substring(1); - } + if (value != null && value != 'Closed-Closed') return days[nextDay][0].toUpperCase() + days[nextDay].substring(1); } return null; } String? findNextOpenTime(dining_model.RegularHours hours) { - final days = [ - hours.mon, - hours.tue, - hours.wed, - hours.thu, - hours.fri, - hours.sat, - hours.sun - ]; + final days = [hours.mon, hours.tue, hours.wed, hours.thu, hours.fri, hours.sat, hours.sun]; final now = DateTime.now(); for (int i = 1; i <= 7; i++) { int nextDay = (now.weekday + i - 1) % 7; String? range = days[nextDay]; - if (range != null && range != 'Closed-Closed') { - return formattedTimeRange(range)?.split('-').first.trim(); - } + if (range != null && range != 'Closed-Closed') return formattedTimeRange(range)?.split('-').first.trim(); } return null; } diff --git a/lib/ui/dining/nutrition_facts_view.dart b/lib/ui/dining/nutrition_facts_view.dart index 3084987be..f8bc5fe8e 100644 --- a/lib/ui/dining/nutrition_facts_view.dart +++ b/lib/ui/dining/nutrition_facts_view.dart @@ -69,8 +69,8 @@ // } // // Widget nutrientValues(BuildContext context, {required Map nutrientData}) { -// //final n = (1.3456).toStringAsFixed(2); -// //final s = double.parse("1.2345"); +// // final n = (1.3456).toStringAsFixed(2); +// // final s = double.parse("1.2345"); // final nutrientTypes = [ // {"nutrient": "totalFat", "name": "Total Fat", "sub": false, "dly": 65.0}, // { diff --git a/lib/ui/dining/payment_filter_button.dart b/lib/ui/dining/payment_filter_button.dart index 45e57ec5c..031239840 100644 --- a/lib/ui/dining/payment_filter_button.dart +++ b/lib/ui/dining/payment_filter_button.dart @@ -18,11 +18,9 @@ class PaymentFilterButton extends StatelessWidget { width: 32, height: 32, ), - backgroundColor: Theme.of(context).brightness == Brightness.light - ? ColorPrimary - : Colors.white, + backgroundColor: Theme.of(context).brightness == Brightness.light ? ColorPrimary : Colors.white, onPressed: () { - Navigator.pushNamed(context, RoutePaths.DiningPaymentFilterView); + Navigator.pushNamed(context, RoutePaths.DINING_PAYMENT_FILTER_VIEW); }, ); } diff --git a/lib/ui/employee_id/employee_id_card.dart b/lib/ui/employee_id/employee_id_card.dart index 3d4debe9d..ff886ab4a 100644 --- a/lib/ui/employee_id/employee_id_card.dart +++ b/lib/ui/employee_id/employee_id_card.dart @@ -25,26 +25,20 @@ class _EmployeeIdCardState extends State { Widget build(BuildContext context) { ScalingUtility().getCurrentMeasurements(context); - EmployeeIdModel? employeeModel = - Provider.of(context).employeeIdModel; + EmployeeIdModel? employeeModel = Provider.of(context).employeeIdModel; isValidId = employeeModel != null && (employeeModel.barcode != null) && - (employeeModel.employeePreferredDisplayName != null && - employeeModel.employeeId != null); + (employeeModel.employeePreferredDisplayName != null && employeeModel.employeeId != null); return CardContainer( active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), - reload: () => Provider.of(context, listen: false) - .fetchData(), + hide: () => Provider.of(context, listen: false).toggleCard(cardId), + reload: () => Provider.of(context, listen: false).fetchData(), isLoading: Provider.of(context).isLoading, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[cardId]!, errorText: Provider.of(context).error, child: () => isValidId - ? buildCardContent( - Provider.of(context).employeeIdModel, - context) + ? buildCardContent(Provider.of(context).employeeIdModel, context) : buildErrorCardContent(context), ); } @@ -65,13 +59,10 @@ class _EmployeeIdCardState extends State { TextSpan(text: "If the problem persists, contact the "), TextSpan( text: "ITS Service Desk", - style: TextStyle( - color: Colors.blueAccent, - decoration: TextDecoration.underline), + style: TextStyle(color: Colors.blueAccent, decoration: TextDecoration.underline), recognizer: TapGestureRecognizer() ..onTap = () { - openLink( - "https://blink.ucsd.edu/technology/help-desk/index.html"); + openLink("https://blink.ucsd.edu/technology/help-desk/index.html"); }), TextSpan(text: ".") ]), @@ -84,8 +75,7 @@ class _EmployeeIdCardState extends State { ); } - Widget buildCardContent( - EmployeeIdModel? employeeIdModel, BuildContext context) { + Widget buildCardContent(EmployeeIdModel? employeeIdModel, BuildContext context) { try { return Column( children: [ @@ -99,8 +89,7 @@ class _EmployeeIdCardState extends State { flex: 4, child: Base64ImageWidget( base64String: employeeIdModel!.photo, - placeholderAssetPath: - 'assets/images/staff_id_placeholder.png', + placeholderAssetPath: 'assets/images/staff_id_placeholder.png', fit: BoxFit.fill, ), ), @@ -113,14 +102,11 @@ class _EmployeeIdCardState extends State { Text( employeeIdModel!.employeePreferredDisplayName, style: TextStyle( - color: Theme.of(context).brightness == Brightness.dark - ? darkPrimaryColor2 - : lightPrimaryColor, + color: + Theme.of(context).brightness == Brightness.dark ? darkPrimaryColor2 : lightPrimaryColor, fontFamily: 'Brix Sans', fontWeight: FontWeight.w400, - fontSize: getFontSize( - employeeIdModel.employeePreferredDisplayName, - "name"), + fontSize: getFontSize(employeeIdModel.employeePreferredDisplayName, "name"), ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -153,8 +139,7 @@ class _EmployeeIdCardState extends State { style: TextStyle( fontFamily: 'Brix Sans', fontWeight: FontWeight.w400, - fontSize: getFontSize( - "Employee ID " + employeeIdModel.employeeId, ""), + fontSize: getFontSize("Employee ID " + employeeIdModel.employeeId, ""), ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -164,13 +149,11 @@ class _EmployeeIdCardState extends State { padding: EdgeInsets.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - child: returnBarcodeContainer( - employeeIdModel.barcode, false, context), + child: returnBarcodeContainer(employeeIdModel.barcode, false, context), onPressed: () { createAlertDialog( context, - returnBarcodeContainer( - employeeIdModel.barcode, true, context), + returnBarcodeContainer(employeeIdModel.barcode, true, context), employeeIdModel.barcode, true, ); @@ -181,8 +164,7 @@ class _EmployeeIdCardState extends State { employeeIdModel.barcode.toString(), style: TextStyle( fontSize: ScalingUtility.horizontalSafeBlock * 3, - letterSpacing: - ScalingUtility.horizontalSafeBlock * 1.5, + letterSpacing: ScalingUtility.horizontalSafeBlock * 1.5, ), ), ), @@ -209,8 +191,7 @@ class _EmployeeIdCardState extends State { } /// Pop up barcode - createAlertDialog( - BuildContext context, Column image, String? cardNumber, bool rotated) { + createAlertDialog(BuildContext context, Column image, String? cardNumber, bool rotated) { return showDialog( context: context, builder: (context) { @@ -237,8 +218,7 @@ class _EmployeeIdCardState extends State { }); } - Column checkForRotation( - Column image, BuildContext context, String? cardNumber, bool rotated) { + Column checkForRotation(Column image, BuildContext context, String? cardNumber, bool rotated) { if (MediaQuery.of(context).orientation == Orientation.landscape) return returnBarcodeContainer(cardNumber, rotated, context); return image; @@ -255,8 +235,7 @@ class _EmployeeIdCardState extends State { } /// Determine barcode to display - returnBarcodeContainer( - String? cardNumber, bool rotated, BuildContext context) { + returnBarcodeContainer(String? cardNumber, bool rotated, BuildContext context) { var barcodeWithText; /// Initialize sizing @@ -267,10 +246,7 @@ class _EmployeeIdCardState extends State { data: cardNumber!, width: ScalingUtility.verticalSafeBlock * 45, height: 80, - style: TextStyle( - letterSpacing: ScalingUtility.verticalSafeBlock * 3, - color: Colors.white, - fontSize: 0), + style: TextStyle(letterSpacing: ScalingUtility.verticalSafeBlock * 3, color: Colors.white, fontSize: 0), ); } else { barcodeWithText = BarcodeWidget( @@ -278,45 +254,37 @@ class _EmployeeIdCardState extends State { data: cardNumber!, width: ScalingUtility.horizontalSafeBlock * 50, height: ScalingUtility.verticalSafeBlock * 4.45, - style: TextStyle( - letterSpacing: ScalingUtility.horizontalSafeBlock * 1.5, - fontSize: 0, - color: Colors.white), + style: TextStyle(letterSpacing: ScalingUtility.horizontalSafeBlock * 1.5, fontSize: 0, color: Colors.white), ); } if (rotated) { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - RotatedBox( - quarterTurns: 1, - child: Row( + return Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ + RotatedBox( + quarterTurns: 1, + child: Row( + children: [ + Padding( + padding: EdgeInsets.all(ScalingUtility.verticalSafeBlock * 7.5), + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - Padding( - padding: - EdgeInsets.all(ScalingUtility.verticalSafeBlock * 7.5), - ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - child: barcodeWithText, - color: Colors.white, - ), - Text( - cardNumber, - style: TextStyle( - color: Colors.black, - fontSize: fontSizeForTablet(), - letterSpacing: letterSpacingForTablet()), - ) - ], + Container( + child: barcodeWithText, + color: Colors.white, ), + Text( + cardNumber, + style: TextStyle( + color: Colors.black, fontSize: fontSizeForTablet(), letterSpacing: letterSpacingForTablet()), + ) ], ), - ), - ]); + ], + ), + ), + ]); } else { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -340,21 +308,18 @@ class _EmployeeIdCardState extends State { } double letterSpacingForTablet() { - if (MediaQuery.of(context).orientation == Orientation.landscape) { - return ScalingUtility.horizontalSafeBlock * 1; - } - return ScalingUtility.horizontalSafeBlock * 3; + final bool isLandscape = MediaQuery.of(context).orientation == Orientation.landscape; + if (isLandscape) return ScalingUtility.horizontalSafeBlock * 1; + return ScalingUtility.horizontalSafeBlock * 2; } double fontSizeForTablet() { - if (MediaQuery.of(context).orientation == Orientation.landscape) { - return ScalingUtility.horizontalSafeBlock * 2; - } + final bool isLandscape = MediaQuery.of(context).orientation == Orientation.landscape; + if (isLandscape) return ScalingUtility.horizontalSafeBlock * 2; return ScalingUtility.horizontalSafeBlock * 4; } - returnBarcodeContainerTablet( - String? cardNumber, bool rotated, BuildContext context) { + returnBarcodeContainerTablet(String? cardNumber, bool rotated, BuildContext context) { var barcodeWithText; SizeConfig().init(context); if (rotated) { @@ -363,10 +328,7 @@ class _EmployeeIdCardState extends State { data: cardNumber!, width: SizeConfig.safeBlockVertical * 60, height: 80, - style: TextStyle( - letterSpacing: SizeConfig.safeBlockVertical * 3, - fontSize: 0, - color: Colors.white), + style: TextStyle(letterSpacing: SizeConfig.safeBlockVertical * 3, fontSize: 0, color: Colors.white), ); } else { barcodeWithText = BarcodeWidget( @@ -374,47 +336,40 @@ class _EmployeeIdCardState extends State { data: cardNumber!, width: SizeConfig.safeBlockHorizontal * 20, height: 40, - style: TextStyle( - letterSpacing: SizeConfig.safeBlockHorizontal * 1.5, - fontSize: 0, - color: Colors.white), + style: TextStyle(letterSpacing: SizeConfig.safeBlockHorizontal * 1.5, fontSize: 0, color: Colors.white), ); } if (rotated) { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: EdgeInsets.all(25), - ), - RotatedBox( - quarterTurns: 1, - child: Row( + return Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ + Padding( + padding: EdgeInsets.all(25), + ), + RotatedBox( + quarterTurns: 1, + child: Row( + children: [ + Padding( + padding: EdgeInsets.all(SizeConfig.safeBlockVertical * 7.5), + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - Padding( - padding: EdgeInsets.all(SizeConfig.safeBlockVertical * 7.5), - ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - child: barcodeWithText, - color: Colors.white, - ), - Text( - cardNumber, - style: TextStyle( - color: Colors.black, - fontSize: getRotatedPopUpFontSize(), - letterSpacing: letterSpacing()), - ) - ], + Container( + child: barcodeWithText, + color: Colors.white, ), + Text( + cardNumber, + style: TextStyle( + color: Colors.black, fontSize: getRotatedPopUpFontSize(), letterSpacing: letterSpacing()), + ) ], ), - ), - ]); + ], + ), + ), + ]); } else { return Column(children: [ Text( @@ -441,15 +396,13 @@ class _EmployeeIdCardState extends State { } } - double letterSpacing() => - MediaQuery.of(context).orientation == Orientation.landscape - ? SizeConfig.safeBlockHorizontal * 1 - : SizeConfig.safeBlockHorizontal * 3; + double letterSpacing() => MediaQuery.of(context).orientation == Orientation.landscape + ? SizeConfig.safeBlockHorizontal * 1 + : SizeConfig.safeBlockHorizontal * 3; - double getRotatedPopUpFontSize() => - MediaQuery.of(context).orientation == Orientation.landscape - ? SizeConfig.safeBlockHorizontal * 2 - : SizeConfig.safeBlockHorizontal * 4; + double getRotatedPopUpFontSize() => MediaQuery.of(context).orientation == Orientation.landscape + ? SizeConfig.safeBlockHorizontal * 2 + : SizeConfig.safeBlockHorizontal * 4; /// Determine the font size for user's textFields double getFontSize(String input, String textField) { @@ -487,19 +440,14 @@ class _EmployeeIdCardState extends State { /// Determine the padding for a border around barcode EdgeInsets addBorder(ThemeData currentTheme) => - currentTheme.brightness == Brightness.dark - ? EdgeInsets.all(5) - : EdgeInsets.all(0); + currentTheme.brightness == Brightness.dark ? EdgeInsets.all(5) : EdgeInsets.all(0); /// Determine the padding for the text to realign - double realignText(ThemeData currentTheme) => - currentTheme.brightness == Brightness.dark ? 7 : 0; + double realignText(ThemeData currentTheme) => currentTheme.brightness == Brightness.dark ? 7 : 0; /// Determine the color of hint above the barcode Color decideColor(ThemeData currentTheme) => - currentTheme.brightness == Brightness.dark - ? Colors.white - : Colors.black45; + currentTheme.brightness == Brightness.dark ? Colors.white : Colors.black45; } // Image Scaling @@ -513,12 +461,8 @@ class ScalingUtility { _queryData = MediaQuery.of(context); /// Calculate blocks accounting for notches and home bar - horizontalSafeBlock = (_queryData.size.width - - (_queryData.padding.left + _queryData.padding.right)) / - 100; - verticalSafeBlock = (_queryData.size.height - - (_queryData.padding.top + _queryData.padding.bottom)) / - 100; + horizontalSafeBlock = (_queryData.size.width - (_queryData.padding.left + _queryData.padding.right)) / 100; + verticalSafeBlock = (_queryData.size.height - (_queryData.padding.top + _queryData.padding.bottom)) / 100; } } @@ -540,10 +484,8 @@ class SizeConfig { blockSizeHorizontal = screenWidth / 100; blockSizeVertical = screenHeight / 100; - _safeAreaHorizontal = - _mediaQueryData.padding.left + _mediaQueryData.padding.right; - _safeAreaVertical = - _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; + _safeAreaHorizontal = _mediaQueryData.padding.left + _mediaQueryData.padding.right; + _safeAreaVertical = _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; safeBlockHorizontal = (screenWidth - _safeAreaHorizontal) / 100; safeBlockVertical = (screenHeight - _safeAreaVertical) / 100; } diff --git a/lib/ui/events/event_tile.dart b/lib/ui/events/event_tile.dart index 1f91a5c74..f992b85a2 100644 --- a/lib/ui/events/event_tile.dart +++ b/lib/ui/events/event_tile.dart @@ -10,9 +10,9 @@ class EventTile extends StatelessWidget { const EventTile({Key? key, required this.data}) : super(key: key); /// LAYOUT CONSTANTS - static const double tileWidth = 190; - static const cornerRadius = Radius.circular(5.0); - static const sideBorder = BorderSide(width: 0.3); + static const double TILE_WIDTH = 190; + static const CORNER_RADIUS = Radius.circular(5.0); + static const SIDE_BORDER = BorderSide(width: 0.3); /// MODELS final EventModel data; @@ -32,13 +32,13 @@ class EventTile extends StatelessWidget { Widget _buildEventTile(BuildContext context) { return Container( - width: tileWidth, - //height: 300, + width: TILE_WIDTH, + // height: 300, child: InkWell( onTap: () { Navigator.pushNamed( context, - RoutePaths.EventDetailView, + RoutePaths.EVENT_DETAIL_VIEW, arguments: data, ); }, @@ -59,8 +59,7 @@ class EventTile extends StatelessWidget { final startDate = df.format(localStart); final endDate = df.format(localEnd); - final dateDisplay = - startDate == endDate ? startDate : '$startDate - $endDate'; + final dateDisplay = startDate == endDate ? startDate : '$startDate - $endDate'; final startTime = DateFormat.jm().format(localStart); final endTime = DateFormat.jm().format(localEnd); @@ -68,12 +67,12 @@ class EventTile extends StatelessWidget { final hasTime = startTime != endTime; return SizedBox( - width: tileWidth, + width: TILE_WIDTH, height: 300, child: DecoratedBox( decoration: BoxDecoration( border: Border.all(width: 0.3), - borderRadius: BorderRadius.all(cornerRadius), + borderRadius: BorderRadius.all(CORNER_RADIUS), ), child: Card( margin: EdgeInsets.symmetric(vertical: 1, horizontal: 1), @@ -82,12 +81,9 @@ class EventTile extends StatelessWidget { children: [ _eventImageLoader(data.imageThumb), Padding( - padding: - const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0), + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0), child: Row( - mainAxisAlignment: hasTime - ? MainAxisAlignment.spaceEvenly - : MainAxisAlignment.center, + mainAxisAlignment: hasTime ? MainAxisAlignment.spaceEvenly : MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Flexible( @@ -106,8 +102,7 @@ class EventTile extends StatelessWidget { ? Text('All day', style: TextStyle( fontSize: 14, - color: Theme.of(context).brightness == - Brightness.light + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w400, @@ -136,19 +131,19 @@ Widget _eventImageLoader(String? url) { return url?.isEmpty ?? true ? ClipRRect( borderRadius: BorderRadius.only( - topLeft: EventTile.cornerRadius, - topRight: EventTile.cornerRadius, + topLeft: EventTile.CORNER_RADIUS, + topRight: EventTile.CORNER_RADIUS, ), child: Image.asset( 'assets/images/UCSDMobile_sharp.png', height: 150, - width: EventTile.tileWidth, + width: EventTile.TILE_WIDTH, fit: BoxFit.cover, )) : ClipRRect( borderRadius: BorderRadius.only( - topLeft: EventTile.cornerRadius, - topRight: EventTile.cornerRadius, + topLeft: EventTile.CORNER_RADIUS, + topRight: EventTile.CORNER_RADIUS, ), child: Image.network( url!, @@ -158,14 +153,13 @@ Widget _eventImageLoader(String? url) { child: CircularProgressIndicator( color: Theme.of(context).colorScheme.secondary, value: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! + ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null, ), ); }, height: 150, - width: EventTile.tileWidth, + width: EventTile.TILE_WIDTH, fit: BoxFit.cover, )); } @@ -176,8 +170,7 @@ class TileTitle extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: - EdgeInsets.only(left: 16.0, top: 5.0, right: 16.0), // Keep padding + padding: EdgeInsets.only(left: 16.0, top: 5.0, right: 16.0), // Keep padding child: Center( // Centers the Text child: Text( @@ -186,9 +179,7 @@ class TileTitle extends StatelessWidget { maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle( - color: Theme.of(context).brightness == Brightness.light - ? linkTextColorLight - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? linkTextColorLight : Colors.white, fontSize: 16, height: 1.4, fontWeight: FontWeight.w600, @@ -213,9 +204,7 @@ class TileTime extends StatelessWidget { final style = TextStyle( fontSize: 14, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w400, ); @@ -269,10 +258,7 @@ class StartEndDateContainer extends StatelessWidget { date.split(' ')[0].toUpperCase(), style: TextStyle( fontSize: 20, - color: - Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w600, ), ), @@ -281,10 +267,7 @@ class StartEndDateContainer extends StatelessWidget { date.split(' ')[1].toUpperCase(), style: TextStyle( fontSize: 22, - color: - Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w600, ), ), @@ -300,9 +283,7 @@ class StartEndDateContainer extends StatelessWidget { date.split(' ')[3].toUpperCase(), style: TextStyle( fontSize: 18, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w400, ), ), @@ -310,8 +291,7 @@ class StartEndDateContainer extends StatelessWidget { Column( children: [ Padding( - padding: - EdgeInsets.only(right: 8), // Adjust padding as needed + padding: EdgeInsets.only(right: 8), // Adjust padding as needed child: Column( children: [ // End Date Day @@ -319,10 +299,7 @@ class StartEndDateContainer extends StatelessWidget { date.split(' ')[4].toUpperCase(), style: TextStyle( fontSize: 20, - color: - Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w600, ), ), @@ -331,10 +308,7 @@ class StartEndDateContainer extends StatelessWidget { date.split(' ')[5].toUpperCase(), style: TextStyle( fontSize: 22, - color: - Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w600, ), ), @@ -347,8 +321,7 @@ class StartEndDateContainer extends StatelessWidget { // If it's a single date, display it normally else ...[ Padding( - padding: EdgeInsets.symmetric( - horizontal: 8.0), // Adjust the padding as needed + padding: EdgeInsets.symmetric(horizontal: 8.0), // Adjust the padding as needed child: Column( children: [ // Month @@ -356,9 +329,7 @@ class StartEndDateContainer extends StatelessWidget { date.split(' ')[0].toUpperCase(), style: TextStyle( fontSize: 20, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w600, ), ), @@ -367,9 +338,7 @@ class StartEndDateContainer extends StatelessWidget { date.split(' ')[1].toUpperCase(), style: TextStyle( fontSize: 22, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w600, ), ), diff --git a/lib/ui/events/events_card.dart b/lib/ui/events/events_card.dart index 62475ba14..b4dbd0982 100644 --- a/lib/ui/events/events_card.dart +++ b/lib/ui/events/events_card.dart @@ -10,30 +10,24 @@ import 'package:provider/provider.dart'; const cardId = 'events'; -//edit these files +// edit these files class EventsCard extends StatelessWidget { @override Widget build(BuildContext context) { return CardContainer( active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), - reload: () => - Provider.of(context, listen: false).fetchEvents(), + hide: () => Provider.of(context, listen: false).toggleCard(cardId), + reload: () => Provider.of(context, listen: false).fetchEvents(), isLoading: Provider.of(context).isLoading, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[cardId]!, errorText: Provider.of(context).error, - child: () => buildEventsCardList( - Provider.of(context).eventsModels), + child: () => buildEventsCardList(Provider.of(context).eventsModels), actionButtons: [ ActionButton( - buttonText: "VIEW ALL EVENTS", - onPressed: () => - Navigator.pushNamed(context, RoutePaths.EventsViewAll)) + buttonText: "VIEW ALL EVENTS", onPressed: () => Navigator.pushNamed(context, RoutePaths.EVENTS_VIEW_ALL)) ], ); } - Widget buildEventsCardList(List? data) => - EventsCardList(listSize: 6); + Widget buildEventsCardList(List? data) => EventsCardList(listSize: 6); } diff --git a/lib/ui/events/events_card_list.dart b/lib/ui/events/events_card_list.dart index 1124f966f..83a298a0d 100644 --- a/lib/ui/events/events_card_list.dart +++ b/lib/ui/events/events_card_list.dart @@ -13,17 +13,12 @@ class EventsCardList extends StatelessWidget { @override Widget build(BuildContext context) { return Provider.of(context).isLoading - ? Center( - child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.secondary)) - : buildEventsList( - Provider.of(context).eventsModels, context); + ? Center(child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary)) + : buildEventsList(Provider.of(context).eventsModels, context); } Widget buildEventsList(List listOfEvents, BuildContext context) { - final List eventTiles = [ - const SizedBox(width: 7.5) - ]; // start off with left spacer + final List eventTiles = [const SizedBox(width: 7.5)]; // start off with left spacer /// check to see if we want to display only a limited number of elements /// if no constraint is given on the size of the list then all elements @@ -34,9 +29,7 @@ class EventsCardList extends StatelessWidget { if (size > listOfEvents.length) size = listOfEvents.length; for (var i = 0; i < size; i++) { - eventTiles.add(EventTile( - data: listOfEvents[ - i])); // get event model and then create a tile from it + eventTiles.add(EventTile(data: listOfEvents[i])); // get event model and then create a tile from it eventTiles.add(const SizedBox(width: 9)); // spacer between tiles } @@ -51,9 +44,7 @@ class EventsCardList extends StatelessWidget { ); } else { return ContainerView( - child: listOfEvents.isEmpty - ? const Center(child: const Text('No events found.')) - : const EventsAll(), + child: listOfEvents.isEmpty ? const Center(child: const Text('No events found.')) : const EventsAll(), ); } } diff --git a/lib/ui/events/events_detail_view.dart b/lib/ui/events/events_detail_view.dart index 79782c90f..b1b843209 100644 --- a/lib/ui/events/events_detail_view.dart +++ b/lib/ui/events/events_detail_view.dart @@ -17,9 +17,7 @@ class EventDetailView extends StatelessWidget { @override Widget build(BuildContext context) { return Provider.of(context).isLoading - ? Center( - child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.secondary)) + ? Center(child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary)) : ContainerView(child: buildDetailView(context)); } @@ -54,9 +52,7 @@ class EventDetailView extends StatelessWidget { Icon( Icons.location_on_sharp, size: 36, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, ), SizedBox(width: 5), Expanded( @@ -66,10 +62,7 @@ class EventDetailView extends StatelessWidget { looseUrl: true, style: TextStyle( fontSize: 16, - color: - Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w400, ), ) @@ -78,17 +71,14 @@ class EventDetailView extends StatelessWidget { SizedBox(width: 5), // Event Time Text( - data.startDate.toLocal().hour == 0 && - data.endDate.toLocal().hour == 23 + data.startDate.toLocal().hour == 0 && data.endDate.toLocal().hour == 23 ? ' All day ' : DateFormat.jm().format(data.startDate.toLocal()) + ' - ' + DateFormat.jm().format(data.endDate.toLocal()), style: TextStyle( fontSize: 16, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w400, ), ), @@ -104,10 +94,7 @@ class EventDetailView extends StatelessWidget { data.description != null && data.description!.isNotEmpty ? Text( data.description!, - style: TextStyle( - fontSize: 16, - height: 1.4, - fontWeight: FontWeight.w400), + style: TextStyle(fontSize: 16, height: 1.4, fontWeight: FontWeight.w400), ) : Container(), ], @@ -115,9 +102,7 @@ class EventDetailView extends StatelessWidget { Container( padding: EdgeInsets.only(left: 15, top: 5, right: 248, bottom: 20), // "GO TO EVENT PAGE" Button - child: data.link != null && data.link!.isNotEmpty - ? GoToEventPageButton(link: data.link!) - : Container(), + child: data.link != null && data.link!.isNotEmpty ? GoToEventPageButton(link: data.link!) : Container(), ) ], ); @@ -129,9 +114,9 @@ class EventDetailView extends StatelessWidget { final localEnd = end.toLocal(); // If today is within the event window - if (!now.isBefore(localStart) && !now.isAfter(localEnd)) { - return now; - } + final bool isNotBeforeStart = !now.isBefore(localStart); + final bool isNotAfterEnd = !now.isAfter(localEnd); + if (isNotBeforeStart && isNotAfterEnd) return now; // Otherwise, show the earlier of the two future dates return localStart.isBefore(localEnd) ? localStart : localEnd; @@ -156,8 +141,7 @@ class EventImage extends StatelessWidget { image: DecorationImage( fit: BoxFit.cover, // Ensure the image fills the container image: (imageUrl.isEmpty) - ? AssetImage('assets/images/UCSDMobile_banner.png') - as ImageProvider + ? AssetImage('assets/images/UCSDMobile_banner.png') as ImageProvider : NetworkImage(imageUrl), ), ), @@ -179,27 +163,21 @@ class EventDateContainer extends StatelessWidget { Text(date.split(' ')[0].toUpperCase(), style: TextStyle( fontSize: 18, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w400, )), // Day Text(date.split(' ')[1].toUpperCase(), style: TextStyle( fontSize: 20, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w500, )), // Year Text(date.split(' ')[2].toUpperCase(), style: TextStyle( fontSize: 18, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w400, )), ], @@ -225,9 +203,7 @@ class EventTitle extends StatelessWidget { style: TextStyle( fontSize: 22, fontWeight: FontWeight.w500, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, ), ), ), @@ -251,22 +227,19 @@ class GoToEventPageButton extends StatelessWidget { style: ElevatedButton.styleFrom( backgroundColor: actionButtonBackgroundColor, padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16), - shape: - RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), onPressed: () async { try { await launch(link, forceSafariVC: true); } catch (e) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text('Could not open.'))); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Could not open.'))); } }, child: FittedBox( child: Row( children: [ - Text('GO TO EVENT PAGE', - style: TextStyle(fontSize: 18, color: lightPrimaryColor)), + Text('GO TO EVENT PAGE', style: TextStyle(fontSize: 18, color: lightPrimaryColor)), SizedBox(width: 4), Icon(Icons.open_in_new, size: 18, color: lightPrimaryColor), ], diff --git a/lib/ui/events/events_view_all.dart b/lib/ui/events/events_view_all.dart index f28cb3061..c003f9173 100644 --- a/lib/ui/events/events_view_all.dart +++ b/lib/ui/events/events_view_all.dart @@ -11,9 +11,7 @@ class EventsAll extends StatelessWidget { @override Widget build(BuildContext context) { return Provider.of(context).isLoading - ? Center( - child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.secondary)) + ? Center(child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary)) : Padding( padding: const EdgeInsets.only(top: 16.0), child: buildEventsList( @@ -38,8 +36,7 @@ class EventsAll extends StatelessWidget { crossAxisSpacing: 1, mainAxisSpacing: 8, children: eventTiles, - childAspectRatio: MediaQuery.of(context).size.width / - (MediaQuery.of(context).size.height / 1.4), + childAspectRatio: MediaQuery.of(context).size.width / (MediaQuery.of(context).size.height / 1.4), ); } else { return ContainerView( diff --git a/lib/ui/finals/finals_card.dart b/lib/ui/finals/finals_card.dart index de3814a84..3b91c0bd2 100644 --- a/lib/ui/finals/finals_card.dart +++ b/lib/ui/finals/finals_card.dart @@ -16,19 +16,17 @@ class FinalsCard extends StatelessWidget { Widget build(BuildContext context) { return CardContainer( active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), + hide: () => Provider.of(context, listen: false).toggleCard(cardId), reload: () { - if (Provider.of(context, listen: false) - .isLoading) { + final bool isLoading = Provider.of(context, listen: false).isLoading; + if (isLoading) { return null; } else { - Provider.of(context, listen: false) - .fetchData(); + Provider.of(context, listen: false).fetchData(); } }, isLoading: Provider.of(context).isLoading, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[cardId]!, errorText: Provider.of(context).error, child: () => buildFinalsCard( Provider.of(context).finals, @@ -61,8 +59,8 @@ class FinalsCard extends StatelessWidget { } } - Widget buildFinalsCard(Map> finalsData, - DateTime lastUpdated, String? nextDayWithClasses, BuildContext context) { + Widget buildFinalsCard(Map> finalsData, DateTime lastUpdated, String? nextDayWithClasses, + BuildContext context) { try { var finalsCount = 0, i = 1; // Iterate through the map and count the number Finals @@ -83,8 +81,7 @@ class FinalsCard extends StatelessWidget { // CSE 127 19:00 - 21:59 Row( children: [ - buildClassCode( - context, data.subjectCode! + ' ' + data.courseCode!), + buildClassCode(context, data.subjectCode! + ' ' + data.courseCode!), SizedBox(width: 10), // 10 logical pixels buildTimeRow(context, data.time), ], @@ -96,8 +93,7 @@ class FinalsCard extends StatelessWidget { // WLH 2005 buildLocationRow(context, data.building! + ' ' + data.room!), ///////////////// Horizontal Division /////////////////// - if (i < finalsCount) - Divider(color: listTileDividerColorLight, thickness: 0.7), + if (i < finalsCount) Divider(color: listTileDividerColorLight, thickness: 0.7), ], ), )); @@ -116,8 +112,7 @@ class FinalsCard extends StatelessWidget { shrinkWrap: true, ); } catch (e) { - FirebaseCrashlytics.instance.recordError( - e, StackTrace.fromString(e.toString()), + FirebaseCrashlytics.instance.recordError(e, StackTrace.fromString(e.toString()), reason: "Finals Card: Failed to build card content.", fatal: false); return Container( width: double.infinity, @@ -125,8 +120,8 @@ class FinalsCard extends StatelessWidget { child: Padding( padding: EdgeInsets.only(left: 12, top: 32, bottom: 48), child: Container( - child: Text( - "Your finals could not be displayed.\n\nIf the problem persists contact mobilesupport@ucsd.edu"), + child: + Text("Your finals could not be displayed.\n\nIf the problem persists contact mobilesupport@ucsd.edu"), ), ), ), @@ -141,9 +136,7 @@ class FinalsCard extends StatelessWidget { style: TextStyle( fontSize: 24.0, fontWeight: FontWeight.w500, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, ), ); } @@ -156,9 +149,7 @@ class FinalsCard extends StatelessWidget { fontFamily: 'Refrigerator Deluxe', fontWeight: FontWeight.w900, letterSpacing: 1.1, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, )); } @@ -168,9 +159,7 @@ class FinalsCard extends StatelessWidget { time ?? 'TBA', // TBA if time is null or empty style: TextStyle( fontSize: 16, - color: Theme.of(context).brightness == Brightness.light - ? descriptiveTextColorLight - : descriptiveTextColorDark, + color: Theme.of(context).brightness == Brightness.light ? descriptiveTextColorLight : descriptiveTextColorDark, fontWeight: FontWeight.w400, ), ); @@ -181,9 +170,8 @@ class FinalsCard extends StatelessWidget { return Text(title, style: TextStyle( fontSize: 18.0, - color: Theme.of(context).brightness == Brightness.light - ? descriptiveTextColorLight - : descriptiveTextColorDark, + color: + Theme.of(context).brightness == Brightness.light ? descriptiveTextColorLight : descriptiveTextColorDark, fontWeight: FontWeight.w400)); } diff --git a/lib/ui/home/home.dart b/lib/ui/home/home.dart index b8e9f6640..f3142991b 100644 --- a/lib/ui/home/home.dart +++ b/lib/ui/home/home.dart @@ -15,8 +15,8 @@ import 'package:campus_mobile_experimental/ui/dining/dining_card.dart'; import 'package:campus_mobile_experimental/ui/employee_id/employee_id_card.dart'; import 'package:campus_mobile_experimental/ui/events/events_card.dart'; import 'package:campus_mobile_experimental/ui/finals/finals_card.dart'; -import 'package:campus_mobile_experimental/ui/mystudentchart/mystudentchart_card.dart'; -import 'package:campus_mobile_experimental/ui/myucsdchart/myucsdchart_card.dart'; +import 'package:campus_mobile_experimental/ui/my_student_chart/my_student_chart_card.dart'; +import 'package:campus_mobile_experimental/ui/my_ucsd_chart/my_ucsd_chart_card.dart'; import 'package:campus_mobile_experimental/ui/navigator/bottom.dart'; import 'package:campus_mobile_experimental/ui/navigator/top.dart'; import 'package:campus_mobile_experimental/ui/news/news_card.dart'; @@ -63,13 +63,10 @@ class MeasureSize extends SingleChildRenderObjectWidget { }) : super(key: key, child: child); @override - RenderObject createRenderObject(BuildContext context) { - return MeasureSizeRenderObject(onChange); - } + RenderObject createRenderObject(BuildContext context) => MeasureSizeRenderObject(onChange); @override - void updateRenderObject( - BuildContext context, covariant MeasureSizeRenderObject renderObject) { + void updateRenderObject(BuildContext context, covariant MeasureSizeRenderObject renderObject) { renderObject.onChange = onChange; } } @@ -115,7 +112,9 @@ class _HomeState extends State { Uri? initialUri = await appLinks.getInitialAppLink(); String? initialLink = initialUri?.toString(); - if (initialLink != null && initialLink.contains("deeplinking.searchmap")) { + final bool hasInitialLink = initialLink != null; + final bool isSearchMapLink = hasInitialLink && initialLink.contains("deeplinking.searchmap"); + if (hasInitialLink && isSearchMapLink) { var uri = Uri.dataFromString(initialLink); var query = uri.queryParameters['query']!; executeQuery(query); @@ -123,7 +122,9 @@ class _HomeState extends State { _sub = appLinks.uriLinkStream.listen((Uri? uri) async { String? link = uri?.toString(); - if (link != null && link.contains("deeplinking.searchmap")) { + final bool hasLink = link != null; + final bool isSearchMapLink = hasLink && link.contains("deeplinking.searchmap"); + if (hasLink && isSearchMapLink) { var query = uri!.queryParameters['query']!; executeQuery(query); _sub?.cancel(); @@ -134,8 +135,7 @@ class _HomeState extends State { void executeQuery(String query) { context.read().searchBarController.text = query; context.read().fetchLocations(); - context.read().currentIndex = - NavigatorConstants.MapTab; + context.read().currentIndex = NavigatorConstants.MAP_TAB; context.read().changeTitle("Maps"); executedInitialDeeplinkQuery = true; } @@ -145,40 +145,34 @@ class _HomeState extends State { // Provider.of(context).changeTitle(null); // reset title to logo (for dining) _connectivityProvider = Provider.of(context); return Padding( - padding: - const EdgeInsets.symmetric(horizontal: cardMargin, vertical: 0.0), + padding: const EdgeInsets.symmetric(horizontal: cardMargin, vertical: 0.0), child: ListView( controller: _controller, - padding: const EdgeInsets.only( - top: 0.0, right: 0.0, bottom: cardMargin + 2.0, left: 0.0), + padding: const EdgeInsets.only(top: 0.0, right: 0.0, bottom: cardMargin + 2.0, left: 0.0), children: createList(), ), ); } List createList() { - final orderedCards = - getOrderedCardsList(context.watch().cardOrder); - final noticesCards = - getNoticesCardsList(context.watch().noticesModel); + final orderedCards = getOrderedCardsList(context.watch().cardOrder); + final noticesCards = getNoticesCardsList(context.watch().noticesModel); return [...noticesCards, ...orderedCards]; } - List getNoticesCardsList(List notices) => notices - .map((notice) => NoticesCard(notice: notice)) - .whereType() - .toList(); + List getNoticesCardsList(List notices) => + notices.map((notice) => NoticesCard(notice: notice)).whereType().toList(); // Constructor tear-offs used below to generate ordered cards list in O(1) time - static const _cardCtors = { - 'MyStudentChart': MyStudentChartCard.new, + static const _CARD_CTORS = { + 'my_student_chart': MyStudentChartCard.new, 'dining': DiningCard.new, 'news': NewsCard.new, 'events': EventsCard.new, 'availability': AvailabilityCard.new, 'schedule': ClassScheduleCard.new, 'finals': FinalsCard.new, - 'MyUCSDChart': MyUCSDChartCard.new, + 'my_ucsd_chart': MyUCSDChartCard.new, 'student_id': StudentIdCard.new, 'employee_id': EmployeeIdCard.new, 'parking': ParkingCard.new, @@ -205,21 +199,19 @@ class _HomeState extends State { final webCards = context.read().webCards; for (String cardName in order) { - /// TODO: if-branches logic here theoretically could be simplified + // TODO: if-branches logic here theoretically could be simplified - December 2025 if (!webCards.containsKey(cardName)) { - final cardCtor = _cardCtors[cardName]; - if (cardCtor != null) { - orderedCards.add(cardCtor()); - } + final cardCtor = _CARD_CTORS[cardName]; + if (cardCtor != null) orderedCards.add(cardCtor()); } else { final card = webCards[cardName]!; orderedCards.add( WebViewContainer( key: ValueKey(cardName), initialUrl: card.initialURL, - titleText: card.titleText ?? '', + titleText: card.titleText, cardId: cardName, - requireAuth: card.requireAuth ?? false, + requireAuth: card.requireAuth, isLoaded: webViewCardNotLoaded[cardName] ?? true, onPageFinished: () { // Only update if loaded state is not already true diff --git a/lib/ui/map/directions_button.dart b/lib/ui/map/directions_button.dart index a8d2ee629..d183a5749 100644 --- a/lib/ui/map/directions_button.dart +++ b/lib/ui/map/directions_button.dart @@ -20,28 +20,20 @@ class DirectionsButton extends StatelessWidget { ), backgroundColor: Colors.white, onPressed: () { - if (Provider.of(context, listen: false) - .coordinates! - .lat == - null || - Provider.of(context, listen: false) - .coordinates! - .lon == - null) { + final MapsDataProvider mapsProvider = Provider.of(context, listen: false); + final bool hasNullLatitude = mapsProvider.coordinates!.lat == null; + final bool hasNullLongitude = mapsProvider.coordinates!.lon == null; + if (hasNullLatitude || hasNullLongitude) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - 'Please turn your location on in order to use this feature.'), + content: Text('Please turn your location on in order to use this feature.'), duration: Duration(seconds: 3), )); } else { - String locationQuery = - Provider.of(context, listen: false) - .searchBarController - .text; + String locationQuery = Provider.of(context, listen: false).searchBarController.text; if (locationQuery.isNotEmpty) { getDirections(context); } else { - Navigator.pushNamed(context, RoutePaths.MapSearch); + Navigator.pushNamed(context, RoutePaths.MAP_SEARCH); } } }, @@ -49,11 +41,7 @@ class DirectionsButton extends StatelessWidget { } Future getDirections(BuildContext context) async { - LatLng currentPin = Provider.of(context, listen: false) - .markers - .values - .toList()[0] - .position; + LatLng currentPin = Provider.of(context, listen: false).markers.values.toList()[0].position; double lat = currentPin.latitude; double lon = currentPin.longitude; diff --git a/lib/ui/map/map.dart b/lib/ui/map/map.dart index 689622a3b..106434d5b 100644 --- a/lib/ui/map/map.dart +++ b/lib/ui/map/map.dart @@ -22,8 +22,7 @@ class Maps extends StatelessWidget { WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(context) ..removeCurrentSnackBar() - ..showSnackBar( - SnackBar(content: Text('No results found for your search.'))); + ..showSnackBar(SnackBar(content: Text('No results found for your search.'))); }); } return Container(); @@ -38,9 +37,7 @@ class Maps extends StatelessWidget { right: width * 0.05, child: Column( children: [ - MyLocationButton( - mapController: - Provider.of(context).mapController), + MyLocationButton(mapController: Provider.of(context).mapController), SizedBox(height: 10), DirectionsButton(), ], @@ -54,27 +51,25 @@ class Maps extends StatelessWidget { Uri? initialUri = await appLinks.getInitialAppLink(); String? initialLink = initialUri?.toString(); - if (initialLink != null && initialLink.contains("deeplinking.searchmap")) { + final bool hasInitialLink = initialLink != null; + final bool isSearchMapLink = hasInitialLink && initialLink.contains("deeplinking.searchmap"); + if (hasInitialLink && isSearchMapLink) { var uri = Uri.dataFromString(initialLink); var query = uri.queryParameters['query']!; - Provider.of(context, listen: false) - .searchBarController - .text = query; + Provider.of(context, listen: false).searchBarController.text = query; Provider.of(context, listen: false).fetchLocations(); - Provider.of(context, listen: false) - .currentIndex = NavigatorConstants.MapTab; + Provider.of(context, listen: false).currentIndex = NavigatorConstants.MAP_TAB; } _sub = appLinks.uriLinkStream.listen((Uri? uri) async { String? link = uri?.toString(); - if (link != null && link.contains("deeplinking.searchmap")) { + final bool hasLink = link != null; + final bool isSearchMapLink = hasLink && link.contains("deeplinking.searchmap"); + if (hasLink && isSearchMapLink) { var query = uri!.queryParameters['query']!; - Provider.of(context, listen: false) - .searchBarController - .text = query; + Provider.of(context, listen: false).searchBarController.text = query; Provider.of(context, listen: false).fetchLocations(); - Provider.of(context, listen: false) - .currentIndex = NavigatorConstants.MapTab; + Provider.of(context, listen: false).currentIndex = NavigatorConstants.MAP_TAB; _sub?.cancel(); } }); @@ -86,15 +81,13 @@ class Maps extends StatelessWidget { return Stack( children: [ GoogleMap( - markers: Set.of( - Provider.of(context).markers.values), + markers: Set.of(Provider.of(context).markers.values), myLocationEnabled: true, myLocationButtonEnabled: false, mapToolbarEnabled: false, zoomControlsEnabled: false, onMapCreated: (controller) { - Provider.of(context, listen: false) - .mapController = controller; + Provider.of(context, listen: false).mapController = controller; }, initialCameraPosition: CameraPosition( target: const LatLng(32.8801, -117.2341), diff --git a/lib/ui/map/map_search_bar_ph.dart b/lib/ui/map/map_search_bar_ph.dart index 54cfd7e77..04cb80c43 100644 --- a/lib/ui/map/map_search_bar_ph.dart +++ b/lib/ui/map/map_search_bar_ph.dart @@ -16,7 +16,7 @@ class MapSearchBarPlaceHolder extends StatelessWidget { margin: EdgeInsets.all(5), child: RawMaterialButton( onPressed: () { - Navigator.pushNamed(context, RoutePaths.MapSearch); + Navigator.pushNamed(context, RoutePaths.MAP_SEARCH); }, child: Row( children: [ @@ -28,9 +28,7 @@ class MapSearchBarPlaceHolder extends StatelessWidget { child: Container( height: 25, width: 25, - child: CircularProgressIndicator( - color: - Theme.of(context).colorScheme.secondary)), + child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary)), ) : Icon( Icons.search, @@ -40,8 +38,7 @@ class MapSearchBarPlaceHolder extends StatelessWidget { Expanded( child: TextField( enabled: false, - controller: Provider.of(context) - .searchBarController, + controller: Provider.of(context).searchBarController, style: TextStyle(fontSize: 20), decoration: InputDecoration( border: InputBorder.none, diff --git a/lib/ui/map/more_results_list.dart b/lib/ui/map/more_results_list.dart index 2e7f170e6..57f4f1dc4 100644 --- a/lib/ui/map/more_results_list.dart +++ b/lib/ui/map/more_results_list.dart @@ -25,8 +25,7 @@ class MoreResultsList extends StatelessWidget { alignment: Alignment.center, child: Text( 'More Results', - style: - TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ), Divider( @@ -34,23 +33,15 @@ class MoreResultsList extends StatelessWidget { ), Expanded( child: ListView.builder( - itemCount: Provider.of(context) - .mapSearchModels - .length, + itemCount: Provider.of(context).mapSearchModels.length, itemBuilder: (BuildContext cntxt, int index) { return ListTile( title: Text( - Provider.of(cntxt, listen: false) - .mapSearchModels[index] - .title!, + Provider.of(cntxt, listen: false).mapSearchModels[index].title!, ), trailing: Text( - Provider.of(cntxt, listen: false) - .mapSearchModels[index] - .distance != - null - ? Provider.of(cntxt, - listen: false) + Provider.of(cntxt, listen: false).mapSearchModels[index].distance != null + ? Provider.of(cntxt, listen: false) .mapSearchModels[index] .distance! .toStringAsFixed(1) + @@ -59,8 +50,7 @@ class MoreResultsList extends StatelessWidget { style: TextStyle(color: Colors.blue[600]), ), onTap: () { - Provider.of(cntxt, listen: false) - .addMarker(index); + Provider.of(cntxt, listen: false).addMarker(index); Navigator.pop(cntxt); }, ); diff --git a/lib/ui/map/my_location_button.dart b/lib/ui/map/my_location_button.dart index 61af0639f..20b9ce0df 100644 --- a/lib/ui/map/my_location_button.dart +++ b/lib/ui/map/my_location_button.dart @@ -22,27 +22,18 @@ class MyLocationButton extends StatelessWidget { ), backgroundColor: Colors.lightBlue, onPressed: () { - if (Provider.of(context, listen: false) - .coordinates! - .lat == - null || - Provider.of(context, listen: false) - .coordinates! - .lon == - null) { + final MapsDataProvider mapsProvider = Provider.of(context, listen: false); + final bool hasNullLatitude = mapsProvider.coordinates!.lat == null; + final bool hasNullLongitude = mapsProvider.coordinates!.lon == null; + if (hasNullLatitude || hasNullLongitude) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - 'Please turn your location on in order to use this feature.'), + content: Text('Please turn your location on in order to use this feature.'), duration: Duration(seconds: 3), )); } else { _mapController!.animateCamera(CameraUpdate.newLatLng(LatLng( - Provider.of(context, listen: false) - .coordinates! - .lat!, - Provider.of(context, listen: false) - .coordinates! - .lon!))); + Provider.of(context, listen: false).coordinates!.lat!, + Provider.of(context, listen: false).coordinates!.lon!))); } }, ); diff --git a/lib/ui/map/quick_search_icons.dart b/lib/ui/map/quick_search_icons.dart index 2d7e4a6a7..d7405d3db 100644 --- a/lib/ui/map/quick_search_icons.dart +++ b/lib/ui/map/quick_search_icons.dart @@ -21,11 +21,8 @@ class QuickSearchIcons extends StatelessWidget { icon: Icons.local_parking, text: 'Parking', onPressed: () { - Provider.of(context, listen: false) - .searchBarController - .text = 'Parking'; - Provider.of(context, listen: false) - .fetchLocations(); + Provider.of(context, listen: false).searchBarController.text = 'Parking'; + Provider.of(context, listen: false).fetchLocations(); Navigator.pop(context); }, ), @@ -33,11 +30,8 @@ class QuickSearchIcons extends StatelessWidget { icon: Icons.local_drink, text: 'Hydration', onPressed: () { - Provider.of(context, listen: false) - .searchBarController - .text = 'Hydration'; - Provider.of(context, listen: false) - .fetchLocations(); + Provider.of(context, listen: false).searchBarController.text = 'Hydration'; + Provider.of(context, listen: false).fetchLocations(); Navigator.pop(context); }, ), @@ -45,11 +39,8 @@ class QuickSearchIcons extends StatelessWidget { icon: Icons.local_atm, text: 'ATM', onPressed: () { - Provider.of(context, listen: false) - .searchBarController - .text = 'ATM'; - Provider.of(context, listen: false) - .fetchLocations(); + Provider.of(context, listen: false).searchBarController.text = 'ATM'; + Provider.of(context, listen: false).fetchLocations(); Navigator.pop(context); }, ), diff --git a/lib/ui/map/search_bar.dart b/lib/ui/map/search_bar.dart index 434822836..de448e3b9 100644 --- a/lib/ui/map/search_bar.dart +++ b/lib/ui/map/search_bar.dart @@ -27,10 +27,9 @@ class MapSearchBar extends StatelessWidget { textInputAction: TextInputAction.search, onChanged: (text) {}, onSubmitted: (text) { - if (Provider.of(context, listen: false) - .searchBarController - .text - .isNotEmpty) { + final bool hasText = + Provider.of(context, listen: false).searchBarController.text.isNotEmpty; + if (hasText) { // Don't fetch on empty text field Provider.of(context, listen: false) .fetchLocations(); // Text doesn't need to be sent over because it's already in the controller @@ -38,8 +37,7 @@ class MapSearchBar extends StatelessWidget { Navigator.pop(context); }, autofocus: true, - controller: - Provider.of(context).searchBarController, + controller: Provider.of(context).searchBarController, style: TextStyle(fontSize: 20), decoration: InputDecoration( border: InputBorder.none, @@ -48,19 +46,12 @@ class MapSearchBar extends StatelessWidget { ), ), ), - Provider.of(context) - .searchBarController - .text - .isNotEmpty + Provider.of(context).searchBarController.text.isNotEmpty ? IconButton( icon: Icon(Icons.clear), onPressed: () { - Provider.of(context, listen: false) - .searchBarController - .clear(); - Provider.of(context, listen: false) - .markers - .clear(); + Provider.of(context, listen: false).searchBarController.clear(); + Provider.of(context, listen: false).markers.clear(); }, ) : Container(height: 0) diff --git a/lib/ui/map/search_history_list.dart b/lib/ui/map/search_history_list.dart index 6ad1bcb34..671e1370c 100644 --- a/lib/ui/map/search_history_list.dart +++ b/lib/ui/map/search_history_list.dart @@ -14,39 +14,25 @@ class SearchHistoryList extends StatelessWidget { margin: EdgeInsets.all(5), child: ListView.separated( separatorBuilder: (context, index) => Divider(height: 0), - itemCount: - Provider.of(context).searchHistory.length, + itemCount: Provider.of(context).searchHistory.length, shrinkWrap: true, itemBuilder: (context, index) { return ListTile( contentPadding: EdgeInsets.symmetric(horizontal: 15, vertical: 0), leading: Icon(Icons.history), - title: Text(Provider.of(context) - .searchHistory - .reversed - .toList()[index]), + title: Text(Provider.of(context).searchHistory.reversed.toList()[index]), trailing: IconButton( iconSize: 20, icon: Icon(Icons.cancel), onPressed: () { - Provider.of(context, listen: false) - .removeFromSearchHistory( - Provider.of(context, listen: false) - .searchHistory - .reversed - .toList()[index]); + Provider.of(context, listen: false).removeFromSearchHistory( + Provider.of(context, listen: false).searchHistory.reversed.toList()[index]); }, ), onTap: () { - Provider.of(context, listen: false) - .searchBarController - .text = - Provider.of(context, listen: false) - .searchHistory - .reversed - .toList()[index]; - Provider.of(context, listen: false) - .fetchLocations(); + Provider.of(context, listen: false).searchBarController.text = + Provider.of(context, listen: false).searchHistory.reversed.toList()[index]; + Provider.of(context, listen: false).fetchLocations(); Navigator.pop(context); }, ); diff --git a/lib/ui/mystudentchart/mystudentchart_card.dart b/lib/ui/my_student_chart/my_student_chart_card.dart similarity index 87% rename from lib/ui/mystudentchart/mystudentchart_card.dart rename to lib/ui/my_student_chart/my_student_chart_card.dart index 4edf2f489..8010a8b56 100644 --- a/lib/ui/mystudentchart/mystudentchart_card.dart +++ b/lib/ui/my_student_chart/my_student_chart_card.dart @@ -6,18 +6,17 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; -const cardId = 'MyStudentChart'; +const cardId = 'my_student_chart'; class MyStudentChartCard extends StatelessWidget { @override Widget build(BuildContext context) { return CardContainer( active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), + hide: () => Provider.of(context, listen: false).toggleCard(cardId), reload: () => null, isLoading: false, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[cardId]!, errorText: null, child: () => buildCardContent(context), actionButtons: [ @@ -26,8 +25,7 @@ class MyStudentChartCard extends StatelessWidget { onPressed: () { try { launchUrl( - Uri.parse( - 'https://mystudentchart.ucsd.edu/SHS/Authentication/Saml/Login?idp=UCSD_STUDENT_AD_LOGIN'), + Uri.parse('https://mystudentchart.ucsd.edu/SHS/Authentication/Saml/Login?idp=UCSD_STUDENT_AD_LOGIN'), mode: LaunchMode.inAppBrowserView); } catch (e) { // an error occurred, do nothing diff --git a/lib/ui/myucsdchart/myucsdchart_card.dart b/lib/ui/my_ucsd_chart/my_ucsd_chart_card.dart similarity index 89% rename from lib/ui/myucsdchart/myucsdchart_card.dart rename to lib/ui/my_ucsd_chart/my_ucsd_chart_card.dart index 42d3fee06..e9dc7c175 100644 --- a/lib/ui/myucsdchart/myucsdchart_card.dart +++ b/lib/ui/my_ucsd_chart/my_ucsd_chart_card.dart @@ -6,18 +6,17 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; -const cardId = 'MyUCSDChart'; +const cardId = 'my_ucsd_chart'; class MyUCSDChartCard extends StatelessWidget { @override Widget build(BuildContext context) { return CardContainer( active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), + hide: () => Provider.of(context, listen: false).toggleCard(cardId), reload: () => null, isLoading: false, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[cardId]!, errorText: null, child: () => buildCardContent(context), actionButtons: [ @@ -25,8 +24,7 @@ class MyUCSDChartCard extends StatelessWidget { buttonText: 'LOG IN TO MyUCSDChart', onPressed: () { try { - const url = - 'https://myucsdchart.ucsd.edu/UCSD/Authentication/Login'; + const url = 'https://myucsdchart.ucsd.edu/UCSD/Authentication/Login'; launchUrl(Uri.parse(url), mode: LaunchMode.inAppBrowserView); } catch (e) { // an error occurred, do nothing diff --git a/lib/ui/navigator/bottom.dart b/lib/ui/navigator/bottom.dart index 37f287539..00cbaad3d 100644 --- a/lib/ui/navigator/bottom.dart +++ b/lib/ui/navigator/bottom.dart @@ -13,15 +13,13 @@ import 'package:provider/provider.dart'; // ---saved scroll offsets for Home Screen--- var _homeScrollOffset = 0.0; double getHomeScrollOffset() => _homeScrollOffset; -void setHomeScrollOffset(double currentScrollOffset) => - _homeScrollOffset = currentScrollOffset; +void setHomeScrollOffset(double currentScrollOffset) => _homeScrollOffset = currentScrollOffset; void resetHomeScrollOffset() => _homeScrollOffset = 0.0; // ---saved scroll offsets for Notification Screen--- var _notificationsScrollOffset = 0.0; double getNotificationsScrollOffset() => _notificationsScrollOffset; -void setNotificationsScrollOffset(double currentScrollOffset) => - _notificationsScrollOffset = currentScrollOffset; +void setNotificationsScrollOffset(double currentScrollOffset) => _notificationsScrollOffset = currentScrollOffset; void resetNotificationsScrollOffset() => _notificationsScrollOffset = 0.0; class BottomTabBar extends StatefulWidget { @@ -44,12 +42,8 @@ class _BottomTabBarState extends State { return Scaffold( drawerScrimColor: Colors.transparent, - backgroundColor: provider.currentIndex == 0 - ? lightPrimaryColor - : theme.scaffoldBackgroundColor, - appBar: PreferredSize( - preferredSize: Size.fromHeight(50), - child: Provider.of(context).appBar), + backgroundColor: provider.currentIndex == 0 ? lightPrimaryColor : theme.scaffoldBackgroundColor, + appBar: PreferredSize(preferredSize: Size.fromHeight(50), child: Provider.of(context).appBar), body: PushNotificationWrapper(child: currentTab[provider.currentIndex]), bottomNavigationBar: Container( decoration: BoxDecoration( @@ -70,26 +64,21 @@ class _BottomTabBarState extends State { onTap: (index) { provider.currentIndex = index; switch (index) { - case NavigatorConstants.HomeTab: - Provider.of(context, listen: false) - .changeTitle(null); + case NavigatorConstants.HOME_TAB: + Provider.of(context, listen: false).changeTitle(null); break; - case NavigatorConstants.MapTab: + case NavigatorConstants.MAP_TAB: resetAllCardLoadedStates(); - Provider.of(context, listen: false) - .changeTitle("Maps"); + Provider.of(context, listen: false).changeTitle("Maps"); break; - case NavigatorConstants.NotificationsTab: + case NavigatorConstants.NOTIFICATIONS_TAB: resetAllCardLoadedStates(); - Provider.of(context, listen: false).changeTitle( - "Notifications", - done: false, - notification: true); + Provider.of(context, listen: false) + .changeTitle("Notifications", done: false, notification: true); break; - case NavigatorConstants.ProfileTab: + case NavigatorConstants.PROFILE_TAB: resetAllCardLoadedStates(); - Provider.of(context, listen: false) - .changeTitle("Profile"); + Provider.of(context, listen: false).changeTitle("Profile"); break; } }, @@ -103,20 +92,17 @@ class _BottomTabBarState extends State { label: 'MAP', ), BottomNavigationBarItem( - icon: _buildIcon( - Icons.notifications, provider.currentIndex == 2, theme), + icon: _buildIcon(Icons.notifications, provider.currentIndex == 2, theme), label: 'NOTIFICATIONS', ), BottomNavigationBarItem( - icon: _buildIcon(Icons.person, provider.currentIndex == 3, theme, - size: 38), + icon: _buildIcon(Icons.person, provider.currentIndex == 3, theme, size: 38), label: 'PROFILE', ), ], showSelectedLabels: false, showUnselectedLabels: false, - unselectedItemColor: - theme.bottomNavigationBarTheme.unselectedItemColor, + unselectedItemColor: theme.bottomNavigationBarTheme.unselectedItemColor, selectedItemColor: theme.bottomNavigationBarTheme.selectedItemColor, elevation: 4, selectedFontSize: 0, @@ -128,15 +114,13 @@ class _BottomTabBarState extends State { } // Build bottom navigator icons - Widget _buildIcon(IconData icon, bool isSelected, ThemeData theme, - {double size = 34}) { + Widget _buildIcon(IconData icon, bool isSelected, ThemeData theme, {double size = 34}) { return Container( height: 34, margin: EdgeInsets.only(top: size == 34 ? 4 : 2), padding: EdgeInsets.only(left: 16, right: 16, top: 0, bottom: 0), decoration: BoxDecoration( - color: - isSelected ? theme.listTileTheme.selectedColor : Colors.transparent, + color: isSelected ? theme.listTileTheme.selectedColor : Colors.transparent, borderRadius: BorderRadius.circular(34), ), child: Icon( diff --git a/lib/ui/navigator/top.dart b/lib/ui/navigator/top.dart index 0b19b742d..a7bca507a 100644 --- a/lib/ui/navigator/top.dart +++ b/lib/ui/navigator/top.dart @@ -32,15 +32,13 @@ class CMAppBar extends StatelessWidget { ), onPressed: () { // Set tab bar index to the Home tab - Provider.of(context, listen: false) - .currentIndex = NavigatorConstants.HomeTab; + Provider.of(context, listen: false).currentIndex = + NavigatorConstants.HOME_TAB; // Navigate to Home tab - Navigator.of(context).pushNamedAndRemoveUntil( - RoutePaths.BottomNavigationBar, - (Route route) => false); + Navigator.of(context) + .pushNamedAndRemoveUntil(RoutePaths.BOTTOM_NAVIGATION_BAR, (Route route) => false); // change the appBar title to the ucsd logo - Provider.of(context, listen: false) - .changeTitle(CustomAppBar().appBar.title); + Provider.of(context, listen: false).changeTitle(CustomAppBar().appBar.title); }, )), hasAction: true, @@ -53,7 +51,7 @@ class CMAppBar extends StatelessWidget { child: IconButton( icon: Icon(Icons.filter_list_outlined), onPressed: () { - Navigator.pushNamed(context, RoutePaths.NotificationsFilter); + Navigator.pushNamed(context, RoutePaths.NOTIFICATIONS_FILTER); }, ), ), @@ -90,7 +88,7 @@ class CustomAppBar extends ChangeNotifier { changeTitle(String? newTitle, {done = false, notification = false}) { // print("\x1B[34m[CustomAppBar] new route is " + (newTitle ?? 'null') + "\x1B[0m, done=$done, notification=$notification"); - title = RouteTitles.titleMap[newTitle]; + title = RouteTitles.TITLE_MAP[newTitle]; doneButton = done; notificationsFilterButton = notification; makeAppBar(); diff --git a/lib/ui/news/news_card.dart b/lib/ui/news/news_card.dart index fece1f948..986b654df 100644 --- a/lib/ui/news/news_card.dart +++ b/lib/ui/news/news_card.dart @@ -29,22 +29,19 @@ class NewsCard extends StatelessWidget { List buildActionButtons(BuildContext context) { List actionButtons = []; actionButtons.add(ActionButton( - buttonText: 'VIEW MORE NEWS STORIES', - onPressed: () => Navigator.pushNamed(context, RoutePaths.NewsViewAll))); + buttonText: 'VIEW MORE NEWS STORIES', onPressed: () => Navigator.pushNamed(context, RoutePaths.NEWS_VIEW_ALL))); return actionButtons; } @override Widget build(BuildContext context) { return CardContainer( - /// TODO: need to hook up hidden to state using provider + // TODO: need to hook up hidden to state using provider - December 2025 active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), - reload: () => - Provider.of(context, listen: false).fetchNews(), + hide: () => Provider.of(context, listen: false).toggleCard(cardId), + reload: () => Provider.of(context, listen: false).fetchNews(), isLoading: Provider.of(context).isLoading, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[cardId]!, errorText: Provider.of(context).error, child: () => buildNewsCard(), actionButtons: buildActionButtons(context), diff --git a/lib/ui/news/news_detail_view.dart b/lib/ui/news/news_detail_view.dart index b495b835c..5d5e6244e 100644 --- a/lib/ui/news/news_detail_view.dart +++ b/lib/ui/news/news_detail_view.dart @@ -20,8 +20,7 @@ class NewsDetailView extends StatelessWidget { image: DecorationImage( fit: BoxFit.cover, image: data.image.isEmpty - ? const AssetImage('assets/images/UCSDMobile_banner.png') - as ImageProvider + ? const AssetImage('assets/images/UCSDMobile_banner.png') as ImageProvider : NetworkImage(data.image), ), ), @@ -55,11 +54,8 @@ class NewsDetailView extends StatelessWidget { : Container(), ), Container( - padding: const EdgeInsets.only( - left: 15, top: 20, right: 248, bottom: 20), - child: data.link.isNotEmpty - ? ContinueReadingButton(link: data.link) - : Container(), + padding: const EdgeInsets.only(left: 15, top: 20, right: 248, bottom: 20), + child: data.link.isNotEmpty ? ContinueReadingButton(link: data.link) : Container(), ), ], ), @@ -83,9 +79,7 @@ class NewsDateContainer extends StatelessWidget { parts[0].toUpperCase(), style: TextStyle( fontSize: 18, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w400, ), ), @@ -93,9 +87,7 @@ class NewsDateContainer extends StatelessWidget { parts[1].toUpperCase(), style: TextStyle( fontSize: 20, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w500, ), ), @@ -103,9 +95,7 @@ class NewsDateContainer extends StatelessWidget { parts[2].toUpperCase(), style: TextStyle( fontSize: 18, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, fontWeight: FontWeight.w400, ), ), @@ -127,9 +117,7 @@ class NewsTitle extends StatelessWidget { style: TextStyle( fontSize: 22, fontWeight: FontWeight.w500, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : Colors.white, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : Colors.white, ), ), ); diff --git a/lib/ui/news/news_list.dart b/lib/ui/news/news_list.dart index 835a070e9..fdc328495 100644 --- a/lib/ui/news/news_list.dart +++ b/lib/ui/news/news_list.dart @@ -78,7 +78,7 @@ class NewsList extends StatelessWidget { onTap: () { Navigator.pushNamed( context, - RoutePaths.NewsDetailView, + RoutePaths.NEWS_DETAIL_VIEW, arguments: newsItem, ); }, @@ -107,14 +107,12 @@ class NewsList extends StatelessWidget { text: TextSpan( children: [ TextSpan( - text: DateFormat.yMMMMd() - .format(newsItem.date.toLocal()), + text: DateFormat.yMMMMd().format(newsItem.date.toLocal()), style: TextStyle( fontSize: 16.0, fontWeight: FontWeight.bold, height: 1.42, - color: - Theme.of(context).textTheme.bodyMedium!.color, + color: Theme.of(context).textTheme.bodyMedium!.color, ), ), TextSpan( @@ -122,17 +120,11 @@ class NewsList extends StatelessWidget { style: Theme.of(context) .textTheme .headlineMedium! - .copyWith( - height: 1.42, - fontSize: 16.0, - decoration: TextDecoration.none), + .copyWith(height: 1.42, fontSize: 16.0, decoration: TextDecoration.none), ), TextSpan( text: newsItem.title, - style: Theme.of(context) - .textTheme - .headlineMedium! - .copyWith(height: 1.42, fontSize: 18.0), + style: Theme.of(context).textTheme.headlineMedium!.copyWith(height: 1.42, fontSize: 18.0), ), ], ), @@ -143,10 +135,7 @@ class NewsList extends StatelessWidget { textAlign: TextAlign.start, overflow: TextOverflow.ellipsis, maxLines: 2, - style: Theme.of(context) - .textTheme - .bodySmall - ?.copyWith(fontSize: 16.0, height: 1.42), + style: Theme.of(context).textTheme.bodySmall?.copyWith(fontSize: 16.0, height: 1.42), ), ], ), diff --git a/lib/ui/notices/notices_card.dart b/lib/ui/notices/notices_card.dart index 32d31d05f..585ac6e4b 100644 --- a/lib/ui/notices/notices_card.dart +++ b/lib/ui/notices/notices_card.dart @@ -16,8 +16,7 @@ class NoticesCard extends StatelessWidget { @override Widget build(BuildContext context) { return Card( - margin: EdgeInsets.only( - top: 0.0, right: 0.0, bottom: cardMargin * 1.5, left: 0.0), + margin: EdgeInsets.only(top: 0.0, right: 0.0, bottom: cardMargin * 1.5, left: 0.0), elevation: 4, shadowColor: Colors.black, shape: RoundedRectangleBorder( diff --git a/lib/ui/notifications/notifications_filter.dart b/lib/ui/notifications/notifications_filter.dart index 26dfd1caf..18597359c 100644 --- a/lib/ui/notifications/notifications_filter.dart +++ b/lib/ui/notifications/notifications_filter.dart @@ -7,9 +7,7 @@ import 'package:provider/provider.dart'; class NotificationsFilterView extends StatelessWidget { @override - Widget build(BuildContext context) { - return ContainerView(child: buildSettingsList(context, getTopics(context))); - } + Widget build(BuildContext context) => ContainerView(child: buildSettingsList(context, getTopics(context))); Widget buildSettingsList(BuildContext context, List? topicsData) { return (topicsData ?? []).isNotEmpty @@ -25,9 +23,7 @@ class NotificationsFilterView extends StatelessWidget { ).toList(), ), ) - : Center( - child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.secondary)); + : Center(child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary)); } List createList(BuildContext context, List topicsAvailable) { @@ -56,16 +52,13 @@ class NotificationsFilterView extends StatelessWidget { trailing: Transform.scale( scale: 0.9, child: Switch.adaptive( - value: Provider.of(context) - .topicSubscriptionState[topic]!, + value: Provider.of(context).topicSubscriptionState[topic]!, onChanged: (_) { - Provider.of(context, listen: false) - .toggleNotifications(topic); + Provider.of(context, listen: false).toggleNotifications(topic); }, thumbColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - return Colors.white; - } + final bool isSelected = states.contains(WidgetState.selected); + if (isSelected) return Colors.white; return null; }), activeColor: toggleActiveColor, @@ -98,17 +91,16 @@ class NotificationsFilterView extends StatelessWidget { List getTopics(BuildContext context) { UserDataProvider _userDataProvider = Provider.of(context); - PushNotificationDataProvider _pushNotificationDataProvider = - Provider.of(context); + PushNotificationDataProvider _pushNotificationDataProvider = Provider.of(context); if (_userDataProvider.userProfileModel.classifications?.student ?? false) { - return _pushNotificationDataProvider.publicTopics() + - _pushNotificationDataProvider.studentTopics(); - } else if (_userDataProvider.userProfileModel.classifications?.staff ?? - false) { - return _pushNotificationDataProvider.publicTopics() + - _pushNotificationDataProvider.staffTopics(); + return _pushNotificationDataProvider.publicTopics() + _pushNotificationDataProvider.studentTopics(); } else { - return _pushNotificationDataProvider.publicTopics(); + final bool isStaff = _userDataProvider.userProfileModel.classifications?.staff ?? false; + if (isStaff) { + return _pushNotificationDataProvider.publicTopics() + _pushNotificationDataProvider.staffTopics(); + } else { + return _pushNotificationDataProvider.publicTopics(); + } } } diff --git a/lib/ui/notifications/notifications_freefood.dart b/lib/ui/notifications/notifications_freefood.dart index 47d81323e..cfdad723c 100644 --- a/lib/ui/notifications/notifications_freefood.dart +++ b/lib/ui/notifications/notifications_freefood.dart @@ -53,9 +53,7 @@ class _CheckBoxButtonState extends State { var isOverCount = _freeFoodDataProvider.isOverCount(messageId!); // print('messageId "' + messageId + '" isOverCount: ' + isOverCount.toString()); var currCount = _freeFoodDataProvider.count(messageId!); - var countText = currCount == 1 - ? '$currCount student is going' - : '$currCount students are going'; + var countText = currCount == 1 ? '$currCount student is going' : '$currCount students are going'; return Container( margin: EdgeInsets.only(top: 8.0), @@ -66,20 +64,16 @@ class _CheckBoxButtonState extends State { width: 170, child: AnimatedCrossFade( duration: Duration(milliseconds: 300), - crossFadeState: isOverCount - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, + crossFadeState: isOverCount ? CrossFadeState.showFirst : CrossFadeState.showSecond, firstChild: Column( children: [ - Text(countText, - style: TextStyle(fontSize: 10, color: Colors.red)), + Text(countText, style: TextStyle(fontSize: 10, color: Colors.red)), Container( margin: EdgeInsets.only(top: 2.0), child: Row( children: [ Icon(Icons.report, color: Colors.grey, size: 15), - Text("There may not be enough food", - style: TextStyle(fontSize: 12)) + Text("There may not be enough food", style: TextStyle(fontSize: 12)) ], )), ], @@ -87,8 +81,7 @@ class _CheckBoxButtonState extends State { ), secondChild: Align( alignment: Alignment.topLeft, - child: Text(countText, - style: TextStyle(fontSize: 12, color: Colors.green)), + child: Text(countText, style: TextStyle(fontSize: 12, color: Colors.green)), ), ), ), @@ -144,8 +137,7 @@ class _CheckBoxButtonState extends State { _toggleGoing(); }, ))), - Text("I'm Going!", - style: TextStyle(color: _textColor, fontSize: 12)), + Text("I'm Going!", style: TextStyle(color: _textColor, fontSize: 12)), ], )), ))); diff --git a/lib/ui/notifications/notifications_list_view.dart b/lib/ui/notifications/notifications_list_view.dart index 34d99b33f..4ef831226 100644 --- a/lib/ui/notifications/notifications_list_view.dart +++ b/lib/ui/notifications/notifications_list_view.dart @@ -15,7 +15,7 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:app_links/app_links.dart'; import 'package:campus_mobile_experimental/ui/navigator/bottom.dart'; -/// TODO: make this not global. Probably put into Widget as stateful variable... +// TODO: make this not global. Probably put into Widget as stateful variable... - December 2025 var hideListView = false; class NotificationsListView extends StatefulWidget { @@ -46,8 +46,7 @@ class _NotificationsListViewState extends State { child: RefreshIndicator( child: buildListView(context), onRefresh: () { - return Provider.of(context, listen: false) - .fetchMessages(true); + return Provider.of(context, listen: false).fetchMessages(true); }, color: Theme.of(context).colorScheme.secondary, ), @@ -55,7 +54,7 @@ class _NotificationsListViewState extends State { } Widget buildListView(BuildContext context) { - /// TODO: fix this logic up + // TODO: fix this logic up - December 2025 Widget Function(BuildContext context, int index)? itemBuilder; var itemCount = 0; if (Provider.of(context).messages.length == 0) { @@ -63,8 +62,7 @@ class _NotificationsListViewState extends State { if (Provider.of(context).isLoading) { // empty notifications view until they load in } else { - itemBuilder = - (BuildContext context, int index) => _buildNoMessagesText(); + itemBuilder = (BuildContext context, int index) => _buildNoMessagesText(); itemCount = 1; } } else { @@ -73,8 +71,7 @@ class _NotificationsListViewState extends State { } } if (itemCount == 0) { - itemBuilder = - (BuildContext context, int index) => _buildMessage(context, index); + itemBuilder = (BuildContext context, int index) => _buildMessage(context, index); itemCount = Provider.of(context).messages.length; } return Padding( @@ -83,13 +80,10 @@ class _NotificationsListViewState extends State { padding: EdgeInsets.only(top: 8), physics: AlwaysScrollableScrollPhysics(), itemBuilder: itemBuilder!, - controller: Provider.of(context, listen: false) - .notificationScrollController, + controller: Provider.of(context, listen: false).notificationScrollController, itemCount: itemCount, separatorBuilder: (BuildContext context, int index) => Divider( - color: Theme.of(context).brightness == Brightness.dark - ? listTileDividerColorDark - : listTileDividerColorLight, + color: Theme.of(context).brightness == Brightness.dark ? listTileDividerColorDark : listTileDividerColorLight, ), ), ); @@ -99,7 +93,7 @@ class _NotificationsListViewState extends State { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(NotificationsConstants.statusFetchProblem), + Text(NotificationsConstants.STATUS_FETCH_PROBLEM), ], ); } @@ -110,7 +104,7 @@ class _NotificationsListViewState extends State { children: [ Flexible( child: Text( - NotificationsConstants.statusNoMessages, + NotificationsConstants.STATUS_NO_MESSAGES, ), ), ], @@ -123,37 +117,33 @@ class _NotificationsListViewState extends State { Uri? initialUri = await appLinks.getInitialAppLink(); String? initialLink = initialUri?.toString(); - if (initialLink != null && initialLink.contains("deeplinking.searchmap")) { + final bool hasInitialLink = initialLink != null; + final bool isSearchMapLink = hasInitialLink && initialLink.contains("deeplinking.searchmap"); + if (hasInitialLink && isSearchMapLink) { var uri = Uri.dataFromString(initialLink); var query = uri.queryParameters['query']!; - Provider.of(context, listen: false) - .searchBarController - .text = query; + Provider.of(context, listen: false).searchBarController.text = query; Provider.of(context, listen: false).fetchLocations(); - Provider.of(context, listen: false) - .currentIndex = NavigatorConstants.MapTab; + Provider.of(context, listen: false).currentIndex = NavigatorConstants.MAP_TAB; } _sub = appLinks.uriLinkStream.listen((Uri? uri) async { String? link = uri?.toString(); - if (link != null && link.contains("deeplinking.searchmap")) { + final bool hasLink = link != null; + final bool isSearchMapLink = hasLink && link.contains("deeplinking.searchmap"); + if (hasLink && isSearchMapLink) { var query = uri!.queryParameters['query']!; - Provider.of(context, listen: false) - .searchBarController - .text = query; + Provider.of(context, listen: false).searchBarController.text = query; Provider.of(context, listen: false).fetchLocations(); - Provider.of(context, listen: false) - .currentIndex = NavigatorConstants.MapTab; + Provider.of(context, listen: false).currentIndex = NavigatorConstants.MAP_TAB; _sub?.cancel(); } }); } Widget _buildMessage(BuildContext context, int index) { - MessageElement data = - Provider.of(context).messages[index]; - FreeFoodDataProvider freefoodProvider = - Provider.of(context); + MessageElement data = Provider.of(context).messages[index]; + FreeFoodDataProvider freefoodProvider = Provider.of(context); String messageType = data.audience.topics?[0] ?? "DM"; return ListView( @@ -180,9 +170,8 @@ class _NotificationsListViewState extends State { Align( alignment: Alignment.topLeft, child: Text(data.message.title, - style: Theme.of(context).brightness == Brightness.dark - ? headlineMediumDark2 - : headlineMediumLight2), + style: + Theme.of(context).brightness == Brightness.dark ? headlineMediumDark2 : headlineMediumLight2), ), ], ), @@ -196,17 +185,16 @@ class _NotificationsListViewState extends State { text: data.message.message, onOpen: (link) async { try { - launchUrl(Uri.parse(link.url), - mode: LaunchMode.inAppBrowserView); + launchUrl(Uri.parse(link.url), mode: LaunchMode.inAppBrowserView); } catch (e) { // an error occurred, do nothing } }, options: LinkifyOptions(humanize: false), - style: Theme.of(context).textTheme.bodySmall?.copyWith( - fontSize: 16, - height: 1.41, - fontWeight: FontWeight.w400)), + style: Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(fontSize: 16, height: 1.41, fontWeight: FontWeight.w400)), freefoodProvider.isFreeFood(data.messageId) ? FreeFoodNotification(messageId: data.messageId) : Container(), @@ -219,10 +207,10 @@ class _NotificationsListViewState extends State { Padding( padding: const EdgeInsets.only(top: 6.0), child: Text(_readTimestamp(data.timestamp), - style: Theme.of(context).textTheme.bodySmall?.copyWith( - fontSize: 12, - height: 1.41, - fontWeight: FontWeight.w700)), + style: Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(fontSize: 12, height: 1.41, fontWeight: FontWeight.w700)), ), ], ), diff --git a/lib/ui/onboarding/onboarding_login.dart b/lib/ui/onboarding/onboarding_login.dart index 0059b54e3..170d0f334 100644 --- a/lib/ui/onboarding/onboarding_login.dart +++ b/lib/ui/onboarding/onboarding_login.dart @@ -47,8 +47,7 @@ class _OnboardingLoginState extends State { body: _userDataProvider.isLoading ? const Center( child: const CircularProgressIndicator( - valueColor: const AlwaysStoppedAnimation( - darkAccentColor), + valueColor: const AlwaysStoppedAnimation(darkAccentColor), ), ) : SingleChildScrollView( @@ -56,10 +55,8 @@ class _OnboardingLoginState extends State { height: _screenHeight, decoration: const BoxDecoration( image: const DecorationImage( - image: const AssetImage( - "assets/images/login-background.png"), - fit: BoxFit - .cover, // Ensure the image covers the entire screen + image: const AssetImage("assets/images/login-background.png"), + fit: BoxFit.cover, // Ensure the image covers the entire screen ), ), child: SafeArea(child: _buildLoginWidget()), @@ -69,8 +66,7 @@ class _OnboardingLoginState extends State { Widget _buildLoginWidget() => Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, // Align items at the top - crossAxisAlignment: - CrossAxisAlignment.center, // Center items horizontally + crossAxisAlignment: CrossAxisAlignment.center, // Center items horizontally children: [ const Spacer(flex: 3), @@ -109,7 +105,7 @@ class _OnboardingLoginState extends State { child: const FractionallySizedBox( widthFactor: 0.64722222, child: const Text( - // TODO: the font here seems a bit light. Might need to swap that out later + // TODO: the font here seems a bit light. Might need to swap that out later - December 2025 "Your personalized gateway to campus life, events, news and more.", textAlign: TextAlign.center, style: const TextStyle( @@ -137,10 +133,7 @@ class _OnboardingLoginState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( - padding: EdgeInsets.only( - left: MediaQuery.of(context).size.width * - ((1 - 0.74444444) / 2) + - 6), + padding: EdgeInsets.only(left: MediaQuery.of(context).size.width * ((1 - 0.74444444) / 2) + 6), child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFFFCD00), // Yellow Button @@ -157,8 +150,7 @@ class _OnboardingLoginState extends State { ), child: Semantics( button: true, - hint: - 'press to login with your information inputted in above textfields', + hint: 'press to login with your information inputted in above textfields', child: const Text( 'SIGN IN', style: const TextStyle( @@ -174,12 +166,10 @@ class _OnboardingLoginState extends State { ? null : () { _userDataProvider - .manualLogin(_emailTextFieldController.text, - _passwordTextFieldController.text) + .manualLogin(_emailTextFieldController.text, _passwordTextFieldController.text) .then((isLoggedIn) async { if (isLoggedIn) { - Navigator.pushNamedAndRemoveUntil(context, - RoutePaths.OnboardingInitial, (_) => false); + Navigator.pushNamedAndRemoveUntil(context, RoutePaths.ONBOARDING_INITIAL, (_) => false); } else { showAlertDialog(context); } @@ -192,14 +182,10 @@ class _OnboardingLoginState extends State { // SizedBox(width: _screenWidth * 0.202), Padding( - padding: EdgeInsets.only( - right: MediaQuery.of(context).size.width * - ((1 - 0.74444444) / 2) + - 6), + padding: EdgeInsets.only(right: MediaQuery.of(context).size.width * ((1 - 0.74444444) / 2) + 6), child: GestureDetector( child: Semantics( - hint: - 'press to be redirected to the UCSD Password reset page', + hint: 'press to be redirected to the UCSD Password reset page', child: const Text( 'Forgot Password?', style: const TextStyle( @@ -212,11 +198,9 @@ class _OnboardingLoginState extends State { ), onTap: () async { try { - /// TODO: Update link to redirect to password reset - String link = - 'https://acms.ucsd.edu/students/accounts-and-passwords/index.html'; - await launchUrl(Uri.parse(link), - mode: LaunchMode.inAppBrowserView); + // TODO: Update link to redirect to password reset - December 2025 + String link = 'https://acms.ucsd.edu/students/accounts-and-passwords/index.html'; + await launchUrl(Uri.parse(link), mode: LaunchMode.inAppBrowserView); } catch (e) { // an error occurred, do nothing } @@ -244,8 +228,7 @@ class _OnboardingLoginState extends State { // Skip This Step GestureDetector( child: Semantics( - hint: - 'press to skip the login process and use this app as a visitor', + hint: 'press to skip the login process and use this app as a visitor', child: const Text( "SKIP THIS STEP", style: const TextStyle( @@ -259,8 +242,7 @@ class _OnboardingLoginState extends State { ), ), onTap: () async { - Navigator.pushNamedAndRemoveUntil( - context, RoutePaths.OnboardingInitial, (_) => false); + Navigator.pushNamedAndRemoveUntil(context, RoutePaths.ONBOARDING_INITIAL, (_) => false); }, ), @@ -269,20 +251,14 @@ class _OnboardingLoginState extends State { ), ); - // TODO: change the font for the password field (it's a lighter gray than what is currently there) - static Widget _buildInputField( - InputDecoration decoration, TextEditingController controller, + // TODO: change the font for the password field (it's a lighter gray than what is currently there) - December 2025 + static Widget _buildInputField(InputDecoration decoration, TextEditingController controller, {TextInputType? keyboardType, bool obscureText = false}) => Padding( padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Container( - decoration: - const BoxDecoration(color: Colors.white, boxShadow: const [ - const BoxShadow( - color: Colors.black26, - blurRadius: 5, - spreadRadius: 2.0, - offset: const Offset(2.0, 2.0)), + decoration: const BoxDecoration(color: Colors.white, boxShadow: const [ + const BoxShadow(color: Colors.black26, blurRadius: 5, spreadRadius: 2.0, offset: const Offset(2.0, 2.0)), ]), child: FractionallySizedBox( widthFactor: 0.74444444, @@ -290,8 +266,7 @@ class _OnboardingLoginState extends State { style: const TextStyle( fontFamily: 'Brix Sans', textBaseline: TextBaseline.alphabetic, - color: const Color( - 0xFF182B49), // Fixed color format with full opacity + color: const Color(0xFF182B49), // Fixed color format with full opacity fontWeight: FontWeight.w400, fontSize: 18.0, height: 1.277, // line height: 23px @@ -364,8 +339,8 @@ class _OnboardingLoginState extends State { builder: (BuildContext context) => AlertDialogWidget( type: MessageTypeConstants.ERROR, icon: Icons.error_outline, - title: LoginConstants.loginFailedTitle, - description: LoginConstants.loginFailedDesc, + title: LoginConstants.LOGIN_FAILED_TITLE, + description: LoginConstants.LOGIN_FAILED_DESC, onClose: () { Navigator.of(context).pop(); // Close the dialog }, diff --git a/lib/ui/onboarding/onboarding_slides.dart b/lib/ui/onboarding/onboarding_slides.dart index 4403d4219..b6ac11bf8 100644 --- a/lib/ui/onboarding/onboarding_slides.dart +++ b/lib/ui/onboarding/onboarding_slides.dart @@ -10,8 +10,7 @@ class OnboardingSlides extends StatefulWidget { _OnboardingSlidesState createState() => _OnboardingSlidesState(); } -class _OnboardingSlidesState extends State - with TickerProviderStateMixin { +class _OnboardingSlidesState extends State with TickerProviderStateMixin { var currentIndex = 0; final _foregroundPageController = PageController(); final _backgroundPageController = PageController(); @@ -22,8 +21,7 @@ class _OnboardingSlidesState extends State void initState() { super.initState(); _foregroundPageController.addListener(() { - _backgroundPageController.jumpTo( - _foregroundPageController.page! * MediaQuery.of(context).size.width); + _backgroundPageController.jumpTo(_foregroundPageController.page! * MediaQuery.of(context).size.width); }); } @@ -32,8 +30,7 @@ class _OnboardingSlidesState extends State dotsCount: 5, position: currentIndex.toDouble(), decorator: const DotsDecorator( - activeColor: const Color(0xFF00619B), - activeSize: const Size(16.0, 16.0) // Bigger active dot + activeColor: const Color(0xFF00619B), activeSize: const Size(16.0, 16.0) // Bigger active dot ), ); @@ -95,18 +92,15 @@ class _OnboardingSlidesState extends State Widget buildPage1() => OnboardingSlideTemplate( width: _screenWidth, height: _screenHeight, - heroImage: - const AssetImage('assets/images/onboarding/hero-1-campus-life.png'), + heroImage: const AssetImage('assets/images/onboarding/hero-1-campus-life.png'), heading: "ONE-STOP ACCESS\nTO CAMPUS LIFE. ", - description: - "Keep up to date with amazing events and stay connected to campus news.", + description: "Keep up to date with amazing events and stay connected to campus news.", ); Widget buildPage2() => OnboardingSlideTemplate( width: _screenWidth, height: _screenHeight, - heroImage: - const AssetImage('assets/images/onboarding/hero-2-schedule.png'), + heroImage: const AssetImage('assets/images/onboarding/hero-2-schedule.png'), heading: "YOUR SCHEDULE\nON THE GO. ", description: "View your classes and finals schedule whenever you need.", ); @@ -114,8 +108,7 @@ class _OnboardingSlidesState extends State Widget buildPage3() => OnboardingSlideTemplate( width: _screenWidth, height: _screenHeight, - heroImage: - const AssetImage('assets/images/onboarding/hero-3-parking.png'), + heroImage: const AssetImage('assets/images/onboarding/hero-3-parking.png'), heading: "PARKING\nMADE EASIER. ", description: "Keep an eye on parking lot capacity to plan your day.", ); @@ -123,21 +116,17 @@ class _OnboardingSlidesState extends State Widget buildPage4() => OnboardingSlideTemplate( width: _screenWidth, height: _screenHeight, - heroImage: - const AssetImage('assets/images/onboarding/hero-4-busyness.png'), + heroImage: const AssetImage('assets/images/onboarding/hero-4-busyness.png'), heading: "SPEND LESS\nTIME WAITING. ", - description: - "Easily see how busy campus locations are before you arrive.", + description: "Easily see how busy campus locations are before you arrive.", ); Widget buildPage5() => OnboardingSlideTemplate( width: _screenWidth, height: _screenHeight, - heroImage: const AssetImage( - 'assets/images/onboarding/hero-5-notifications.png'), + heroImage: const AssetImage('assets/images/onboarding/hero-5-notifications.png'), heading: "YOU'RE ALL SET. ", - description: - "We recommend turning on push notifications to receive campus and safety alerts.", + description: "We recommend turning on push notifications to receive campus and safety alerts.", ); Widget buildGoToTheAppButton() => GestureDetector( @@ -155,8 +144,7 @@ class _OnboardingSlidesState extends State ), ), onTap: () async { - Navigator.pushNamedAndRemoveUntil( - context, RoutePaths.BottomNavigationBar, (_) => false); + Navigator.pushNamedAndRemoveUntil(context, RoutePaths.BOTTOM_NAVIGATION_BAR, (_) => false); final prefs = await SharedPreferences.getInstance(); prefs.setBool('showOnboardingScreen', false); }, diff --git a/lib/ui/parking/circular_parking_indicator.dart b/lib/ui/parking/circular_parking_indicator.dart index 571107423..46a77401b 100644 --- a/lib/ui/parking/circular_parking_indicator.dart +++ b/lib/ui/parking/circular_parking_indicator.dart @@ -32,16 +32,14 @@ class CircularParkingIndicators extends StatelessWidget { List listOfCircularParkingInfo = []; List selectedSpots = []; - Provider.of(context) - .spotTypesState - .forEach((key, value) { - if (value && selectedSpots.length < 4) selectedSpots.add(key); + Provider.of(context).spotTypesState.forEach((key, value) { + final bool hasValue = value; + final bool hasSpaceForMore = selectedSpots.length < 4; + if (hasValue && hasSpaceForMore) selectedSpots.add(key); }); for (String spot in selectedSpots) { listOfCircularParkingInfo.add(buildCircularParkingInfo( - Provider.of(context).spotTypeMap[spot], - model.availability[spot], - context)); + Provider.of(context).spotTypeMap[spot], model.availability[spot], context)); } return Expanded( child: Row( @@ -51,8 +49,7 @@ class CircularParkingIndicators extends StatelessWidget { ); } - Widget buildCircularParkingInfo( - Spot? spotType, dynamic locationData, BuildContext context) { + Widget buildCircularParkingInfo(Spot? spotType, dynamic locationData, BuildContext context) { int open, total; if (locationData != null) { open = locationData["Open"] is String @@ -87,10 +84,7 @@ class CircularParkingIndicators extends StatelessWidget { lineWidth: 9, percent: (open / total).isNaN ? 0.0 : open / total, center: Text( - (open / total).isNaN - ? "N/A" - : ((open / total) * 100).round().toString() + - "%", + (open / total).isNaN ? "N/A" : ((open / total) * 100).round().toString() + "%", style: Theme.of(context).textTheme.titleMedium, ), circularStrokeCap: CircularStrokeCap.round, @@ -106,21 +100,15 @@ class CircularParkingIndicators extends StatelessWidget { padding: const EdgeInsets.all(8.0), child: spotType != null ? CircleAvatar( - backgroundColor: - colorFromHex(spotType.logoBackgroundColor), + backgroundColor: colorFromHex(spotType.logoBackgroundColor), child: spotType.logoText.startsWith('icon - ') - ? Icon( - ParkingConstants.stringToIconData[ - spotType.logoText] ?? - Icons.error, - size: 25.0, - color: colorFromHex(spotType.logoTextColor)) + ? Icon(ParkingConstants.STRING_TO_ICON_DATA[spotType.logoText] ?? Icons.error, + size: 25.0, color: colorFromHex(spotType.logoTextColor)) : (spotType.logoText.isNotEmpty ? Text( spotType.logoText, style: TextStyle( - color: colorFromHex( - spotType.logoTextColor), + color: colorFromHex(spotType.logoTextColor), fontFamily: 'Brix Sans', fontSize: 28, fontWeight: FontWeight.w700, @@ -165,21 +153,15 @@ class CircularParkingIndicators extends StatelessWidget { padding: const EdgeInsets.all(8.0), child: spotType != null ? CircleAvatar( - backgroundColor: - colorFromHex(spotType.logoBackgroundColor), + backgroundColor: colorFromHex(spotType.logoBackgroundColor), child: spotType.logoText.startsWith('icon - ') - ? Icon( - ParkingConstants.stringToIconData[ - spotType.logoText] ?? - Icons.error, - size: 25.0, - color: colorFromHex(spotType.logoTextColor)) + ? Icon(ParkingConstants.STRING_TO_ICON_DATA[spotType.logoText] ?? Icons.error, + size: 25.0, color: colorFromHex(spotType.logoTextColor)) : (spotType.logoText.isNotEmpty ? Text( spotType.logoText, style: TextStyle( - color: colorFromHex( - spotType.logoTextColor), + color: colorFromHex(spotType.logoTextColor), fontFamily: 'Brix Sans', fontWeight: FontWeight.w700, fontSize: 28, @@ -224,9 +206,7 @@ class CircularParkingIndicators extends StatelessWidget { child: Center( child: Text( "~" + - Provider.of(context) - .getApproxNumOfOpenSpots(model.locationName)["Open"] - .toString() + + Provider.of(context).getApproxNumOfOpenSpots(model.locationName)["Open"].toString() + " of " + Provider.of(context) .getApproxNumOfOpenSpots(model.locationName)["Total"] diff --git a/lib/ui/parking/manage_parking_view.dart b/lib/ui/parking/manage_parking_view.dart index d34f385b3..715d115a3 100644 --- a/lib/ui/parking/manage_parking_view.dart +++ b/lib/ui/parking/manage_parking_view.dart @@ -37,9 +37,9 @@ class _ManageParkingViewState extends State { ), onTap: () { if (i == 0) { - Navigator.pushNamed(context, RoutePaths.NeighborhoodsView); + Navigator.pushNamed(context, RoutePaths.NEIGHBORHOODS_VIEW); } else if (i == 1) { - Navigator.pushNamed(context, RoutePaths.ParkingStructureView); + Navigator.pushNamed(context, RoutePaths.PARKING_STRUCTURE_VIEW); } }, ), diff --git a/lib/ui/parking/neighborhood_lot_view.dart b/lib/ui/parking/neighborhood_lot_view.dart index 55a62010f..2c6d3f820 100644 --- a/lib/ui/parking/neighborhood_lot_view.dart +++ b/lib/ui/parking/neighborhood_lot_view.dart @@ -35,9 +35,7 @@ class _NeighborhoodLotsViewState extends State { padding: const EdgeInsets.fromLTRB(8, 0, 0, 0), child: Text( "Parking Lots", - style: Theme.of(context).brightness == Brightness.dark - ? textSubheaderDark - : textSubheaderLight, + style: Theme.of(context).brightness == Brightness.dark ? textSubheaderDark : textSubheaderLight, ), ), ), @@ -73,8 +71,8 @@ class _NeighborhoodLotsViewState extends State { return AlertDialogWidget( type: MessageTypeConstants.ERROR, icon: Icons.block_flipped, - title: ParkingConstants.lotMaxTitle, - description: ParkingConstants.lotMaxDesc, + title: ParkingConstants.LOT_MAX_TITLE, + description: ParkingConstants.LOT_MAX_DESC, onClose: () { Navigator.of(context).pop(); }, @@ -100,9 +98,7 @@ class _NeighborhoodLotsViewState extends State { children: ListTile.divideTiles( tiles: list, context: context, - color: Theme.of(context).brightness == Brightness.dark - ? listTileDividerColorDark - : listTileDividerColorLight, + color: Theme.of(context).brightness == Brightness.dark ? listTileDividerColorDark : listTileDividerColorLight, ).toList(), ), ); diff --git a/lib/ui/parking/neighborhoods_view.dart b/lib/ui/parking/neighborhoods_view.dart index 74db83b74..56c84e923 100644 --- a/lib/ui/parking/neighborhoods_view.dart +++ b/lib/ui/parking/neighborhoods_view.dart @@ -19,8 +19,7 @@ class _NeighborhoodsViewState extends State { } Widget buildNeighborhoodsList(BuildContext context) { - Map> neighborhoods = - Provider.of(context).getParkingMap(); + Map> neighborhoods = Provider.of(context).getParkingMap(); List listTiles = []; @@ -28,9 +27,7 @@ class _NeighborhoodsViewState extends State { ListTile( title: Text( "Neighborhoods", - style: Theme.of(context).brightness == Brightness.dark - ? textSubheaderDark - : textSubheaderLight, + style: Theme.of(context).brightness == Brightness.dark ? textSubheaderDark : textSubheaderLight, ), ), ); @@ -51,7 +48,7 @@ class _NeighborhoodsViewState extends State { onTap: () { Navigator.pushNamed( context, - RoutePaths.NeighborhoodsLotsView, + RoutePaths.NEIGHBORHOODS_LOTS_VIEW, arguments: value, ); }, @@ -68,9 +65,7 @@ class _NeighborhoodsViewState extends State { children: ListTile.divideTiles( tiles: listTiles, context: context, - color: Theme.of(context).brightness == Brightness.dark - ? listTileDividerColorDark - : listTileDividerColorLight, + color: Theme.of(context).brightness == Brightness.dark ? listTileDividerColorDark : listTileDividerColorLight, ).toList(), ), ); diff --git a/lib/ui/parking/parking_card.dart b/lib/ui/parking/parking_card.dart index 330f70b42..69ddd06ea 100644 --- a/lib/ui/parking/parking_card.dart +++ b/lib/ui/parking/parking_card.dart @@ -17,50 +17,45 @@ class ParkingCard extends StatefulWidget { } class _ParkingCardState extends State { - static const cardId = 'parking'; + static const CARD_ID = 'parking'; late ParkingDataProvider _parkingDataProvider; final _controller = PageController(viewportFraction: 0.92); int _currentPage = 0; - //if parking data provider changes (e.g in "Manage Spots"), this will be called. + // if parking data provider changes (e.g in "Manage Spots"), this will be called. @override void didChangeDependencies() { super.didChangeDependencies(); _parkingDataProvider = Provider.of(context); - if (_controller.hasClients) { - _controller.jumpToPage(_currentPage); - } + if (_controller.hasClients) _controller.jumpToPage(_currentPage); } @override Widget build(BuildContext context) { return CardContainer( - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[CARD_ID]!, isLoading: _parkingDataProvider.isLoading, reload: () => {_parkingDataProvider.fetchParkingData()}, errorText: _parkingDataProvider.error, child: () => buildParkingCard(context), - active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), + active: Provider.of(context).cardStates[CARD_ID], + hide: () => Provider.of(context, listen: false).toggleCard(CARD_ID), actionButtons: [ ActionButton( buttonText: 'MANAGE SPOTS', onPressed: () { - if (!_parkingDataProvider.isLoading && - _parkingDataProvider.error == null) { - Navigator.pushNamed(context, RoutePaths.SpotTypesView); - } + final bool isNotLoading = !_parkingDataProvider.isLoading; + final bool hasNoError = _parkingDataProvider.error == null; + if (isNotLoading && hasNoError) Navigator.pushNamed(context, RoutePaths.SPOT_TYPES_VIEW); }), ActionLink( buttonText: 'MANAGE LOTS', onPressed: () { - if (!_parkingDataProvider.isLoading && - _parkingDataProvider.error == null) { - Navigator.pushNamed(context, RoutePaths.ManageParkingView); - } + final bool isNotLoading = !_parkingDataProvider.isLoading; + final bool hasNoError = _parkingDataProvider.error == null; + if (isNotLoading && hasNoError) Navigator.pushNamed(context, RoutePaths.MANAGE_PARKING_VIEW); }), ], ); @@ -70,9 +65,8 @@ class _ParkingCardState extends State { try { List selectedLotsViews = []; for (ParkingModel model in _parkingDataProvider.parkingModels) { - if (_parkingDataProvider.parkingViewState[model.locationName] == true) { + if (_parkingDataProvider.parkingViewState[model.locationName] == true) selectedLotsViews.add(CircularParkingIndicators(model: model)); - } } if (selectedLotsViews.isEmpty) { @@ -117,9 +111,8 @@ class _ParkingCardState extends State { dotsCount: selectedLotsViews.length, decorator: DotsDecorator( color: dotsUnselectedColor, - activeColor: Theme.of(context).brightness == Brightness.dark - ? dotsSelectedColorDark - : dotsSelectedColorLight, + activeColor: + Theme.of(context).brightness == Brightness.dark ? dotsSelectedColorDark : dotsSelectedColorLight, activeSize: const Size(22.0, 22.0), size: const Size(10.0, 10.0), ), diff --git a/lib/ui/parking/parking_structure_view.dart b/lib/ui/parking/parking_structure_view.dart index a8a3f9479..be6ffd185 100644 --- a/lib/ui/parking/parking_structure_view.dart +++ b/lib/ui/parking/parking_structure_view.dart @@ -23,8 +23,7 @@ class _ParkingStructureViewState extends State { } Widget structureList(BuildContext context) { - List structures = - Provider.of(context).getStructures(); + List structures = Provider.of(context).getStructures(); List listTiles = []; listTiles.add( @@ -33,9 +32,7 @@ class _ParkingStructureViewState extends State { title: Padding( padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), child: Text("Parking Structures", - style: Theme.of(context).brightness == Brightness.dark - ? textSubheaderDark - : textSubheaderLight), + style: Theme.of(context).brightness == Brightness.dark ? textSubheaderDark : textSubheaderLight), ), ), ); @@ -46,8 +43,7 @@ class _ParkingStructureViewState extends State { }); for (var structureName in structures) { - bool structureState = - parkingDataProvider.parkingViewState[structureName]!; + bool structureState = parkingDataProvider.parkingViewState[structureName]!; listTiles.add( ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 16.0), @@ -72,8 +68,8 @@ class _ParkingStructureViewState extends State { return AlertDialogWidget( type: MessageTypeConstants.ERROR, icon: Icons.block_flipped, - title: ParkingConstants.lotMaxTitle, - description: ParkingConstants.lotMaxDesc, + title: ParkingConstants.LOT_MAX_TITLE, + description: ParkingConstants.LOT_MAX_DESC, onClose: () { Navigator.of(context).pop(); }, diff --git a/lib/ui/parking/spot_types_view.dart b/lib/ui/parking/spot_types_view.dart index d68001efd..8a32263c4 100644 --- a/lib/ui/parking/spot_types_view.dart +++ b/lib/ui/parking/spot_types_view.dart @@ -1,6 +1,6 @@ import 'package:campus_mobile_experimental/core/models/spot_types.dart'; import 'package:campus_mobile_experimental/core/providers/parking.dart'; -import 'package:campus_mobile_experimental/ui/common/HexColor.dart'; +import 'package:campus_mobile_experimental/ui/common/hex_color.dart'; import 'package:campus_mobile_experimental/ui/common/container_view.dart'; import 'package:campus_mobile_experimental/app_styles.dart'; import 'package:flutter/cupertino.dart'; @@ -46,17 +46,13 @@ class _SpotTypesViewState extends State { ); List createList(BuildContext context) { - var selectedSpots = Provider.of(context) - .spotTypesState - .values - .where((selected) => selected == true) - .length; + var selectedSpots = + Provider.of(context).spotTypesState.values.where((selected) => selected == true).length; List list = []; for (Spot data in spotTypesDataProvider.spotTypeModel!.spots!) { - var isSelected = Provider.of(context) - .spotTypesState[data.spotKey]!; + var isSelected = Provider.of(context).spotTypesState[data.spotKey]!; var iconColor = HexColor(data.logoBackgroundColor); var textColor = HexColor(data.logoTextColor); @@ -74,11 +70,8 @@ class _SpotTypesViewState extends State { child: Align( alignment: Alignment.center, child: data.logoText.startsWith('icon - ') - ? Icon( - ParkingConstants.stringToIconData[data.logoText] ?? - Icons.error, - size: 25.0, - color: textColor) + ? Icon(ParkingConstants.STRING_TO_ICON_DATA[data.logoText] ?? Icons.error, + size: 25.0, color: textColor) : (data.logoText.isNotEmpty ? Text( data.logoText, @@ -106,8 +99,8 @@ class _SpotTypesViewState extends State { return AlertDialogWidget( type: MessageTypeConstants.ERROR, icon: Icons.block_flipped, - title: ParkingConstants.spotMaxTitle, - description: ParkingConstants.spotMaxDesc, + title: ParkingConstants.SPOT_MAX_TITLE, + description: ParkingConstants.SPOT_MAX_DESC, onClose: () { Navigator.of(context).pop(); }, @@ -116,8 +109,7 @@ class _SpotTypesViewState extends State { ); return; } - spotTypesDataProvider.toggleSpotSelection( - data.spotKey, selectedSpots); + spotTypesDataProvider.toggleSpotSelection(data.spotKey, selectedSpots); }, activeTrackColor: toggleActiveColor, inactiveTrackColor: Colors.grey.shade400, diff --git a/lib/ui/profile/cards.dart b/lib/ui/profile/cards.dart index 9dfb28afc..197c36dea 100644 --- a/lib/ui/profile/cards.dart +++ b/lib/ui/profile/cards.dart @@ -33,8 +33,7 @@ class _CardsViewState extends State { header: Padding( padding: const EdgeInsets.only(top: 10), child: Text("Hold and drag to reorder", - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall), + textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodySmall), ), children: createList(), onReorder: (int oldIndex, int newIndex) { @@ -80,9 +79,7 @@ class _CardsViewState extends State { margin: EdgeInsets.fromLTRB(cardMargin, 5, cardMargin, 5), child: ListTile( leading: Icon(Icons.drag_handle, - color: Theme.of(context).brightness == Brightness.dark - ? linkTextColorDark - : linkTextColorLight), + color: Theme.of(context).brightness == Brightness.dark ? linkTextColorDark : linkTextColorLight), title: Text(_cardsDataProvider.availableCards[card]!.titleText, style: Theme.of(context).textTheme.bodyMedium), trailing: Transform.scale( @@ -92,12 +89,10 @@ class _CardsViewState extends State { onChanged: (_) { _cardsDataProvider.toggleCard(card); }, - activeColor: - toggleActiveColor, // Ensure this is a solid color + activeColor: toggleActiveColor, // Ensure this is a solid color thumbColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - return Colors.white; - } + final bool isSelected = states.contains(WidgetState.selected); + if (isSelected) return Colors.white; return null; }), ), @@ -107,8 +102,7 @@ class _CardsViewState extends State { ); } catch (e) { FirebaseCrashlytics.instance.log('error getting $card in profile'); - FirebaseCrashlytics.instance.recordError( - e, StackTrace.fromString(e.toString()), + FirebaseCrashlytics.instance.recordError(e, StackTrace.fromString(e.toString()), reason: "Profile/Cards: Failed to load Cards page", fatal: false); _cardsDataProvider.changeInternetStatus(true); diff --git a/lib/ui/profile/login.dart b/lib/ui/profile/login.dart index 58dc68fc0..9ae09ccf6 100644 --- a/lib/ui/profile/login.dart +++ b/lib/ui/profile/login.dart @@ -40,25 +40,19 @@ class _LoginState extends State { return Container( constraints: BoxConstraints(maxWidth: 100, maxHeight: 100), - child: Center( - child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.secondary))); + child: Center(child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary))); } Widget buildLoggedInWidget(BuildContext context) { return Container( padding: const EdgeInsets.fromLTRB(15.0, 15.0, 0, 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'LOGGED IN AS:', - style: Theme.of(context).brightness == Brightness.dark - ? titleMediumDark - : titleMediumLight, - ), - buildUserProfileTile(context), - ]), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text( + 'LOGGED IN AS:', + style: Theme.of(context).brightness == Brightness.dark ? titleMediumDark : titleMediumLight, + ), + buildUserProfileTile(context), + ]), ); } @@ -125,10 +119,7 @@ class _LoginState extends State { ), SizedBox(height: 10), TextField( - style: TextStyle( - fontFamily: 'Brix Sans', - fontWeight: FontWeight.w400, - color: const Color(0xFF737373)), + style: TextStyle(fontFamily: 'Brix Sans', fontWeight: FontWeight.w400, color: const Color(0xFF737373)), decoration: InputDecoration( hintText: 'UCSD Email', hintStyle: TextStyle( @@ -136,8 +127,7 @@ class _LoginState extends State { ), border: OutlineInputBorder(), focusedBorder: new OutlineInputBorder( - borderSide: new BorderSide( - color: Theme.of(context).colorScheme.secondary), + borderSide: new BorderSide(color: Theme.of(context).colorScheme.secondary), ), labelText: 'UCSD Email', labelStyle: TextStyle( @@ -149,10 +139,7 @@ class _LoginState extends State { ), SizedBox(height: 10), TextField( - style: TextStyle( - fontFamily: 'Brix Sans', - fontWeight: FontWeight.w400, - color: const Color(0xFF737373)), + style: TextStyle(fontFamily: 'Brix Sans', fontWeight: FontWeight.w400, color: const Color(0xFF737373)), decoration: InputDecoration( hintText: 'Password', hintStyle: TextStyle( @@ -163,15 +150,14 @@ class _LoginState extends State { // Based on passwordObscured state choose the icon _passwordObscured ? Icons.visibility_off : Icons.visibility, - /// TODO: Change color to improve its visibility in dark theme. + // TODO: Change color to improve its visibility in dark theme. - December 2025 color: Theme.of(context).iconTheme.color, ), onPressed: () => _toggle(), ), border: OutlineInputBorder(), focusedBorder: new OutlineInputBorder( - borderSide: new BorderSide( - color: Theme.of(context).colorScheme.secondary), + borderSide: new BorderSide(color: Theme.of(context).colorScheme.secondary), ), labelText: 'Password', labelStyle: TextStyle( @@ -203,8 +189,7 @@ class _LoginState extends State { ? null : () { _userDataProvider - .manualLogin(_emailTextFieldController.text, - _passwordTextFieldController.text) + .manualLogin(_emailTextFieldController.text, _passwordTextFieldController.text) .then((isLoggedIn) { if (!isLoggedIn) { showDialog( @@ -213,9 +198,8 @@ class _LoginState extends State { return AlertDialogWidget( type: MessageTypeConstants.ERROR, icon: Icons.block_flipped, - title: LoginConstants.loginFailedTitle, - description: - LoginConstants.loginFailedDesc, + title: LoginConstants.LOGIN_FAILED_TITLE, + description: LoginConstants.LOGIN_FAILED_DESC, onClose: () { Navigator.of(context).pop(); }, @@ -243,8 +227,7 @@ class _LoginState extends State { ), onTap: () async { try { - String link = - 'https://acms.ucsd.edu/students/accounts-and-passwords/index.html'; + String link = 'https://acms.ucsd.edu/students/accounts-and-passwords/index.html'; await launch(link, forceSafariVC: true); } catch (e) { // an error occurred, do nothing @@ -281,35 +264,25 @@ class _LoginState extends State { children: [ Expanded( child: Icon(Icons.info_outline, - color: Theme.of(context).brightness == Brightness.dark - ? linkTextColorDark - : linkTextColorLight), + color: Theme.of(context).brightness == Brightness.dark ? linkTextColorDark : linkTextColorLight), flex: 1, ), Expanded( child: Text( - LoginConstants.loginFailedTitle, + LoginConstants.LOGIN_FAILED_TITLE, textAlign: TextAlign.left, style: Theme.of(context).brightness == Brightness.dark ? TextStyle( - color: linkTextColorDark, - fontFamily: 'Brix Sans', - fontWeight: FontWeight.w700, - fontSize: 18.0) + color: linkTextColorDark, fontFamily: 'Brix Sans', fontWeight: FontWeight.w700, fontSize: 18.0) : TextStyle( - color: linkTextColorLight, - fontFamily: 'Brix Sans', - fontWeight: FontWeight.w700, - fontSize: 18.0), + color: linkTextColorLight, fontFamily: 'Brix Sans', fontWeight: FontWeight.w700, fontSize: 18.0), ), flex: 7, ), Expanded( child: IconButton( icon: Icon(Icons.close, - color: Theme.of(context).brightness == Brightness.dark - ? linkTextColorDark - : linkTextColorLight), + color: Theme.of(context).brightness == Brightness.dark ? linkTextColorDark : linkTextColorLight), alignment: Alignment.topRight, onPressed: () { Navigator.of(context).pop(); @@ -321,8 +294,7 @@ class _LoginState extends State { ), content: Container( constraints: BoxConstraints( - maxHeight: MediaQuery.of(context).size.height * - 0.6, // Set max height to 60% of screen height + maxHeight: MediaQuery.of(context).size.height * 0.6, // Set max height to 60% of screen height ), child: SingleChildScrollView( child: Row( @@ -338,7 +310,7 @@ class _LoginState extends State { mainAxisSize: MainAxisSize.min, children: [ Text( - LoginConstants.loginFailedDesc, + LoginConstants.LOGIN_FAILED_DESC, textAlign: TextAlign.left, style: TextStyle( color: linkTextColorLight, diff --git a/lib/ui/profile/profile.dart b/lib/ui/profile/profile.dart index 561bd14a6..6dfdc81c4 100644 --- a/lib/ui/profile/profile.dart +++ b/lib/ui/profile/profile.dart @@ -21,27 +21,25 @@ class Profile extends StatelessWidget { Uri? initialUri = await appLinks.getInitialAppLink(); String? initialLink = initialUri?.toString(); - if (initialLink != null && initialLink.contains("deeplinking.searchmap")) { + final bool hasInitialLink = initialLink != null; + final bool isSearchMapLink = hasInitialLink && initialLink.contains("deeplinking.searchmap"); + if (hasInitialLink && isSearchMapLink) { var uri = Uri.dataFromString(initialLink); var query = uri.queryParameters['query']!; - Provider.of(context, listen: false) - .searchBarController - .text = query; + Provider.of(context, listen: false).searchBarController.text = query; Provider.of(context, listen: false).fetchLocations(); - Provider.of(context, listen: false) - .currentIndex = NavigatorConstants.MapTab; + Provider.of(context, listen: false).currentIndex = NavigatorConstants.MAP_TAB; } _sub = appLinks.uriLinkStream.listen((Uri? uri) async { String? link = uri?.toString(); - if (link != null && link.contains("deeplinking.searchmap")) { + final bool hasLink = link != null; + final bool isSearchMapLink = hasLink && link.contains("deeplinking.searchmap"); + if (hasLink && isSearchMapLink) { var query = uri!.queryParameters['query']!; - Provider.of(context, listen: false) - .searchBarController - .text = query; + Provider.of(context, listen: false).searchBarController.text = query; Provider.of(context, listen: false).fetchLocations(); - Provider.of(context, listen: false) - .currentIndex = NavigatorConstants.MapTab; + Provider.of(context, listen: false).currentIndex = NavigatorConstants.MAP_TAB; _sub?.cancel(); } }); @@ -70,22 +68,17 @@ class Profile extends StatelessWidget { ListTile( title: Text( 'SETTINGS & SUPPORT', - style: Theme.of(context).brightness == Brightness.dark - ? titleMediumDark - : titleMediumLight, + style: Theme.of(context).brightness == Brightness.dark ? titleMediumDark : titleMediumLight, ), ), ListTile( - leading: Icon(Icons.drag_handle, - color: Theme.of(context).iconTheme.color, size: 30.0), + leading: Icon(Icons.drag_handle, color: Theme.of(context).iconTheme.color, size: 30.0), title: Text( 'Card Settings', - style: Theme.of(context).brightness == Brightness.dark - ? linkTextDark - : linkTextLight, + style: Theme.of(context).brightness == Brightness.dark ? linkTextDark : linkTextLight, ), onTap: () { - Navigator.pushNamed(context, RoutePaths.CardsView); + Navigator.pushNamed(context, RoutePaths.CARDS_VIEW); }, ), ListTile( @@ -93,15 +86,12 @@ class Profile extends StatelessWidget { decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( - color: Theme.of(context).brightness == Brightness.dark - ? darkPrimaryColor - : lightPrimaryColor, + color: Theme.of(context).brightness == Brightness.dark ? darkPrimaryColor : lightPrimaryColor, width: 3.0, // Set the border width ), ), child: Padding( - padding: const EdgeInsets.all( - 1.0), // Adjust the padding as needed + padding: const EdgeInsets.all(1.0), // Adjust the padding as needed child: Icon( Icons.question_mark, color: Theme.of(context).iconTheme.color, @@ -110,32 +100,24 @@ class Profile extends StatelessWidget { ), title: Text( 'Get Mobile App Support', - style: Theme.of(context).brightness == Brightness.dark - ? linkTextDark - : linkTextLight, + style: Theme.of(context).brightness == Brightness.dark ? linkTextDark : linkTextLight, ), onTap: handleFeedbackTap, ), ListTile( - leading: Icon(Icons.lock, - color: Theme.of(context).iconTheme.color, size: 30.0), + leading: Icon(Icons.lock, color: Theme.of(context).iconTheme.color, size: 30.0), title: Text( 'View Privacy Policy', - style: Theme.of(context).brightness == Brightness.dark - ? linkTextDark - : linkTextLight, + style: Theme.of(context).brightness == Brightness.dark ? linkTextDark : linkTextLight, ), onTap: handlePrivacyTap, ), if (isLoggedIn) ListTile( - leading: Icon(Icons.warning_amber_rounded, - color: Theme.of(context).iconTheme.color, size: 36.0), + leading: Icon(Icons.warning_amber_rounded, color: Theme.of(context).iconTheme.color, size: 36.0), title: Text( 'Report a Campus Facility Issue', - style: Theme.of(context).brightness == Brightness.dark - ? linkTextDark - : linkTextLight, + style: Theme.of(context).brightness == Brightness.dark ? linkTextDark : linkTextLight, ), onTap: handleReportTap, ), @@ -149,7 +131,7 @@ class Profile extends StatelessWidget { } // handleNotificationsTap(BuildContext context) { - // Navigator.pushNamed(context, RoutePaths.NotificationsFilter); + // Navigator.pushNamed(context, RoutePaths.NOTIFICATIONSFilter); // } Future handleFeedbackTap() async { @@ -163,8 +145,7 @@ class Profile extends StatelessWidget { } Future handleReportTap() async { - const reportUrl = - "https://experience.arcgis.com/experience/91b8f66d6fa547f481c2a1cb6af252d0"; + const reportUrl = "https://experience.arcgis.com/experience/91b8f66d6fa547f481c2a1cb6af252d0"; openLink(reportUrl); } } diff --git a/lib/ui/shuttle/add_shuttle_stops_view.dart b/lib/ui/shuttle/add_shuttle_stops_view.dart index 3c407c235..cbf44ccb8 100644 --- a/lib/ui/shuttle/add_shuttle_stops_view.dart +++ b/lib/ui/shuttle/add_shuttle_stops_view.dart @@ -30,8 +30,7 @@ class _AddShuttleStopsViewState extends State { child: Container( height: 32, width: 32, - child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.secondary)), + child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary)), ), )), ]); @@ -44,8 +43,7 @@ class _AddShuttleStopsViewState extends State { } } - Widget buildAllLocationsList(BuildContext context) => - ListView(children: createList(context)); + Widget buildAllLocationsList(BuildContext context) => ListView(children: createList(context)); List createList(BuildContext context) { List list = []; diff --git a/lib/ui/shuttle/manage_shuttle_view.dart b/lib/ui/shuttle/manage_shuttle_view.dart index 64dec6b16..83fcfdfa7 100644 --- a/lib/ui/shuttle/manage_shuttle_view.dart +++ b/lib/ui/shuttle/manage_shuttle_view.dart @@ -35,8 +35,7 @@ class _ManageShuttleViewState extends State { header: Padding( padding: const EdgeInsets.only(top: 10), child: Text("Hold and drag to reorder", - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall), + textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodySmall), ), children: createList(context), onReorder: _onReorder, @@ -69,12 +68,9 @@ class _ManageShuttleViewState extends State { elevation: 2.0, margin: EdgeInsets.fromLTRB(cardMargin, 5, cardMargin, 5), child: ListTile( - title: Text(model.name, - style: Theme.of(context).textTheme.bodyMedium), + title: Text(model.name, style: Theme.of(context).textTheme.bodyMedium), leading: Icon(Icons.drag_handle, - color: Theme.of(context).brightness == Brightness.dark - ? linkTextColorDark - : linkTextColorLight), + color: Theme.of(context).brightness == Brightness.dark ? linkTextColorDark : linkTextColorLight), trailing: IconButton( icon: Icon(Icons.close), onPressed: () async { @@ -97,7 +93,7 @@ class _ManageShuttleViewState extends State { buttonText: 'ADD MORE STOPS', onPressed: () { if (_shuttleDataProvider.stopsToRender.length < 5) { - Navigator.pushNamed(context, RoutePaths.AddShuttleStopsView); + Navigator.pushNamed(context, RoutePaths.ADD_SHUTTLE_STOPS_VIEW); } else { showDialog( context: context, @@ -105,8 +101,8 @@ class _ManageShuttleViewState extends State { return AlertDialogWidget( type: MessageTypeConstants.ERROR, icon: Icons.block_flipped, - title: LoginConstants.shuttleMaxTitle, - description: LoginConstants.shuttleMaxDesc, + title: LoginConstants.SHUTTLE_MAX_TITLE, + description: LoginConstants.SHUTTLE_MAX_DESC, onClose: () { Navigator.of(context).pop(); }, @@ -130,7 +126,7 @@ class _ManageShuttleViewState extends State { // backgroundColor: ColorPrimary, // onPressed: () { // if (_shuttleDataProvider.stopsToRender.length < 5) { - // Navigator.pushNamed(context, RoutePaths.AddShuttleStopsView); + // Navigator.pushNamed(context, RoutePaths.ADD_SHUTTLE_STOPS_VIEW); // } else { // showAlertDialog(context); // } @@ -162,7 +158,7 @@ class _ManageShuttleViewState extends State { ), Expanded( child: Text( - LoginConstants.shuttleMaxTitle, + LoginConstants.SHUTTLE_MAX_TITLE, textAlign: TextAlign.left, style: Theme.of(context).brightness == Brightness.dark ? TextStyle( @@ -195,8 +191,7 @@ class _ManageShuttleViewState extends State { ), content: Container( constraints: BoxConstraints( - maxHeight: MediaQuery.of(context).size.height * - 0.6, // Set max height to 60% of screen height + maxHeight: MediaQuery.of(context).size.height * 0.6, // Set max height to 60% of screen height ), child: SingleChildScrollView( child: Row( @@ -212,7 +207,7 @@ class _ManageShuttleViewState extends State { mainAxisSize: MainAxisSize.min, children: [ Text( - LoginConstants.shuttleMaxDesc, + LoginConstants.SHUTTLE_MAX_DESC, textAlign: TextAlign.left, style: Theme.of(context).brightness == Brightness.dark ? TextStyle( diff --git a/lib/ui/shuttle/shuttle_card.dart b/lib/ui/shuttle/shuttle_card.dart index 8cc48531b..ea2a5fe45 100644 --- a/lib/ui/shuttle/shuttle_card.dart +++ b/lib/ui/shuttle/shuttle_card.dart @@ -38,20 +38,17 @@ class _ShuttleCardState extends State { Widget build(BuildContext context) { return CardContainer( active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), + hide: () => Provider.of(context, listen: false).toggleCard(cardId), reload: () { setState(() { _currentPage = 0; }); - Provider.of(context, listen: false) - .fetchStops(true); + Provider.of(context, listen: false).fetchStops(true); }, isLoading: _shuttleCardDataProvider.isLoading, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[cardId]!, errorText: _shuttleCardDataProvider.error, - child: () => buildShuttleCard(_shuttleCardDataProvider.stopsToRender, - _shuttleCardDataProvider.arrivalsToRender), + child: () => buildShuttleCard(_shuttleCardDataProvider.stopsToRender, _shuttleCardDataProvider.arrivalsToRender), actionButtons: [ ActionLink( buttonText: 'MANAGE SHUTTLE STOPS', @@ -59,14 +56,13 @@ class _ShuttleCardState extends State { setState(() { _currentPage = 0; }); - Navigator.pushNamed(context, RoutePaths.ManageShuttleView); + Navigator.pushNamed(context, RoutePaths.MANAGE_SHUTTLE_VIEW); }), ], ); } - Widget buildShuttleCard(List stopsToRender, - Map> arrivalsToRender) { + Widget buildShuttleCard(List stopsToRender, Map> arrivalsToRender) { List renderList = []; try { // Initialize first shuttle display with arrival information @@ -77,7 +73,7 @@ class _ShuttleCardState extends State { ); } - // TODO: Reuse if you want to show the closest stop, let's say in the "Manage Shuttle Stops" screen, delete if not needed + // TODO: Reuse if you want to show the closest stop, let's say in the "Manage Shuttle Stops" screen, delete if not needed - December 2025 // if (_shuttleCardDataProvider.closestStop != null) { // renderList.add(ShuttleDisplay( // stop: _shuttleCardDataProvider.closestStop!, @@ -89,8 +85,7 @@ class _ShuttleCardState extends State { for (var i = 0; i < _shuttleCardDataProvider.stopsToRender.length; i++) { renderList.add(ShuttleDisplay( stop: _shuttleCardDataProvider.stopsToRender[i], - arrivingShuttles: arrivalsToRender[ - _shuttleCardDataProvider.stopsToRender[i].id])); + arrivingShuttles: arrivalsToRender[_shuttleCardDataProvider.stopsToRender[i].id])); } return Column( children: [ @@ -110,9 +105,8 @@ class _ShuttleCardState extends State { dotsCount: renderList.length, decorator: DotsDecorator( color: dotsUnselectedColor, - activeColor: Theme.of(context).brightness == Brightness.dark - ? dotsSelectedColorDark - : dotsSelectedColorLight, + activeColor: + Theme.of(context).brightness == Brightness.dark ? dotsSelectedColorDark : dotsSelectedColorLight, activeSize: const Size(22.0, 22.0), size: const Size(10.0, 10.0), ), @@ -140,8 +134,8 @@ class _ShuttleCardState extends State { 'Manage Shuttle Stops', ), onPressed: () { - if (!_shuttleCardDataProvider.isLoading) - Navigator.pushNamed(context, RoutePaths.ManageShuttleView); + final bool isNotLoading = !_shuttleCardDataProvider.isLoading; + if (isNotLoading) Navigator.pushNamed(context, RoutePaths.MANAGE_SHUTTLE_VIEW); }, )); return actionButtons; diff --git a/lib/ui/shuttle/shuttle_display.dart b/lib/ui/shuttle/shuttle_display.dart index fb42ea060..4bed17986 100644 --- a/lib/ui/shuttle/shuttle_display.dart +++ b/lib/ui/shuttle/shuttle_display.dart @@ -5,8 +5,7 @@ import 'package:flutter/material.dart'; import 'package:campus_mobile_experimental/app_styles.dart'; class ShuttleDisplay extends StatelessWidget { - ShuttleDisplay({Key? key, required this.stop, required this.arrivingShuttles}) - : super(key: key); + ShuttleDisplay({Key? key, required this.stop, required this.arrivingShuttles}) : super(key: key); /// STATES final List? arrivingShuttles; @@ -22,10 +21,7 @@ class ShuttleDisplay extends StatelessWidget { height: 200.0, child: Center( child: Container( - height: 32, - width: 32, - child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.secondary)), + height: 32, width: 32, child: CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary)), ), ); } else { @@ -62,23 +58,18 @@ class ShuttleDisplay extends StatelessWidget { ), child: CircleAvatar( minRadius: 40, - backgroundColor: HexColor(arrivingShuttles!.isEmpty - ? noArrivalsFoundColor - : arrivingShuttles![0].routeColor), + backgroundColor: + HexColor(arrivingShuttles!.isEmpty ? noArrivalsFoundColor : arrivingShuttles![0].routeColor), foregroundColor: Colors.black, child: Builder( builder: (context) { - Color circleColor = HexColor(arrivingShuttles!.isEmpty - ? noArrivalsFoundColor - : arrivingShuttles![0].routeColor); + Color circleColor = + HexColor(arrivingShuttles!.isEmpty ? noArrivalsFoundColor : arrivingShuttles![0].routeColor); // Calculate luminance to determine the color of "?" final double luminance = circleColor.computeLuminance(); - final Color textColor = - luminance > 0.5 ? Colors.black : Colors.white; + final Color textColor = luminance > 0.5 ? Colors.black : Colors.white; return Text( - arrivingShuttles!.isEmpty - ? "?" - : arrivingShuttles![0].routeName[0], + arrivingShuttles!.isEmpty ? "?" : arrivingShuttles![0].routeName[0], style: TextStyle( fontSize: 50, color: textColor, @@ -89,18 +80,13 @@ class ShuttleDisplay extends StatelessWidget { ), ), SizedBox(width: 16), - Text("@", - style: Theme.of(context).brightness == Brightness.light - ? titleMediumLight - : titleMediumDark), + Text("@", style: Theme.of(context).brightness == Brightness.light ? titleMediumLight : titleMediumDark), SizedBox(width: 8), Expanded( child: Text( stop.name, textAlign: TextAlign.start, - style: Theme.of(context).brightness == Brightness.light - ? titleMediumLight - : titleMediumDark, + style: Theme.of(context).brightness == Brightness.light ? titleMediumLight : titleMediumDark, overflow: TextOverflow.visible, // optional, default wraps softWrap: true, // optional, default true ), @@ -177,9 +163,7 @@ class ShuttleDisplay extends StatelessWidget { var minutesToArrival = arrivingShuttles![0].secondsToArrival ~/ 60; return Text( "$minutesToArrival minutes", - style: Theme.of(context).brightness == Brightness.light - ? titleMediumLight - : titleMediumDark, + style: Theme.of(context).brightness == Brightness.light ? titleMediumLight : titleMediumDark, ); } @@ -194,9 +178,7 @@ class ShuttleDisplay extends StatelessWidget { child: Text( "Next Arrivals", textAlign: TextAlign.left, - style: Theme.of(context).brightness == Brightness.light - ? titleMediumLight - : titleMediumDark, + style: Theme.of(context).brightness == Brightness.light ? titleMediumLight : titleMediumDark, ), ), ], @@ -208,9 +190,7 @@ class ShuttleDisplay extends StatelessWidget { int count = (arrivingShuttles!.length - 1).clamp(0, 2); for (var index = 1; index <= count; index++) { arrivalsToRender.add(buildArrivalTime(context, arrivingShuttles![index])); - if (index != count) { - arrivalsToRender.add(Divider()); - } + if (index != count) arrivalsToRender.add(Divider()); } return Column(children: arrivalsToRender); } @@ -246,9 +226,7 @@ class ShuttleDisplay extends StatelessWidget { padding: const EdgeInsets.all(8.0), child: Text( "$minutesToArrival min", - style: Theme.of(context).brightness == Brightness.light - ? titleMediumLight - : titleMediumDark, + style: Theme.of(context).brightness == Brightness.light ? titleMediumLight : titleMediumDark, ), ), ], diff --git a/lib/ui/student_id/student_id_card.dart b/lib/ui/student_id/student_id_card.dart index 0277da26f..01ecb27af 100644 --- a/lib/ui/student_id/student_id_card.dart +++ b/lib/ui/student_id/student_id_card.dart @@ -17,7 +17,7 @@ class StudentIdCard extends StatefulWidget { } class _StudentIdCardState extends State { - static const cardId = "student_id"; + static const CARD_ID = "student_id"; /// Pop up barcode createAlertDialog( @@ -60,9 +60,8 @@ class _StudentIdCardState extends State { String cardNumber, bool rotated, ) { - if (MediaQuery.of(context).orientation == Orientation.landscape) { + if (MediaQuery.of(context).orientation == Orientation.landscape) return returnBarcodeContainer(cardNumber, rotated, context); - } return image; } @@ -71,13 +70,11 @@ class _StudentIdCardState extends State { ScalingUtility().getCurrentMeasurements(context); return CardContainer( - active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), - reload: () => Provider.of(context, listen: false) - .fetchData(), + active: Provider.of(context).cardStates[CARD_ID], + hide: () => Provider.of(context, listen: false).toggleCard(CARD_ID), + reload: () => Provider.of(context, listen: false).fetchData(), isLoading: Provider.of(context).isLoading, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[CARD_ID]!, errorText: Provider.of(context).error, child: () => buildCardContent( Provider.of(context).studentIdNameModel, @@ -218,8 +215,7 @@ class _StudentIdCardState extends State { child: Row( children: [ Padding( - padding: - EdgeInsets.all(ScalingUtility.verticalSafeBlock * 7.5), + padding: EdgeInsets.all(ScalingUtility.verticalSafeBlock * 7.5), ), Column( mainAxisAlignment: MainAxisAlignment.center, @@ -267,14 +263,14 @@ class _StudentIdCardState extends State { } double letterSpacingForTablet() { - if (MediaQuery.of(context).orientation == Orientation.landscape) - return ScalingUtility.horizontalSafeBlock * 1; + final bool isLandscape = MediaQuery.of(context).orientation == Orientation.landscape; + if (isLandscape) return ScalingUtility.horizontalSafeBlock * 1; return ScalingUtility.horizontalSafeBlock * 3; } double fontSizeForTablet() { - if (MediaQuery.of(context).orientation == Orientation.landscape) - return ScalingUtility.horizontalSafeBlock * 2; + final bool isLandscape = MediaQuery.of(context).orientation == Orientation.landscape; + if (isLandscape) return ScalingUtility.horizontalSafeBlock * 2; return ScalingUtility.horizontalSafeBlock * 4; } @@ -378,15 +374,13 @@ class _StudentIdCardState extends State { // } // } - double letterSpacing() => - MediaQuery.of(context).orientation == Orientation.landscape - ? SizeConfig.safeBlockHorizontal * 1 - : SizeConfig.safeBlockHorizontal * 3; + double letterSpacing() => MediaQuery.of(context).orientation == Orientation.landscape + ? SizeConfig.safeBlockHorizontal * 1 + : SizeConfig.safeBlockHorizontal * 3; - double getRotatedPopUpFontSize() => - MediaQuery.of(context).orientation == Orientation.landscape - ? SizeConfig.safeBlockHorizontal * 2 - : SizeConfig.safeBlockHorizontal * 4; + double getRotatedPopUpFontSize() => MediaQuery.of(context).orientation == Orientation.landscape + ? SizeConfig.safeBlockHorizontal * 2 + : SizeConfig.safeBlockHorizontal * 4; /// Determine the font size for user's textFields double getFontSize(String input, String textField) { @@ -418,15 +412,11 @@ class _StudentIdCardState extends State { } /// Determine the padding for a border around barcode - EdgeInsets addBorder(ThemeData currentTheme) { - return currentTheme.brightness == Brightness.dark - ? EdgeInsets.all(5) - : EdgeInsets.all(0); - } + EdgeInsets addBorder(ThemeData currentTheme) => + currentTheme.brightness == Brightness.dark ? EdgeInsets.all(5) : EdgeInsets.all(0); /// Determine the padding for the text to realign - double realignText(ThemeData currentTheme) => - currentTheme.brightness == Brightness.dark ? 7 : 0; + double realignText(ThemeData currentTheme) => currentTheme.brightness == Brightness.dark ? 7 : 0; // /// Determine the color of hint above the barcode // Color decideColor(ThemeData currentTheme) { @@ -525,7 +515,7 @@ class _StudentIdCardState extends State { Widget _buildBarcodeNumber(StudentIdProfileModel profileModel) { return Padding( padding: const EdgeInsets.only(top: 6.0), - //child: Center( + // child: Center( child: Text( profileModel.barcode.toString(), style: TextStyle( @@ -538,7 +528,7 @@ class _StudentIdCardState extends State { } } -//Image Scaling +// Image Scaling class ScalingUtility { late MediaQueryData _queryData; static late double horizontalSafeBlock; @@ -549,12 +539,8 @@ class ScalingUtility { _queryData = MediaQuery.of(context); /// Calculate blocks accounting for notches and home bar - horizontalSafeBlock = (_queryData.size.width - - (_queryData.padding.left + _queryData.padding.right)) / - 100; - verticalSafeBlock = (_queryData.size.height - - (_queryData.padding.top + _queryData.padding.bottom)) / - 100; + horizontalSafeBlock = (_queryData.size.width - (_queryData.padding.left + _queryData.padding.right)) / 100; + verticalSafeBlock = (_queryData.size.height - (_queryData.padding.top + _queryData.padding.bottom)) / 100; } } @@ -577,10 +563,8 @@ class SizeConfig { blockSizeHorizontal = screenWidth / 100; blockSizeVertical = screenHeight / 100; - _safeAreaHorizontal = - _mediaQueryData.padding.left + _mediaQueryData.padding.right; - _safeAreaVertical = - _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; + _safeAreaHorizontal = _mediaQueryData.padding.left + _mediaQueryData.padding.right; + _safeAreaVertical = _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; safeBlockHorizontal = (screenWidth - _safeAreaHorizontal) / 100; safeBlockVertical = (screenHeight - _safeAreaVertical) / 100; } diff --git a/lib/ui/wifi/wifi_card.dart b/lib/ui/wifi/wifi_card.dart index 2b235effd..9a3e3e65d 100644 --- a/lib/ui/wifi/wifi_card.dart +++ b/lib/ui/wifi/wifi_card.dart @@ -18,8 +18,7 @@ class WiFiCard extends StatefulWidget { enum TestStatus { initial, running, finished, unavailable, simulated } -class _WiFiCardState extends State - with AutomaticKeepAliveClientMixin { +class _WiFiCardState extends State with AutomaticKeepAliveClientMixin { /// STATES String cardId = "speed_test"; bool _buttonEnabled = true; @@ -50,13 +49,12 @@ class _WiFiCardState extends State super.build(context); return CardContainer( active: Provider.of(context).cardStates[cardId], - hide: () => Provider.of(context, listen: false) - .toggleCard(cardId), + hide: () => Provider.of(context, listen: false).toggleCard(cardId), reload: () => cardState != TestStatus.running ? Provider.of(context, listen: false).init() : print("running test..."), isLoading: _speedTestProvider.isLoading!, - titleText: CardTitleConstants.titleMap[cardId]!, + titleText: CardTitleConstants.TITLE_MAP[cardId]!, errorText: _speedTestProvider.error, child: () => buildCardContent(context), ); @@ -86,21 +84,23 @@ class _WiFiCardState extends State }); } else if (!_speedTestProvider.isUCSDWiFi!) { setState(() => cardState = TestStatus.unavailable); - } else if (_speedTestProvider.timeElapsedDownload + - _speedTestProvider.timeElapsedUpload > - SPEED_TEST_TIMEOUT_CONST) { - setState(() { - goodSpeed = false; - cardState = TestStatus.finished; - timedOut = true; - }); - _speedTestProvider.cancelDownload(); - _speedTestProvider.cancelUpload(); - } else if (_speedTestProvider.speedTestDone) { - setState(() { - goodSpeed = true; - cardState = TestStatus.finished; - }); + } else { + final bool hasTimedOut = + _speedTestProvider.timeElapsedDownload + _speedTestProvider.timeElapsedUpload > SPEED_TEST_TIMEOUT_CONST; + if (hasTimedOut) { + setState(() { + goodSpeed = false; + cardState = TestStatus.finished; + timedOut = true; + }); + _speedTestProvider.cancelDownload(); + _speedTestProvider.cancelUpload(); + } else if (_speedTestProvider.speedTestDone) { + setState(() { + goodSpeed = true; + cardState = TestStatus.finished; + }); + } } } catch (_) {} }); @@ -129,9 +129,7 @@ class _WiFiCardState extends State Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon( Icons.wifi_sharp, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, size: 38, ), SizedBox(width: 10), @@ -166,9 +164,7 @@ class _WiFiCardState extends State setState(() { cardState = TestStatus.running; }); - _speedTestProvider.speedTest().timeout( - const Duration(seconds: 1), - onTimeout: _onTimeout); + _speedTestProvider.speedTest().timeout(const Duration(seconds: 1), onTimeout: _onTimeout); } }), // REPORT ISSUE @@ -183,8 +179,8 @@ class _WiFiCardState extends State return AlertDialogWidget( type: MessageTypeConstants.ERROR, icon: Icons.block_flipped, - title: WifiConstants.wifiIssueFailedTitle, - description: WifiConstants.wifiIssueFailedDesc, + title: WifiConstants.WIFI_ISSUE_FAILED_TITLE, + description: WifiConstants.WIFI_ISSUE_FAILED_DESC, onClose: () { Navigator.of(context).pop(); }); @@ -228,9 +224,7 @@ class _WiFiCardState extends State style: TextStyle(color: Colors.grey), ), direction: Axis.horizontal, - value: (_speedTestProvider.percentDownloaded + - _speedTestProvider.percentUploaded) / - 2, + value: (_speedTestProvider.percentDownloaded + _speedTestProvider.percentUploaded) / 2, valueColor: AlwaysStoppedAnimation(lightPrimaryColor)), ), ], @@ -243,8 +237,7 @@ class _WiFiCardState extends State String downloadSpeed = lastSpeed != null ? lastSpeed!.toStringAsPrecision(3) : _speedTestProvider.speed!.toStringAsPrecision(3) + " Mbps"; - String uploadSpeed = - _speedTestProvider.uploadSpeed!.toStringAsPrecision(3) + " Mbps"; + String uploadSpeed = _speedTestProvider.uploadSpeed!.toStringAsPrecision(3) + " Mbps"; if (downloadSpeed.contains("Infinity")) downloadSpeed = "N/A"; if (uploadSpeed.contains("Infinity")) uploadSpeed = "N/A"; @@ -288,9 +281,7 @@ class _WiFiCardState extends State style: TextStyle( fontSize: 24.0, fontWeight: FontWeight.w700, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, ), )) ], @@ -323,9 +314,7 @@ class _WiFiCardState extends State style: TextStyle( fontSize: 24.0, fontWeight: FontWeight.w700, - color: Theme.of(context).brightness == Brightness.light - ? lightPrimaryColor - : darkPrimaryColor2, + color: Theme.of(context).brightness == Brightness.light ? lightPrimaryColor : darkPrimaryColor2, ), )) ], @@ -337,8 +326,8 @@ class _WiFiCardState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ // TEST SPEED - // TODO: This brings you back to the initial state, but that requires two "TEST SPEED" Button clicks. - // TODO: For the UI people, do you want 1 click? or 2 clicks but the first one indicating to "Reset" + // TODO: This brings you back to the initial state, but that requires two "TEST SPEED" Button clicks. - December 2025 + // TODO: For the UI people, do you want 1 click? or 2 clicks but the first one indicating to "Reset" - December 2025 ActionButton( buttonText: 'TEST SPEED', onPressed: () { @@ -347,9 +336,7 @@ class _WiFiCardState extends State cardState = TestStatus.running; _speedTestProvider.resetSpeedTest(); }); - _speedTestProvider.speedTest().timeout( - const Duration(seconds: 1), - onTimeout: _onTimeout); + _speedTestProvider.speedTest().timeout(const Duration(seconds: 1), onTimeout: _onTimeout); // setState(() { // timedOut = false; // cardState = TestStatus.initial; @@ -360,25 +347,23 @@ class _WiFiCardState extends State // REPORT ISSUE ActionLink( buttonText: 'REPORT ISSUE', - onPressed: - _buttonEnabled // TODO: DO WE REALLY NEED THIS BUTTON ENABLED? - ? () { - _speedTestProvider.reportIssue(); - showDialog( - context: context, - builder: (context) { - return AlertDialogWidget( - type: MessageTypeConstants.SUCCESS, - icon: Icons.check_circle_outline_sharp, - title: WifiConstants.wifiIssueSuccessTitle, - description: - WifiConstants.wifiIssueSuccessDesc, - onClose: () { - Navigator.of(context).pop(); - }); - }); - } - : () {}, + onPressed: _buttonEnabled // TODO: DO WE REALLY NEED THIS BUTTON ENABLED? - December 2025 + ? () { + _speedTestProvider.reportIssue(); + showDialog( + context: context, + builder: (context) { + return AlertDialogWidget( + type: MessageTypeConstants.SUCCESS, + icon: Icons.check_circle_outline_sharp, + title: WifiConstants.WIFI_ISSUE_SUCCESS_TITLE, + description: WifiConstants.WIFI_ISSUE_SUCCESS_DESC, + onClose: () { + Navigator.of(context).pop(); + }); + }); + } + : () {}, ) ], ), @@ -474,12 +459,8 @@ class ScalingUtility { _queryData = MediaQuery.of(context); /// Calculate blocks accounting for notches and home bar - horizontalSafeBlock = (_queryData.size.width - - (_queryData.padding.left + _queryData.padding.right)) / - 100; - verticalSafeBlock = (_queryData.size.height - - (_queryData.padding.top + _queryData.padding.bottom)) / - 100; + horizontalSafeBlock = (_queryData.size.width - (_queryData.padding.left + _queryData.padding.right)) / 100; + verticalSafeBlock = (_queryData.size.height - (_queryData.padding.top + _queryData.padding.bottom)) / 100; } } @@ -503,10 +484,8 @@ class SizeConfig { blockSizeHorizontal = screenWidth / 100; blockSizeVertical = screenHeight / 100; - _safeAreaHorizontal = - _mediaQueryData.padding.left + _mediaQueryData.padding.right; - _safeAreaVertical = - _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; + _safeAreaHorizontal = _mediaQueryData.padding.left + _mediaQueryData.padding.right; + _safeAreaVertical = _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; safeBlockHorizontal = (screenWidth - _safeAreaHorizontal) / 100; safeBlockVertical = (screenHeight - _safeAreaVertical) / 100; } diff --git a/scripts/auto_check_all.sh b/scripts/auto_check_all.sh new file mode 100644 index 000000000..16b3381a2 --- /dev/null +++ b/scripts/auto_check_all.sh @@ -0,0 +1,193 @@ +#!/bin/bash +# Auto checks all the code to report violations back to the developer +# Usage: +# ./scripts/auto_check_all.sh +# ./scripts/auto_check_all.sh --dry-run # preview what would be fixed + +# Catch errors early +set -e + +# Set up working directory +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$script_dir/.." && pwd)" +cd "$repo_root" + +# Check --dry-run flag +DRY_RUN=false +if [[ "$1" == "--dry-run" ]]; then + DRY_RUN=true +fi + +echo "===================================" +echo "Checking Current Code Style..." +echo "===================================" + +if [[ "$DRY_RUN" == "true" ]]; then + echo "DRY RUN MODE - No changes will be made" + echo + echo "Would run the following checks:" + echo "1. Check static constants have FULL_UPPER_SNAKE_CASE" + echo "2. Check package imports contain The/Full/Path" + echo "3. Check classes have UpperCamelCase" + echo "4. Check file and directory names have lower_snake_case" + echo "5. Check variable and function names have lowerCamelCase" + echo "6. Check very long if statement conditions" + echo + exit 0 +fi + +# Track fixes applied +violations=0 +total_violations=0 + +# Create a temporary file to collect all violations +# Use existing VIOLATIONS_OUTPUT if set, otherwise create a new temp file +if [[ -n "${VIOLATIONS_OUTPUT:-}" ]]; then + violations_file="$VIOLATIONS_OUTPUT" + echo "Using existing violations file: $violations_file" +else + violations_file=$(mktemp) + echo "Created new violations file: $violations_file" +fi +export VIOLATIONS_OUTPUT="$violations_file" + +# Array to store check results and violation counts +declare -A check_results +declare -A violation_counts + +# First Check - Ensure all static const variables use FULL_UPPER_SNAKE_CASE +echo "1. Checking static constants have FULL_UPPER_SNAKE_CASE..." +if bash ./scripts/checking/check_static_const_upper_snake_case.sh check; then + check_results["static_const"]="PASSED" + violation_counts["static_const"]=0 + echo "All static constants follow UPPER_SNAKE_CASE naming convention." +else + check_results["static_const"]="FAILED" + # Count violations from the violations file + static_const_violations=$(grep -A999 "STATIC_CONST_VIOLATIONS_START" "$violations_file" 2>/dev/null | grep -B999 "STATIC_CONST_VIOLATIONS_END" | grep -v "VIOLATIONS_" | wc -l || echo "0") + violation_counts["static_const"]=$static_const_violations + violations=$((violations + 1)) + total_violations=$((total_violations + static_const_violations)) +fi +echo +echo "=====================================================================" + +# Second Check - Ensure all package imports contain The/Full/Path +echo "2. Checking package imports contain The/Full/Path..." +if bash ./scripts/checking/check_package_imports.sh check; then + check_results["package_imports"]="PASSED" + violation_counts["package_imports"]=0 + echo "All imports use package import paths." +else + check_results["package_imports"]="FAILED" + package_import_violations=$(grep -A999 "PACKAGE_IMPORT_VIOLATIONS_START" "$violations_file" 2>/dev/null | grep -B999 "PACKAGE_IMPORT_VIOLATIONS_END" | grep -v "VIOLATIONS_" | wc -l || echo "0") + violation_counts["package_imports"]=$package_import_violations + violations=$((violations + 1)) + total_violations=$((total_violations + package_import_violations)) +fi +echo +echo "=====================================================================" + +# Third Check - Ensure all classes use UpperCamelCase +echo "3. Checking classes have UpperCamelCase..." +if bash ./scripts/checking/check_classes_have_upper_camel_case.sh check; then + check_results["class_naming"]="PASSED" + violation_counts["class_naming"]=0 + echo "All classes follow UpperCamelCase naming convention." +else + check_results["class_naming"]="FAILED" + class_naming_violations=$(grep -A999 "CLASS_NAMING_VIOLATIONS_START" "$violations_file" 2>/dev/null | grep -B999 "CLASS_NAMING_VIOLATIONS_END" | grep -v "VIOLATIONS_" | wc -l || echo "0") + violation_counts["class_naming"]=$class_naming_violations + violations=$((violations + 1)) + total_violations=$((total_violations + class_naming_violations)) +fi +echo +echo "=====================================================================" + +# Fourth Check - Ensure all file and directory names use lower_snake_case +echo "4. Checking file and directory names have lower_snake_case..." +if bash ./scripts/checking/check_files_directories_have_snake_case.sh check; then + check_results["file_dir_naming"]="PASSED" + violation_counts["file_dir_naming"]=0 + echo "All files and directories follow snake_case naming convention." +else + check_results["file_dir_naming"]="FAILED" + file_dir_violations=$(grep -A999 "FILE_DIR_NAMING_VIOLATIONS_START" "$violations_file" 2>/dev/null | grep -B999 "FILE_DIR_NAMING_VIOLATIONS_END" | grep -v "VIOLATIONS_" | wc -l || echo "0") + violation_counts["file_dir_naming"]=$file_dir_violations + violations=$((violations + 1)) + total_violations=$((total_violations + file_dir_violations)) +fi +echo +echo "=====================================================================" + +# Fifth Check - Ensure all variable and function names use lowerCamelCase +echo "5. Checking variable and function names have lowerCamelCase..." +if bash ./scripts/checking/check_var_func_lower_camel_case.sh check; then + check_results["variable_naming"]="PASSED" + violation_counts["variable_naming"]=0 + echo "All variables and functions follow lowerCamelCase naming convention." +else + check_results["variable_naming"]="FAILED" + variable_violations=$(grep -A999 "VARIABLE_NAMING_VIOLATIONS_START" "$violations_file" 2>/dev/null | grep -B999 "VARIABLE_NAMING_VIOLATIONS_END" | grep -v "VIOLATIONS_" | wc -l || echo "0") + violation_counts["variable_naming"]=$variable_violations + violations=$((violations + 1)) + total_violations=$((total_violations + variable_violations)) +fi +echo +echo "=====================================================================" + +# Sixth Check - Ensure no very long if statement conditions +echo "6. Checking very long if statement conditions..." +if bash ./scripts/checking/check_very_long_conditions.sh check; then + check_results["long_conditions"]="PASSED" + violation_counts["long_conditions"]=0 + echo "No very long if statement conditions found." +else + check_results["long_conditions"]="FAILED" + long_condition_violations=$(grep -A999 "LONG_CONDITIONS_VIOLATIONS_START" "$violations_file" 2>/dev/null | grep -B999 "LONG_CONDITIONS_VIOLATIONS_END" | grep -v "VIOLATIONS_" | wc -l || echo "0") + violation_counts["long_conditions"]=$long_condition_violations + violations=$((violations + 1)) + total_violations=$((total_violations + long_condition_violations)) +fi +echo +echo "=====================================================================" +echo "Code Style Check Summary" +echo "Total violation categories: $violations/6" +echo "Total individual violations: $total_violations" + +if [[ $violations -eq 0 ]]; then + echo "All checks passed! Your code follows all style guidelines." + echo + # Clean up + rm -f "$violations_file" + exit 0 +else + echo "Code style violations found. Please address the following issues:" + echo + + # Export detailed violations for the GitHub workflow + if [[ -s "$violations_file" ]]; then + echo "VIOLATIONS_DETAILS_START" >> "$violations_file" + echo "Total Categories Failed: $violations/6" >> "$violations_file" + echo "Total Individual Violations: $total_violations" >> "$violations_file" + echo "Check Results:" >> "$violations_file" + echo "- Static Constants (UPPER_SNAKE_CASE): ${check_results[static_const]} (${violation_counts[static_const]} violations)" >> "$violations_file" + echo "- Package Imports: ${check_results[package_imports]} (${violation_counts[package_imports]} violations)" >> "$violations_file" + echo "- Class Naming (UpperCamelCase): ${check_results[class_naming]} (${violation_counts[class_naming]} violations)" >> "$violations_file" + echo "- File/Directory Naming (snake_case): ${check_results[file_dir_naming]} (${violation_counts[file_dir_naming]} violations)" >> "$violations_file" + echo "- Variable Naming (lowerCamelCase): ${check_results[variable_naming]} (${violation_counts[variable_naming]} violations)" >> "$violations_file" + echo "- Very Long If Statement Conditions: ${check_results[long_conditions]} (${violation_counts[long_conditions]} violations)" >> "$violations_file" + echo "VIOLATIONS_DETAILS_END" >> "$violations_file" + fi + + # Display summary to user + echo "Failed checks:" + [[ "${check_results[static_const]}" == "FAILED" ]] && echo " - Static Constants: ${violation_counts[static_const]} violations" + [[ "${check_results[package_imports]}" == "FAILED" ]] && echo " - Package Imports: ${violation_counts[package_imports]} violations" + [[ "${check_results[class_naming]}" == "FAILED" ]] && echo " - Class Naming: ${violation_counts[class_naming]} violations" + [[ "${check_results[file_dir_naming]}" == "FAILED" ]] && echo " - File/Directory Naming: ${violation_counts[file_dir_naming]} violations" + [[ "${check_results[variable_naming]}" == "FAILED" ]] && echo " - Variable Naming: ${violation_counts[variable_naming]} violations" + [[ "${check_results[long_conditions]}" == "FAILED" ]] && echo " - Very Long If Statement Conditions: ${violation_counts[long_conditions]} violations" + echo "Please review the detailed output above and fix the violations before committing." + exit 1 +fi diff --git a/scripts/auto_fix_all.sh b/scripts/auto_fix_all.sh new file mode 100644 index 000000000..72609527c --- /dev/null +++ b/scripts/auto_fix_all.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# Auto-fix all code quality violations +# Usage: +# ./scripts/auto_fix_all.sh --dry-run # Preview what would be fixed +# ./scripts/auto_fix_all.sh # Apply all auto-fixes + +set -e + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$script_dir/.." && pwd)" +cd "$repo_root" + +DRY_RUN=false +if [[ "$1" == "--dry-run" ]]; then + DRY_RUN=true +fi + +echo +echo "====================================" +echo "Auto Code Styling Process Started..." + +# Track fixes applied +fixes_applied=0 + +if [[ "$DRY_RUN" == "true" ]]; then + echo "DRY RUN MODE - No changes will be made" + echo + echo "Would run the following fixes:" + echo "1. Comment spacing fixes" + echo "2. TODO dating fixes" + echo "3. Ensure one-line if statements are without braces" + echo "4. Ensure one-line functions use arrow syntax" + echo + exit 0 +fi + +echo "------------------------------------" +# First fix is to change all //Comments to // Comments +echo "1. Ensuring all comments have a space after the slashes..." +if bash scripts/styling/add_space_after_comment_slashes.sh --fix; then + echo "Comment spacing fixes completed." + fixes_applied=$((fixes_applied + 1)) +else + echo "Nothing to fix, moving on..." +fi + +echo "------------------------------------" +# Second fix is to ensure all TODOs have a date +echo "2. Ensuring all TODO comments have a date..." +if bash scripts/styling/add_date_to_todo_comment.sh --fix; then + echo "TODO dates fixes completed." + fixes_applied=$((fixes_applied + 1)) +else + echo "Nothing to fix, moving on..." +fi + +echo "------------------------------------" +# Third fix is to ensure all if statements that can be one-liners are one-liners without braces +echo "3. Ensuring all one-line if statements are without braces..." +if bash scripts/styling/one_line_if_statement_fix.sh --fix; then + echo "One-line if statements have been fixed." + fixes_applied=$((fixes_applied + 1)) +else + echo "Nothing to fix, moving on..." +fi + +echo "------------------------------------" +# Fourth fix is to ensure all functions that can be one-liners use arrow syntax +echo "4. Ensuring all functions that can be one-liners use arrow syntax..." +if bash scripts/styling/one_line_function_fix.sh --fix; then + echo "One-line functions have been fixed." + fixes_applied=$((fixes_applied + 1)) +else + echo "Nothing to fix, moving on..." +fi + +echo "------------------------------------" +# Fifth fix is to run dart format +echo "Running dart format..." +if bash dart format --line-length 120 lib; then + echo "Dart formatting completed" +else + echo "Nothing to fix, moving on..." +fi + +echo "====================================" +echo "Total fixes applied: $fixes_applied/4" +echo "Auto Code Styling Process Completed!" diff --git a/scripts/checking/check_classes_have_upper_camel_case.sh b/scripts/checking/check_classes_have_upper_camel_case.sh new file mode 100644 index 000000000..b347afe76 --- /dev/null +++ b/scripts/checking/check_classes_have_upper_camel_case.sh @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +# classes_upper_camel_case.sh - Enforce UpperCamelCase (PascalCase) naming for class names in Dart files. +# Usage: +# ./scripts/classes_upper_camel_case.sh -> check all files and report violations +# ./scripts/classes_upper_camel_case.sh check -> check mode (exits non-zero if violations found) +# ./scripts/classes_upper_camel_case.sh fix -> attempt to auto-fix violations (experimental) + +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$script_dir/../.." && pwd)" +cd "$repo_root" + +# Defaults +MODE="report" + +# Parse arguments +for arg in "$@"; do + case "$arg" in + check) + MODE="check" + ;; + fix) + MODE="fix" + ;; + *) + echo "Unknown argument: $arg" + echo "Usage: $0 [check|fix]" + exit 2 + ;; + esac +done + +# Function to check if a name follows UpperCamelCase (PascalCase) +is_upper_camel_case() { + local name="$1" + + # Check for private classes starting with underscore + if [[ "$name" =~ ^_ ]]; then + # Remove leading underscore and check the rest + local rest="${name#_}" + # Rest should be UpperCamelCase (starts with uppercase, no underscores) + if [[ "$rest" =~ ^[A-Z][a-zA-Z0-9]*$ ]]; then + return 0 + else + return 1 + fi + else + # Regular UpperCamelCase: starts with uppercase letter, can contain letters and numbers, no underscores + if [[ "$name" =~ ^[A-Z][a-zA-Z0-9]*$ ]]; then + return 0 + else + return 1 + fi + fi +} + +# Function to convert to UpperCamelCase (basic conversion) +to_upper_camel_case() { + local name="$1" + # Convert snake_case to UpperCamelCase + echo "$name" | sed 's/_\([a-z]\)/\U\1/g' | sed 's/^./\U&/' +} + +violations_found=0 +total_files=0 + +echo "Checking Dart files for UpperCamelCase class names..." +echo "Scanning: lib/" +echo "Looking for class names that should be UpperCamelCase..." + +# Create violations array to store detailed information +violations=() + +# Find class declarations and extract class names correctly +# Use a temporary file to avoid subshell issues with variable counting +temp_results=$(mktemp) + +# Find regular class declarations +grep -rn --include="*.dart" -E "^[[:space:]]*class[[:space:]]+" lib/ > "$temp_results" 2>/dev/null || true + +while IFS=: read -r file line_num content; do + # Pattern: class ClassName or class ClassName extends/implements/with + if [[ "$content" =~ class[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*) ]]; then + class_name="${BASH_REMATCH[1]}" + if ! is_upper_camel_case "$class_name"; then + suggested=$(to_upper_camel_case "$class_name") + violation_msg="$file:$line_num: Class '$class_name' should be UpperCamelCase (suggested: $suggested)" + violations+=("$violation_msg") + echo " $violation_msg" + violations_found=$((violations_found + 1)) + fi + fi +done < "$temp_results" + +# Also check for abstract classes +grep -rn --include="*.dart" -E "^[[:space:]]*abstract[[:space:]]+class[[:space:]]+" lib/ > "$temp_results" 2>/dev/null || true + +while IFS=: read -r file line_num content; do + # Pattern: abstract class ClassName + if [[ "$content" =~ abstract[[:space:]]+class[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*) ]]; then + class_name="${BASH_REMATCH[1]}" + if ! is_upper_camel_case "$class_name"; then + suggested=$(to_upper_camel_case "$class_name") + violation_msg="$file:$line_num: Abstract class '$class_name' should be UpperCamelCase (suggested: $suggested)" + violations+=("$violation_msg") + echo " $violation_msg" + violations_found=$((violations_found + 1)) + fi + fi +done < "$temp_results" + +# Clean up temporary file +rm -f "$temp_results" + +# Count total files processed +total_files=$(find lib -name "*.dart" -type f | wc -l) + +echo "" +echo "Summary:" +echo " Files checked: $total_files" +echo " Total violations: $violations_found" + +# Export violations for parent script if VIOLATIONS_OUTPUT is set +if [[ -n "${VIOLATIONS_OUTPUT:-}" ]]; then + if [[ $violations_found -gt 0 ]]; then + echo "CLASS_NAMING_VIOLATIONS_START" >> "$VIOLATIONS_OUTPUT" + # Re-process violations to write to file + grep -rn --include="*.dart" -E "^[[:space:]]*class[[:space:]]+" lib/ 2>/dev/null | while IFS=: read -r file line_num content; do + if [[ "$content" =~ class[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*) ]]; then + class_name="${BASH_REMATCH[1]}" + if ! is_upper_camel_case "$class_name"; then + suggested=$(to_upper_camel_case "$class_name") + echo "$file:$line_num: Class '$class_name' should be UpperCamelCase (suggested: $suggested)" >> "$VIOLATIONS_OUTPUT" + fi + fi + done + grep -rn --include="*.dart" -E "^[[:space:]]*abstract[[:space:]]+class[[:space:]]+" lib/ 2>/dev/null | while IFS=: read -r file line_num content; do + if [[ "$content" =~ abstract[[:space:]]+class[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*) ]]; then + class_name="${BASH_REMATCH[1]}" + if ! is_upper_camel_case "$class_name"; then + suggested=$(to_upper_camel_case "$class_name") + echo "$file:$line_num: Abstract class '$class_name' should be UpperCamelCase (suggested: $suggested)" >> "$VIOLATIONS_OUTPUT" + fi + fi + done + echo "CLASS_NAMING_VIOLATIONS_END" >> "$VIOLATIONS_OUTPUT" + fi +fi + +if [[ $violations_found -gt 0 ]]; then + echo "" + echo "UpperCamelCase guidelines for classes:" + echo " - Good: UserProfile, NetworkHelper, DatabaseManager" + echo " - Bad: userProfile, network_helper, databaseManager" + echo "" + + if [[ "$MODE" == "fix" ]]; then + echo "Auto-fix mode is experimental and may require manual review." + echo "Please verify changes before committing." + elif [[ "$MODE" == "check" ]]; then + exit 1 + else + echo "Run with 'fix' argument to see suggested corrections." + echo "Run with 'check' argument for CI/CD integration." + fi +fi + +if [[ $violations_found -gt 0 ]]; then + exit 1 +else + exit 0 +fi diff --git a/scripts/checking/check_files_directories_have_snake_case.sh b/scripts/checking/check_files_directories_have_snake_case.sh new file mode 100644 index 000000000..6cc2b2f76 --- /dev/null +++ b/scripts/checking/check_files_directories_have_snake_case.sh @@ -0,0 +1,165 @@ +#!/bin/bash +# Enforce snake_case naming for files and directories in lib/ +# Usage: +# ./scripts/file_dir_snake_case.sh + +set -e + +MODE="check" +if [[ "$1" == "--fix" ]]; then + MODE="fix" +fi + +# Function to check if name follows snake_case convention +is_snake_case() { + local name="$1" + # Remove .dart extension if present + name="${name%.dart}" + # Also handle generated files like .g.dart, .freezed.dart, etc. + name="${name%.g}" + name="${name%.freezed}" + name="${name%.mocks}" + + # Basic snake_case: lowercase letters, digits, and underscores only + if ! [[ "$name" =~ ^[a-z][a-z0-9_]*$ ]]; then + return 1 + fi + + # Additional check: reject names that look like they have concatenated words + # This catches cases like "mystudentchart" which should be "my_student_chart" + # Look for patterns where lowercase is followed by another word that should be separated + if [[ "$name" =~ (my|student|chart|ucsd|page|view|card|list|item|data|info|home|main|app|user|profile|settings|detail|edit|create|update|delete) ]] && [[ ! "$name" =~ _ ]]; then + # If it contains common words but no underscores, it might need separation + # But allow single word names and properly separated names + local word_count=$(echo "$name" | grep -o -E "(my|student|chart|ucsd|page|view|card|list|item|data|info|home|main|app|user|profile|settings|detail|edit|create|update|delete)" | wc -l) + if [[ $word_count -gt 1 ]]; then + return 1 + fi + fi + + return 0 +} + +# Function to convert to snake_case +to_snake_case() { + local name="$1" + local result="$name" + + # Handle PascalCase/camelCase first + result=$(echo "$result" | sed -E 's/([A-Z])/_\L\1/g' | sed 's/^_//' | tr '[:upper:]' '[:lower:]') + + # Handle common word patterns for concatenated lowercase words + result=$(echo "$result" | sed -E 's/my([a-z])/my_\1/g') + result=$(echo "$result" | sed -E 's/student([a-z])/student_\1/g') + result=$(echo "$result" | sed -E 's/chart([a-z])/chart_\1/g') + result=$(echo "$result" | sed -E 's/ucsd([a-z])/ucsd_\1/g') + result=$(echo "$result" | sed -E 's/([a-z])chart/\1_chart/g') + result=$(echo "$result" | sed -E 's/([a-z])page/\1_page/g') + result=$(echo "$result" | sed -E 's/([a-z])view/\1_view/g') + result=$(echo "$result" | sed -E 's/([a-z])card/\1_card/g') + + echo "$result" +} + +echo "Checking file and directory naming conventions..." + +violations_found=0 +violations=() + +# Check files +echo "Checking files in lib/..." +while IFS= read -r filepath; do + if [[ -n "$filepath" ]]; then + filename=$(basename "$filepath") + + if ! is_snake_case "$filename"; then + suggested=$(to_snake_case "$filename") + violation_msg="$filepath: File '$filename' should be snake_case (suggested: $suggested)" + violations+=("$violation_msg") + echo " $violation_msg" + violations_found=$((violations_found + 1)) + + if [[ "$MODE" == "fix" ]]; then + echo " Fixing: $filename -> $suggested" + + # Perform the actual rename + new_filepath="${filepath%/*}/$suggested" + if [[ "$filepath" != "$new_filepath" ]]; then + mv "$filepath" "$new_filepath" + echo " Renamed: $filepath -> $new_filepath" + fi + fi + fi + fi +done < <(find lib -type f -name "*.dart") + +# Check directories +echo "Checking directories in lib/..." +while IFS= read -r dirpath; do + if [[ -n "$dirpath" && "$dirpath" != "lib" ]]; then + dirname=$(basename "$dirpath") + + if ! is_snake_case "$dirname"; then + suggested=$(to_snake_case "$dirname") + violation_msg="$dirpath/: Directory '$dirname' should be snake_case (suggested: $suggested)" + violations+=("$violation_msg") + echo " $violation_msg" + violations_found=$((violations_found + 1)) + + if [[ "$MODE" == "fix" ]]; then + echo " Fixing: $dirname -> $suggested" + + # Perform the actual directory rename + parent_dir="$(dirname "$dirpath")" + new_dirpath="$parent_dir/$suggested" + if [[ "$dirpath" != "$new_dirpath" ]]; then + mv "$dirpath" "$new_dirpath" + echo " Renamed directory: $dirpath -> $new_dirpath" + fi + fi + fi + fi +done < <(find lib -type d -not -path "lib") + +echo +echo "File and Directory naming check complete." +echo "Violations found: $violations_found" + +# Export violations for parent script if VIOLATIONS_OUTPUT is set +if [[ -n "${VIOLATIONS_OUTPUT:-}" ]]; then + if [[ $violations_found -gt 0 ]]; then + echo "FILE_DIR_NAMING_VIOLATIONS_START" >> "$VIOLATIONS_OUTPUT" + # Re-process violations to write to file + find lib -type f -name "*.dart" | while IFS= read -r filepath; do + if [[ -n "$filepath" ]]; then + filename=$(basename "$filepath") + if ! is_snake_case "$filename"; then + suggested=$(to_snake_case "$filename") + echo "$filepath: File '$filename' should be snake_case (suggested: $suggested)" >> "$VIOLATIONS_OUTPUT" + fi + fi + done + find lib -type d -not -path "lib" | while IFS= read -r dirpath; do + if [[ -n "$dirpath" && "$dirpath" != "lib" ]]; then + dirname=$(basename "$dirpath") + if ! is_snake_case "$dirname"; then + suggested=$(to_snake_case "$dirname") + echo "$dirpath/: Directory '$dirname' should be snake_case (suggested: $suggested)" >> "$VIOLATIONS_OUTPUT" + fi + fi + done + echo "FILE_DIR_NAMING_VIOLATIONS_END" >> "$VIOLATIONS_OUTPUT" + fi +fi + +if [[ $violations_found -gt 0 ]]; then + if [[ "$MODE" == "fix" ]]; then + echo "Fixed $violations_found file/directory naming violations" + exit 0 + else + echo "Files and directories should use snake_case naming convention" + exit 1 + fi +else + exit 0 +fi diff --git a/scripts/checking/check_package_imports.sh b/scripts/checking/check_package_imports.sh new file mode 100644 index 000000000..3c86b4991 --- /dev/null +++ b/scripts/checking/check_package_imports.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# Enforce full package import paths (no relative imports) +# Usage: +# ./scripts/enforce_package_imports.sh [--fix] + +set -e + +MODE="check" +if [[ "$1" == "--fix" ]]; then + MODE="fix" +fi + +# Get the package name from pubspec.yaml +get_package_name() { + if [[ -f "pubspec.yaml" ]]; then + grep "^name:" pubspec.yaml | sed 's/name: *//' | tr -d '"'"'" + else + echo "campus_mobile_experimental" # fallback + fi +} + +PACKAGE_NAME=$(get_package_name) + +echo "Checking for relative imports in Dart files..." +echo "Package name: $PACKAGE_NAME" + +violations_found=0 +violations=() + +# Simple approach: use grep to find all relative imports +echo "Scanning for relative imports..." + +# Create a temporary file to store results +temp_file=$(mktemp) + +# Find all relative imports and save to temp file +find lib -type f -name "*.dart" -exec grep -Hn "^[[:space:]]*import[[:space:]]*['\"][.][.]" {} \; > "$temp_file" 2>/dev/null || true + +# Process the results +while IFS=: read -r filepath line_number line_content; do + if [[ -n "$filepath" && -n "$line_number" && -n "$line_content" ]]; then + # Extract the relative path and create suggestion + rel_path=$(echo "$line_content" | sed -E "s/.*import[[:space:]]*['\"]([^'\"]+)['\"].*/\1/") + suggested_path=$(echo "$rel_path" | sed 's|\.\./||g') + + # Determine quote style + if [[ "$line_content" =~ \" ]]; then + quote='"' + else + quote="'" + fi + + suggested_import="import ${quote}package:${PACKAGE_NAME}/${suggested_path}${quote};" + violation_msg="$filepath:$line_number: Relative import found (suggested: $suggested_import)" + violations+=("$violation_msg") + + echo " $filepath:$line_number: Relative import found" + echo " Current: $line_content" + echo " Suggested: $suggested_import" + violations_found=$((violations_found + 1)) + fi +done < "$temp_file" + +# Clean up +rm -f "$temp_file" + +# Count total files scanned +total_files=$(find lib -type f -name "*.dart" | wc -l) + +echo +echo "Import path check complete." +echo "Files scanned: $total_files" +echo "Violations found: $violations_found" + +# Export violations for parent script if VIOLATIONS_OUTPUT is set +if [[ -n "${VIOLATIONS_OUTPUT:-}" ]]; then + if [[ $violations_found -gt 0 ]]; then + echo "PACKAGE_IMPORT_VIOLATIONS_START" >> "$VIOLATIONS_OUTPUT" + # Re-process violations to write to file + find lib -type f -name "*.dart" -exec grep -Hn "^[[:space:]]*import[[:space:]]*['\"][.][.]" {} \; 2>/dev/null | while IFS=: read -r filepath line_number line_content; do + if [[ -n "$filepath" && -n "$line_number" && -n "$line_content" ]]; then + rel_path=$(echo "$line_content" | sed -E "s/.*import[[:space:]]*['\"]([^'\"]+)['\"].*/\1/") + suggested_path=$(echo "$rel_path" | sed 's|\.\./||g') + if [[ "$line_content" =~ \" ]]; then + quote='"' + else + quote="'" + fi + suggested_import="import ${quote}package:${PACKAGE_NAME}/${suggested_path}${quote};" + echo "$filepath:$line_number: Relative import found (suggested: $suggested_import)" >> "$VIOLATIONS_OUTPUT" + fi + done + echo "PACKAGE_IMPORT_VIOLATIONS_END" >> "$VIOLATIONS_OUTPUT" + fi +fi + +if [[ $violations_found -gt 0 ]]; then + echo "Use package imports instead of relative imports" + echo " GOOD: import 'package:$PACKAGE_NAME/core/providers/map.dart';" + echo " BAD: import '../../core/providers/map.dart';" + exit 1 +else + exit 0 +fi diff --git a/scripts/checking/check_static_const_upper_snake_case.sh b/scripts/checking/check_static_const_upper_snake_case.sh new file mode 100644 index 000000000..4b5f342c3 --- /dev/null +++ b/scripts/checking/check_static_const_upper_snake_case.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +# static_const_upper_snake_case.sh - Enforce UPPER_SNAKE_CASE naming for static constants in Dart files. +# Usage: +# ./scripts/static_const_upper_snake_case.sh -> check all files and report violations +# ./scripts/static_const_upper_snake_case.sh check -> check mode (exits non-zero if violations found) +# ./scripts/static_const_upper_snake_case.sh fix -> attempt to auto-fix violations (experimental) + +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$script_dir/../.." && pwd)" +cd "$repo_root" + +# Defaults +MODE="report" + +# Parse arguments +for arg in "$@"; do + case "$arg" in + check) + MODE="check" + ;; + fix) + MODE="fix" + ;; + *) + echo "Unknown argument: $arg" + echo "Usage: $0 [check|fix]" + exit 2 + ;; + esac +done + +# Function to check if a name follows UPPER_SNAKE_CASE +is_upper_snake_case() { + local name="$1" + # UPPER_SNAKE_CASE: can start with underscore (for private) or uppercase letter, followed by uppercase letters, numbers, and underscores + if [[ "$name" =~ ^(_?[A-Z][A-Z0-9_]*)$ ]]; then + return 0 + else + return 1 + fi +} + +# Function to convert to UPPER_SNAKE_CASE (basic conversion) +to_upper_snake_case() { + local name="$1" + # Convert lowerCamelCase to UPPER_SNAKE_CASE + echo "$name" | sed 's/\([a-z]\)\([A-Z]\)/\1_\2/g' | tr '[:lower:]' '[:upper:]' +} + +violations_found=0 +total_files=0 + +echo "Checking Dart files for UPPER_SNAKE_CASE static constants..." +echo "Scanning: lib/" +echo "Looking for static const variables that should be UPPER_SNAKE_CASE..." + +# Create violations array to store detailed information +violations=() + +# Find static const violations +# Use a temporary file to avoid subshell issues with variable counting +temp_results=$(mktemp) +grep -rn --include="*.dart" "static const" lib/ > "$temp_results" + +while IFS=: read -r file line_num content; do + # Pattern 1: static const variableName (without explicit type) + if [[ "$content" =~ static[[:space:]]+const[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*= ]]; then + var_name="${BASH_REMATCH[1]}" + if ! is_upper_snake_case "$var_name"; then + suggested=$(to_upper_snake_case "$var_name") + violation_msg="$file:$line_num: Static const '$var_name' should be UPPER_SNAKE_CASE (suggested: $suggested)" + violations+=("$violation_msg") + echo " $violation_msg" + violations_found=$((violations_found + 1)) + fi + # Pattern 2: static const Type variableName (with explicit type) + elif [[ "$content" =~ static[[:space:]]+const[[:space:]]+[A-Za-z][A-Za-z0-9_\<\>\?,[:space:]]*[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*= ]]; then + var_name="${BASH_REMATCH[1]}" + if ! is_upper_snake_case "$var_name"; then + suggested=$(to_upper_snake_case "$var_name") + violation_msg="$file:$line_num: Static const '$var_name' should be UPPER_SNAKE_CASE (suggested: $suggested)" + violations+=("$violation_msg") + echo " $violation_msg" + violations_found=$((violations_found + 1)) + fi + fi +done < "$temp_results" + +# Clean up temporary file +rm -f "$temp_results" + +# Count total files processed +total_files=$(find lib -name "*.dart" -type f | wc -l) + +echo "" +echo "Summary:" +echo " Files checked: $total_files" +echo " Total violations: $violations_found" + +# Export violations for parent script if VIOLATIONS_OUTPUT is set +if [[ -n "${VIOLATIONS_OUTPUT:-}" ]]; then + if [[ $violations_found -gt 0 ]]; then + echo "STATIC_CONST_VIOLATIONS_START" >> "$VIOLATIONS_OUTPUT" + # Re-process violations to write to file (since array was in subshell) + grep -rn --include="*.dart" "static const" lib/ | while IFS=: read -r file line_num content; do + # Pattern 1: static const variableName (without explicit type) + if [[ "$content" =~ static[[:space:]]+const[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*= ]]; then + var_name="${BASH_REMATCH[1]}" + if ! is_upper_snake_case "$var_name"; then + suggested=$(to_upper_snake_case "$var_name") + echo "$file:$line_num: Static const '$var_name' should be UPPER_SNAKE_CASE (suggested: $suggested)" >> "$VIOLATIONS_OUTPUT" + fi + # Pattern 2: static const Type variableName (with explicit type) + elif [[ "$content" =~ static[[:space:]]+const[[:space:]]+[A-Za-z][A-Za-z0-9_\<\>\?,[:space:]]*[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*= ]]; then + var_name="${BASH_REMATCH[1]}" + if ! is_upper_snake_case "$var_name"; then + suggested=$(to_upper_snake_case "$var_name") + echo "$file:$line_num: Static const '$var_name' should be UPPER_SNAKE_CASE (suggested: $suggested)" >> "$VIOLATIONS_OUTPUT" + fi + fi + done + echo "STATIC_CONST_VIOLATIONS_END" >> "$VIOLATIONS_OUTPUT" + fi +fi + +if [[ $violations_found -gt 0 ]]; then + echo "" + echo "UPPER_SNAKE_CASE guidelines for static constants:" + echo " - Good: PAYMENT_FILTER_TYPES, MAX_RETRY_COUNT, API_BASE_URL" + echo " - Bad: payment_filter_types, paymentFilterTypes, Payment_Filter_Types" + echo "" + + if [[ "$MODE" == "fix" ]]; then + echo "Auto-fix mode is experimental and may require manual review." + echo "Please verify changes before committing." + elif [[ "$MODE" == "check" ]]; then + exit 1 + else + echo "Run with 'fix' argument to see suggested corrections." + echo "Run with 'check' argument for CI/CD integration." + fi +fi + +if [[ $violations_found -gt 0 ]]; then + exit 1 +else + exit 0 +fi diff --git a/scripts/checking/check_var_func_lower_camel_case.sh b/scripts/checking/check_var_func_lower_camel_case.sh new file mode 100644 index 000000000..ca33d7ba3 --- /dev/null +++ b/scripts/checking/check_var_func_lower_camel_case.sh @@ -0,0 +1,170 @@ +#!/usr/bin/env bash +# var_func_lower_camel_case.sh - Check and enforce lowerCamelCase naming for regular variables in Dart files. +# This script focuses ONLY on regular variables (var, final, const) and excludes static constants. +# For static constants, use static_const_upper_snake_case.sh instead. +# Usage: +# ./scripts/var_func_lower_camel_case.sh -> check all files and report violations +# ./scripts/var_func_lower_camel_case.sh check -> check mode (exits non-zero if violations found) +# ./scripts/var_func_lower_camel_case.sh fix -> attempt to auto-fix violations (experimental) + +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$script_dir/../.." && pwd)" +cd "$repo_root" + +# Defaults +MODE="report" + +# Parse arguments +for arg in "$@"; do + case "$arg" in + check) + MODE="check" + ;; + fix) + MODE="fix" + ;; + *) + echo "Unknown argument: $arg" + echo "Usage: $0 [check|fix]" + exit 2 + ;; + esac +done + +# Function to check if a name follows lowerCamelCase +is_lower_camel_case() { + local name="$1" + + # Check for private variables starting with underscore + if [[ "$name" =~ ^_ ]]; then + # Remove leading underscore and check the rest + local rest="${name#_}" + # Rest should be lowerCamelCase (starts with lowercase, no underscores) + if [[ "$rest" =~ ^[a-z][a-zA-Z0-9]*$ ]]; then + return 0 + else + return 1 + fi + else + # Regular lowerCamelCase: starts with lowercase, no underscores + if [[ "$name" =~ ^[a-z][a-zA-Z0-9]*$ ]]; then + return 0 + else + return 1 + fi + fi +} + +# Function to convert to lowerCamelCase (basic conversion) +to_lower_camel_case() { + local name="$1" + # Convert snake_case to camelCase + echo "$name" | sed 's/_\([a-z]\)/\U\1/g' | sed 's/^\([A-Z]\)/\l\1/' +} + +violations_found=0 +total_files=0 + +echo "Checking Dart files for lowerCamelCase regular variables (excluding static const)..." +echo "Scanning: lib/" + +# Create violations array to store detailed information +violations=() + +# Find and report violations for regular variables (excluding static const) +echo "Looking for non-lowerCamelCase variables (excluding static const)..." + +# Find variable declarations and extract variable names correctly +# Use a temporary file to avoid subshell issues with variable counting +temp_results=$(mktemp) +grep -rn --include="*.dart" -E "(var|final|const)[[:space:]]+" lib/ | \ + grep -v "static const" | \ + grep -v "static final" | \ + grep -v "^[^:]*:[^:]*:[[:space:]]*const[[:space:]]" > "$temp_results" + +while IFS=: read -r file line_num content; do + # Pattern 1: var/final/const variableName (without explicit type) + if [[ "$content" =~ (var|final|const)[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*= ]]; then + var_name="${BASH_REMATCH[2]}" + if ! is_lower_camel_case "$var_name"; then + suggested=$(to_lower_camel_case "$var_name") + violation_msg="$file:$line_num: Variable '$var_name' should be lowerCamelCase (suggested: $suggested)" + violations+=("$violation_msg") + echo " $violation_msg" + violations_found=$((violations_found + 1)) + fi + # Pattern 2: var/final/const Type variableName (with explicit type) + elif [[ "$content" =~ (var|final|const)[[:space:]]+[A-Za-z][A-Za-z0-9_\<\>\?,[:space:]]*[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*= ]]; then + var_name="${BASH_REMATCH[2]}" + if ! is_lower_camel_case "$var_name"; then + suggested=$(to_lower_camel_case "$var_name") + violation_msg="$file:$line_num: Variable '$var_name' should be lowerCamelCase (suggested: $suggested)" + violations+=("$violation_msg") + echo " $violation_msg" + violations_found=$((violations_found + 1)) + fi + fi +done < "$temp_results" + +# Clean up temporary file +rm -f "$temp_results" + +# Count total files processed (approximate) +total_files=$(find lib -name "*.dart" -type f | wc -l) + +echo +echo "Summary:" +echo " Files checked: $total_files" +echo " Total violations: $violations_found" + +# Export violations for parent script if VIOLATIONS_OUTPUT is set +if [[ -n "${VIOLATIONS_OUTPUT:-}" ]]; then + if [[ $violations_found -gt 0 ]]; then + echo "VARIABLE_NAMING_VIOLATIONS_START" >> "$VIOLATIONS_OUTPUT" + # Re-process violations to write to file + grep -rn --include="*.dart" -E "(var|final|const)[[:space:]]+" lib/ | grep -v "static const" | while IFS=: read -r file line_num content; do + # Pattern 1: var/final/const variableName (without explicit type) + if [[ "$content" =~ (var|final|const)[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*= ]]; then + var_name="${BASH_REMATCH[2]}" + if ! is_lower_camel_case "$var_name"; then + suggested=$(to_lower_camel_case "$var_name") + echo "$file:$line_num: Variable '$var_name' should be lowerCamelCase (suggested: $suggested)" >> "$VIOLATIONS_OUTPUT" + fi + # Pattern 2: var/final/const Type variableName (with explicit type) + elif [[ "$content" =~ (var|final|const)[[:space:]]+[A-Za-z][A-Za-z0-9_\<\>\?,[:space:]]*[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]*= ]]; then + var_name="${BASH_REMATCH[2]}" + if ! is_lower_camel_case "$var_name"; then + suggested=$(to_lower_camel_case "$var_name") + echo "$file:$line_num: Variable '$var_name' should be lowerCamelCase (suggested: $suggested)" >> "$VIOLATIONS_OUTPUT" + fi + fi + done + echo "VARIABLE_NAMING_VIOLATIONS_END" >> "$VIOLATIONS_OUTPUT" + fi +fi + +if [[ $violations_found -gt 0 ]]; then + echo + echo "lowerCamelCase guidelines:" + echo " - Variables: myVariable, userName, isLoggedIn" + echo " - Functions: calculateTotal(), getUserData(), handleClick()" + echo " - Private members: _privateVariable, _helperFunction()" + + if [[ "$MODE" == "fix" ]]; then + echo "Auto-fix mode is experimental and may require manual review." + echo "Please verify changes before committing." + elif [[ "$MODE" == "check" ]]; then + exit 1 + else + echo "Run with 'fix' argument to see suggested corrections." + echo "Run with 'check' argument for CI/CD integration." + fi +fi + +if [[ $violations_found -gt 0 ]]; then + exit 1 +else + exit 0 +fi diff --git a/scripts/checking/check_very_long_conditions.sh b/scripts/checking/check_very_long_conditions.sh new file mode 100644 index 000000000..604fd6f7c --- /dev/null +++ b/scripts/checking/check_very_long_conditions.sh @@ -0,0 +1,198 @@ +#!/usr/bin/env bash +# check_very_long_conditions.sh - Check for overly long if statement conditions that should be split into separate variables. +# This script detects if statements with complex conditions spanning multiple lines or exceeding reasonable length limits. +# Long conditions make code harder to read and should be broken down into meaningful variable assignments. +# Usage: +# ./scripts/checking/check_very_long_conditions.sh -> check all files and report violations +# ./scripts/checking/check_very_long_conditions.sh check -> check mode (exits non-zero if violations found) + +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$script_dir/../.." && pwd)" +cd "$repo_root" + +# Defaults +MODE="report" + +# Parse arguments +for arg in "$@"; do + case "$arg" in + check) + MODE="check" + ;; + *) + echo "Unknown argument: $arg" + echo "Usage: $0 [check]" + exit 2 + ;; + esac +done + +violations_found=0 + +echo "Checking Dart files for overly long if statement conditions..." +echo "Scanning: lib/" +echo "Looking for complex if conditions that should be split into variables..." + +# Create temporary file for all violations +violations_temp=$(mktemp) + +# Find if statements and check them efficiently +# This approach is much faster as it processes files in bulk +find lib -name "*.dart" -exec grep -l "if[[:space:]]*(" {} \; | \ +xargs grep -Hn "if[[:space:]]*(" | \ +while IFS=: read -r file line_num line_content; do + # Skip commented lines - check if line starts with // after whitespace + if [[ "$line_content" =~ ^[[:space:]]*// ]]; then + continue + fi + + # Quick heuristic checks for potentially long conditions + if [[ ${#line_content} -gt 80 ]] || [[ "$line_content" == *"&&"* ]] || [[ "$line_content" == *"||"* ]]; then + # Extract condition between if( and matching ) - handle nested parentheses + if [[ "$line_content" =~ if[[:space:]]*\( ]]; then + # Find the matching closing parenthesis by counting parentheses + temp_line="$line_content" + # Remove everything up to and including "if (" + temp_line=${temp_line#*if*\(} + + # Extract condition by counting parentheses + paren_count=1 + condition="" + for (( i=0; i<${#temp_line}; i++ )); do + char="${temp_line:$i:1}" + if [[ "$char" == "(" ]]; then + ((paren_count++)) + elif [[ "$char" == ")" ]]; then + ((paren_count--)) + if [[ $paren_count -eq 0 ]]; then + break + fi + fi + condition+="$char" + done + + # Clean up any whitespace + condition=$(echo "$condition" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + + # Check if it's a long/complex condition + should_flag=false + + # Check for complex expressions that should be refactored + + # Simple condition with only variable names and logical operators should NOT be flagged + # Pattern: variable && variable or variable || variable (no dots, no method calls, etc.) + simple_condition=false + if [[ "$condition" =~ ^[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*(\|\||&&)[[:space:]]*[!]?[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then + simple_condition=true + fi + + # Don't flag simple conditions + if [[ "$simple_condition" == true ]]; then + should_flag=false + # Don't flag simple string contains checks like e.toString().contains("401") + elif [[ "$condition" =~ ^[a-zA-Z_][a-zA-Z0-9_]*\.toString\(\)\.contains\([^\)]+\)$ ]]; then + should_flag=false + # Flag complex conditions + else + # Flag if condition contains property access (dots) + if [[ "$condition" == *"."* ]]; then + should_flag=true + fi + + # Flag if condition contains method calls (parentheses after words) + if [[ "$condition" == *"("* ]]; then + should_flag=true + fi + + # Flag if condition contains null coalescing operator + if [[ "$condition" == *"??"* ]]; then + should_flag=true + fi + + # Flag if condition is very long (regardless of content) + if [[ ${#condition} -gt 100 ]]; then + should_flag=true + fi + + # Flag if has logical operators with non-simple patterns + if [[ "$condition" == *"&&"* ]] || [[ "$condition" == *"||"* ]]; then + if [[ ${#condition} -gt 60 ]]; then + should_flag=true + fi + fi + fi + + if [[ "$should_flag" == true ]]; then + # Export violation for parent script if needed + if [[ -n "${VIOLATIONS_OUTPUT:-}" ]]; then + echo "$file:$line_num: If condition is too long and should be split into variables" >> "$violations_temp" + else + echo "$file:$line_num: If condition is too long and should be split into variables" | tee -a "$violations_temp" + fi + + # Truncate condition for display + display_condition="$condition" + if [[ ${#display_condition} -gt 100 ]]; then + display_condition="${display_condition:0:100}..." + fi + echo " Condition: $display_condition" + fi + fi + fi +done + +# Count violations +violations_found=$(wc -l < "$violations_temp" 2>/dev/null || echo "0") +total_files=$(find lib -name "*.dart" -type f | wc -l) + +echo "" +echo "Summary:" +echo " Files checked: $total_files" +echo " Total violations: $violations_found" + +# Export violations markers for parent script if needed +if [[ -n "${VIOLATIONS_OUTPUT:-}" && $violations_found -gt 0 ]]; then + echo "LONG_CONDITIONS_VIOLATIONS_START" >> "$VIOLATIONS_OUTPUT" + cat "$violations_temp" >> "$VIOLATIONS_OUTPUT" + echo "LONG_CONDITIONS_VIOLATIONS_END" >> "$VIOLATIONS_OUTPUT" +fi + +# Clean up +rm -f "$violations_temp" + +if [[ $violations_found -gt 0 ]]; then + echo + echo "Guidelines for if statement conditions:" + echo " - BAD:" + echo " if (userDataProvider.isLoggedIn &&" + echo " (userDataProvider.userProfileModel.classifications?.staff ?? false)) {" + echo " // action" + echo " }" + echo + echo " + GOOD:" + echo " var isLoggedIn = userDataProvider.isLoggedIn;" + echo " var isStaff = userDataProvider.userProfileModel.classifications?.staff ?? false;" + echo " if (isLoggedIn && isStaff) {" + echo " // action" + echo " }" + echo + echo "Benefits of splitting long conditions:" + echo " - Improved readability and maintainability" + echo " - Easier debugging and testing" + echo " - Better variable naming provides self-documentation" + echo " - Reduced cognitive complexity" + + if [[ "$MODE" == "check" ]]; then + exit 1 + else + echo "Run with 'check' argument for CI/CD integration." + fi +fi + +if [[ $violations_found -gt 0 ]]; then + exit 1 +else + exit 0 +fi diff --git a/scripts/cleanup_backups.sh b/scripts/cleanup_backups.sh new file mode 100644 index 000000000..7e95ab58c --- /dev/null +++ b/scripts/cleanup_backups.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# Remove all .bak backup files generated by auto-fix scripts +# Usage: +# ./scripts/cleanup_backups.sh +# ./scripts/cleanup_backups.sh --dry-run # Preview what would be deleted + +set -e + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$script_dir/.." && pwd)" +cd "$repo_root" + +DRY_RUN=false +if [[ "$1" == "--dry-run" ]]; then + DRY_RUN=true +fi + +echo "===============================" +echo "๐Ÿงน Cleaning up backup files" +echo "===============================" +echo + +# Find all .bak files in the repository +backup_files=$(find . -name "*.bak" -type f 2>/dev/null || true) + +if [[ -z "$backup_files" ]]; then + echo "โœ… No backup files found - repository is clean!" + exit 0 +fi + +echo "Found the following backup files:" +echo "$backup_files" | while IFS= read -r file; do + echo " $file" +done + +backup_count=$(echo "$backup_files" | wc -l) +echo +echo "Total backup files found: $backup_count" + +if [[ "$DRY_RUN" == "true" ]]; then + echo + echo "DRY RUN MODE - No files will be deleted" + echo "Run without --dry-run to actually remove these files" + exit 0 +fi + +echo +read -p "Are you sure you want to delete all these backup files? (y/N): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "โŒ Cleanup cancelled" + exit 1 +fi + +echo +echo "๐Ÿ—‘๏ธ Removing backup files..." + +deleted_count=0 +echo "$backup_files" | while IFS= read -r file; do + if [[ -f "$file" ]]; then + rm "$file" + echo " Deleted: $file" + deleted_count=$((deleted_count + 1)) + fi +done + +echo +echo "โœ… Cleanup completed!" +echo "๐Ÿ“Š Deleted $backup_count backup files" +echo "๐ŸŽฏ Repository is now clean of .bak files" diff --git a/scripts/styling/add_date_to_todo_comment.sh b/scripts/styling/add_date_to_todo_comment.sh new file mode 100644 index 000000000..c55ee1dbc --- /dev/null +++ b/scripts/styling/add_date_to_todo_comment.sh @@ -0,0 +1,128 @@ +#!/bin/bash +# Enforce TODO comments format: two slashes and include month/year date +# Usage: +# ./scripts/todo_date_check.sh [--fix] + +set -e + +MODE="check" +if [[ "$1" == "--fix" ]]; then + MODE="fix" +fi + +# Function to check if a TODO has proper date format +has_valid_date() { + local todo_text="$1" + + # Pattern 1: Full month name + year (e.g., "November 2025", "Dec 2024") + if [[ "$todo_text" =~ (January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[[:space:]]+20[0-9]{2} ]]; then + return 0 + fi + + # Pattern 2: Short month + 2-digit year (e.g., "Nov 25", "Dec 24") + if [[ "$todo_text" =~ (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[[:space:]]+[0-9]{2}[[:space:]] ]]; then + return 0 + fi + + # Pattern 3: Numeric month/year format (e.g., "11/2025", "12/24") + if [[ "$todo_text" =~ [0-9]{1,2}/20[0-9]{2} ]] || [[ "$todo_text" =~ [0-9]{1,2}/[0-9]{2}[[:space:]] ]]; then + return 0 + fi + + return 1 +} + +# Function to suggest a date format +suggest_date_format() { + local current_date=$(date "+%B %Y") + echo "$current_date" +} + +echo "Checking TODO comments for proper format (// with date)..." + +violations_found=0 + +# Create a temporary file to store results +temp_file=$(mktemp) + +# Find all TODO comments +grep -rn --include="*.dart" "TODO" lib/ > "$temp_file" 2>/dev/null || true + +# Process the results +while IFS=: read -r filepath line_number line_content; do + if [[ -n "$filepath" && -n "$line_number" && -n "$line_content" ]]; then + # Check if it's a TODO comment (not just TODO in string or other context) + if [[ "$line_content" =~ //.*TODO ]] || [[ "$line_content" =~ ///.*TODO ]]; then + + # Check if it uses three slashes (should use two) + if [[ "$line_content" =~ ///.*TODO ]]; then + echo " $filepath:$line_number: TODO should use // not ///" + echo " $line_content" + violations_found=$((violations_found + 1)) + + if [[ "$MODE" == "fix" ]]; then + # Actually fix changing /// to // + fixed_line=$(echo "$line_content" | sed 's|///\(.*TODO\)|//\1|') + echo " Fixing: $fixed_line" + + # Create backup and fix the line + cp "$filepath" "$filepath.bak" + awk -v ln="$line_number" -v new_line="$fixed_line" 'NR==ln {print new_line; next} {print}' "$filepath" > "$filepath.tmp" && mv "$filepath.tmp" "$filepath" + fi + continue + fi + + # Check if it has proper date format + if ! has_valid_date "$line_content"; then + echo " $filepath:$line_number: TODO missing date (month and year)" + echo " $line_content" + violations_found=$((violations_found + 1)) + + if [[ "$MODE" == "fix" ]]; then + # Actually add current date + suggested_date=$(suggest_date_format) + # Try to add date before any existing description + if [[ "$line_content" =~ TODO:[[:space:]]* ]]; then + fixed_line=$(echo "$line_content" | sed "s|TODO:[[:space:]]*|TODO: |" | sed "s|TODO: \(.*\)|TODO: \1 - $suggested_date|") + else + fixed_line=$(echo "$line_content" | sed "s|TODO\(.*\)|TODO\1 - $suggested_date|") + fi + echo " Fixing: $fixed_line" + + # Create backup and fix the line + cp "$filepath" "$filepath.bak" + awk -v ln="$line_number" -v new_line="$fixed_line" 'NR==ln {print new_line; next} {print}' "$filepath" > "$filepath.tmp" && mv "$filepath.tmp" "$filepath" + fi + fi + fi + fi +done < "$temp_file" + +# Clean up +rm -f "$temp_file" + +# Count total files scanned +total_files=$(find lib -type f -name "*.dart" | wc -l) + +echo +echo "TODO comment check complete." +echo "Files scanned: $total_files" +echo "Violations found: $violations_found" + +if [[ $violations_found -gt 0 ]]; then + if [[ "$MODE" == "fix" ]]; then + echo "Fixed $violations_found TODO comment violation(s)." + exit 0 + else + echo "TODO comments must use // (not ///) and include month/year date" + echo " GOOD: // TODO: Fix this issue - November 2025" + echo " GOOD: // TODO: Update code Nov 25" + echo " GOOD: // TODO: Refactor - 11/2025" + echo " BAD: /// TODO: Fix this (wrong slashes)" + echo " BAD: // TODO: Fix this (missing date)" + exit 1 + fi +else + echo "All TODO comments follow proper format" + exit 0 +fi diff --git a/scripts/styling/add_space_after_comment_slashes.sh b/scripts/styling/add_space_after_comment_slashes.sh new file mode 100644 index 000000000..b4dce60b9 --- /dev/null +++ b/scripts/styling/add_space_after_comment_slashes.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# Enforce proper spacing after double slashes in comments +# Usage: +# ./scripts/comment_space.sh [--fix] + +set -e + +MODE="check" +if [[ "$1" == "--fix" ]]; then + MODE="fix" +fi + +echo "Checking for proper spacing after // in comments..." + +violations_found=0 + +# Create a temporary file to store results +temp_file=$(mktemp) + +# Find all comments that don't have space after // (but aren't URLs or special cases) +# Use a simple approach: look for // followed immediately by alphanumeric characters +grep -rn --include="*.dart" "//[a-zA-Z0-9]" lib/ > "$temp_file" 2>/dev/null || true + +# Process the results +while IFS=: read -r filepath line_number line_content; do + if [[ -n "$filepath" && -n "$line_number" && -n "$line_content" ]]; then + # Skip URLs (http://, https://, ftp://, etc.) + if [[ "$line_content" =~ https?://|ftp://|file:// ]]; then + continue + fi + + # Skip documentation comments (///) - these are handled differently + if [[ "$line_content" =~ /// ]]; then + continue + fi + + # Skip comment blocks that are intentionally formatted (like /////// separators) + if [[ "$line_content" =~ ////+ ]]; then + continue + fi + + # Skip single-line comment disabling (like //ignore or //TODO without space might be intentional) + # But still catch most cases + echo " $filepath:$line_number: Comment missing space after //" + echo " $line_content" + violations_found=$((violations_found + 1)) + + if [[ "$MODE" == "fix" ]]; then + # Actually fix the file by adding space after // + fixed_line=$(echo "$line_content" | sed 's|//\([^ /]\)|// \1|g') + echo " Fixing: $fixed_line" + + # Use a simpler approach to fix the line + # Create backup first + cp "$filepath" "$filepath.bak" + + # Use awk to fix the specific line + awk -v ln="$line_number" -v new_line="$fixed_line" 'NR==ln {print new_line; next} {print}' "$filepath" > "$filepath.tmp" && mv "$filepath.tmp" "$filepath" + fi + fi +done < "$temp_file" + +# Clean up +rm -f "$temp_file" + +# Count total files scanned +total_files=$(find lib -type f -name "*.dart" | wc -l) + +echo +echo "Comment spacing check complete." +echo "Files scanned: $total_files" +echo "Violations found: $violations_found" + +if [[ $violations_found -gt 0 ]]; then + if [[ "$MODE" == "fix" ]]; then + echo "Fixed $violations_found comment spacing violation(s)." + exit 0 + else + echo "Comments should have a space after //" + echo " GOOD: // This is a proper comment" + echo " BAD: //This comment is missing a space" + exit 1 + fi +else + echo "All comments have proper spacing after //" + exit 0 +fi diff --git a/scripts/styling/one_line_function_fix.sh b/scripts/styling/one_line_function_fix.sh new file mode 100644 index 000000000..0e658b6b0 --- /dev/null +++ b/scripts/styling/one_line_function_fix.sh @@ -0,0 +1,145 @@ +#!/bin/bash +# Enforce arrow syntax for one-line functions +# Usage: +# ./scripts/styling/one_line_function_fix.sh [--fix] [files...] + +set -e + +MODE="check" +FILES_TO_PROCESS=() + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --fix) + MODE="fix" + shift + ;; + *) + FILES_TO_PROCESS+=("$1") + shift + ;; + esac +done + +# If no files specified, find all Dart files in lib +if [[ ${#FILES_TO_PROCESS[@]} -eq 0 ]]; then + echo "No specific files provided, scanning lib directory..." + mapfile -t FILES_TO_PROCESS < <(find lib -type f -name "*.dart") +else + echo "Processing specific files: ${FILES_TO_PROCESS[*]}" +fi + +echo "Checking for functions that should use arrow syntax..." + +violations_found=0 + +echo "" +echo "Checking for multi-line functions that could be single line with arrow syntax..." + +# Process each specified Dart file +while IFS= read -r filepath; do + if [[ ! -f "$filepath" ]]; then + continue + fi + + # Use a simpler approach - read the file into memory and process line by line + file_modified=false + declare -a file_lines + line_num=0 + + # Read entire file into array + while IFS= read -r line; do + line_num=$((line_num + 1)) + file_lines[$line_num]="$line" + done < "$filepath" + + # Process lines to find function declarations + i=1 + while [[ $i -le $line_num ]]; do + current_line="${file_lines[$i]}" + + # Check for function declaration ending with ) { + # This should match: returnType functionName(...) { but NOT if (...) {, while (...) {, etc. + if [[ "$current_line" =~ ^[[:space:]]*[a-zA-Z_]+.*[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*\(.*\)[[:space:]]*\{[[:space:]]*$ ]] && \ + [[ ! "$current_line" =~ ^[[:space:]]*(if|while|for|switch|try)[[:space:]]*\( ]]; then + next_line_idx=$((i + 1)) + closing_brace_idx=$((i + 2)) + + # Check if next line exists and is not empty/comment + if [[ $next_line_idx -le $line_num && $closing_brace_idx -le $line_num ]]; then + next_line="${file_lines[$next_line_idx]}" + closing_line="${file_lines[$closing_brace_idx]}" + + # Check if next line is a return statement and the line after is just } + if [[ "$next_line" =~ ^[[:space:]]*return[[:space:]]+.*\;[[:space:]]*$ ]] && \ + [[ "$closing_line" =~ ^[[:space:]]*\}[[:space:]]*$ ]]; then + + echo "$filepath:$i: Function with single return should use arrow syntax" + violations_found=$((violations_found + 1)) + + if [[ "$MODE" == "fix" ]]; then + echo " Fixing: Converting multi-line function to arrow syntax" + + # Extract function signature (remove trailing { and spaces) + function_sig=$(echo "$current_line" | sed 's/[[:space:]]*{[[:space:]]*$//') + + # Extract return value (remove 'return' and semicolon, keep spaces around operators) + return_value=$(echo "$next_line" | sed 's/^[[:space:]]*return[[:space:]]*//' | sed 's/[[:space:]]*;[[:space:]]*$//') + + # Create fixed line with arrow syntax + fixed_line="$function_sig => $return_value;" + + # Mark file as modified + file_modified=true + + # Replace the three lines with one + file_lines[$i]="$fixed_line" + unset file_lines[$next_line_idx] + unset file_lines[$closing_brace_idx] + + echo " Fixed: $fixed_line" + fi + + # Skip the processed lines + i=$((closing_brace_idx + 1)) + continue + fi + fi + fi + + i=$((i + 1)) + done + + # Write back modified file if changes were made + if [[ "$file_modified" == "true" ]]; then + # Create backup + cp "$filepath" "$filepath.bak" + + # Write modified content + > "$filepath" # Clear file + for ((j=1; j<=line_num; j++)); do + if [[ -n "${file_lines[$j]+x}" ]]; then # Check if element exists + echo "${file_lines[$j]}" >> "$filepath" + fi + done + fi + + unset file_lines +done < <(printf '%s\n' "${FILES_TO_PROCESS[@]}") + +# Summary +echo "" +if [[ $violations_found -gt 0 ]]; then + if [[ "$MODE" == "fix" ]]; then + echo "Fixed $violations_found function arrow syntax violation(s)." + exit 0 + else + echo "Found $violations_found violation(s) of function arrow syntax convention." + echo "Run with --fix to see suggestions." + exit 1 + fi +else + echo "No violations found! All functions follow the arrow syntax convention." + exit 0 +fi diff --git a/scripts/styling/one_line_if_statement_fix.sh b/scripts/styling/one_line_if_statement_fix.sh new file mode 100644 index 000000000..ac7ef15fd --- /dev/null +++ b/scripts/styling/one_line_if_statement_fix.sh @@ -0,0 +1,124 @@ +#!/bin/bash +# Enforce no curly braces for one-line if statements +# Usage: +# ./scripts/no_braces_one_line_if.sh [--fix] + +set -e + +MODE="check" +if [[ "$1" == "--fix" ]]; then + MODE="fix" +fi + +echo "Checking for unnecessary braces in one-line if statements..." + +violations_found=0 + +echo "" +echo "Checking for multi-line if statements that could be single line..." + +# Process each Dart file to find and fix multi-line if statements that should be single line +while IFS= read -r filepath; do + if [[ ! -f "$filepath" ]]; then + continue + fi + + # Use a simpler approach - read the file into memory and process line by line + file_modified=false + declare -a file_lines + line_num=0 + + # Read entire file into array + while IFS= read -r line; do + line_num=$((line_num + 1)) + file_lines[$line_num]="$line" + done < "$filepath" + + # Process lines to find if statements + i=1 + while [[ $i -le $line_num ]]; do + current_line="${file_lines[$i]}" + + # Check for if statement opening: if (...) { + if [[ "$current_line" =~ ^[[:space:]]*if[[:space:]]*\(.*\)[[:space:]]*\{[[:space:]]*$ ]]; then + next_line_idx=$((i + 1)) + closing_brace_idx=$((i + 2)) + + # Check if next line exists and is not empty/comment + if [[ $next_line_idx -le $line_num && $closing_brace_idx -le $line_num ]]; then + next_line="${file_lines[$next_line_idx]}" + closing_line="${file_lines[$closing_brace_idx]}" + + # Check if next line is a statement and the line after is just } + if [[ ! "$next_line" =~ ^[[:space:]]*$ ]] && \ + [[ ! "$next_line" =~ ^[[:space:]]*// ]] && \ + [[ "$closing_line" =~ ^[[:space:]]*\}[[:space:]]*$ ]]; then + + echo "$filepath:$i: One-line if statement should not use braces" + violations_found=$((violations_found + 1)) + + if [[ "$MODE" == "fix" ]]; then + echo " Fixing: Converting multi-line if to single line" + + # Extract condition (remove trailing { and spaces) + condition=$(echo "$current_line" | sed 's/[[:space:]]*{[[:space:]]*$//') + + # Extract statement (remove leading/trailing spaces but keep semicolon) + statement=$(echo "$next_line" | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//') + + # Create fixed line + fixed_line="$condition $statement" + + # Mark file as modified + file_modified=true + + # Replace the three lines with one + file_lines[$i]="$fixed_line" + unset file_lines[$next_line_idx] + unset file_lines[$closing_brace_idx] + + echo " Fixed: $fixed_line" + fi + + # Skip the processed lines + i=$((closing_brace_idx + 1)) + continue + fi + fi + fi + + i=$((i + 1)) + done + + # Write back modified file if changes were made + if [[ "$file_modified" == "true" ]]; then + # Create backup + cp "$filepath" "$filepath.bak" + + # Write modified content + > "$filepath" # Clear file + for ((j=1; j<=line_num; j++)); do + if [[ -n "${file_lines[$j]+x}" ]]; then # Check if element exists + echo "${file_lines[$j]}" >> "$filepath" + fi + done + fi + + unset file_lines +done < <(find lib -type f -name "*.dart") + +# Summary +echo "" +if [[ $violations_found -gt 0 ]]; then + if [[ "$MODE" == "fix" ]]; then + echo "Fixed $violations_found one-line if brace violation(s)." + exit 0 + else + echo "Found $violations_found violation(s) of one-line if brace convention." + echo "Run with --fix to see suggestions." + exit 1 + fi +else + echo "No violations found! All if statements follow the convention." + exit 0 +fi