From 16bff634b54c86f52dc3c4a57bfd1ad3d56428fa Mon Sep 17 00:00:00 2001 From: alvocool Date: Mon, 27 Jul 2026 09:11:17 +0300 Subject: [PATCH] Initial commit: Grounded Flutter frontend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A to-do app that doesn't believe you — an enforcement layer rather than a neutral ledger. Architecture ported from Autoreceptives/Frontend/Receptive: stacked MVVM with the mandatory 4-file screen pattern, one ParentViewModel owning the loading / network / error overlays and the handleError decision tree, one AppDataManager gateway, dio comms carrying the three identity headers, secure storage with random-suffixed keys, and a single-chokepoint Navigator. Package root and Dart package name are both Grounded; org is nya. The enforcement engine, one unit per formula in utils/: - DebtEngine w(class) x severity(d) x decay(t), sublinear severity so old misses cannot swamp the score; abandonment 2x with 30-day decay immunity; late complete retains 30% - StandingEngine Good -> Warned -> Grounded -> Lockdown, derived not set; Grounded replaces home with the overdue queue - CapacityEngine blocks over-scheduling against p50 of historically completed minutes, with a learned per-category estimation multiplier - IntegrityEngine session integrity, weekly volume, plyometric contact ceiling and enforced recovery gaps - ExcuseAnalyser on-device excuse clustering plus the confrontation copy - GuardrailEngine distress detection and rationed amnesty - ToneEngine all enforcement copy, so the tone cap lives in one place CommitmentEvent is append-only and is the source of truth rather than the status field, which is what makes honest history and excuse analysis possible. Goals contain commitments via parentId, and a task can be run from a full-screen runner that derives elapsed time from wall-clock so screen-off cannot lose time. Backgrounding pauses the clock and is counted. The runner is mirrored into an ongoing notification, with alarm-class full-screen intents reserved for non-negotiables. Design language, fonts, icon and native splash are in place; Mason bricks are retargeted to this project and verified end-to-end. flutter analyze lib/ reports no errors. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Mfu2gQLSFN21YRBcU2NrTt --- .gitattributes | 27 + .gitignore | 209 +++ README.md | 94 ++ frontend/.env.example | 10 + frontend/.gitignore | 45 + frontend/.metadata | 39 + frontend/CLAUDE.md | 179 +++ frontend/README.md | 17 + frontend/analysis_options.yaml | 24 + frontend/android/.gitignore | 14 + frontend/android/app/build.gradle.kts | 45 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 69 + .../main/kotlin/nya/grounded/MainActivity.kt | 5 + .../res/drawable-hdpi/android12splash.png | Bin 0 -> 9673 bytes .../drawable-hdpi/ic_launcher_foreground.png | Bin 0 -> 3781 bytes .../app/src/main/res/drawable-hdpi/splash.png | Bin 0 -> 12481 bytes .../res/drawable-mdpi/android12splash.png | Bin 0 -> 5784 bytes .../drawable-mdpi/ic_launcher_foreground.png | Bin 0 -> 2328 bytes .../app/src/main/res/drawable-mdpi/splash.png | Bin 0 -> 7513 bytes .../drawable-night-hdpi/android12splash.png | Bin 0 -> 9673 bytes .../drawable-night-mdpi/android12splash.png | Bin 0 -> 5784 bytes .../drawable-night-xhdpi/android12splash.png | Bin 0 -> 13033 bytes .../drawable-night-xxhdpi/android12splash.png | Bin 0 -> 23073 bytes .../android12splash.png | Bin 0 -> 30894 bytes .../src/main/res/drawable-v21/background.png | Bin 0 -> 69 bytes .../res/drawable-v21/launch_background.xml | 9 + .../res/drawable-xhdpi/android12splash.png | Bin 0 -> 13033 bytes .../drawable-xhdpi/ic_launcher_foreground.png | Bin 0 -> 4968 bytes .../src/main/res/drawable-xhdpi/splash.png | Bin 0 -> 16720 bytes .../res/drawable-xxhdpi/android12splash.png | Bin 0 -> 23073 bytes .../ic_launcher_foreground.png | Bin 0 -> 7777 bytes .../src/main/res/drawable-xxhdpi/splash.png | Bin 0 -> 30002 bytes .../res/drawable-xxxhdpi/android12splash.png | Bin 0 -> 30894 bytes .../ic_launcher_foreground.png | Bin 0 -> 11109 bytes .../src/main/res/drawable-xxxhdpi/splash.png | Bin 0 -> 39722 bytes .../app/src/main/res/drawable/background.png | Bin 0 -> 69 bytes .../main/res/drawable/launch_background.xml | 9 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 9 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1975 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1183 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 2405 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 3923 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 4920 bytes .../src/main/res/values-night-v31/styles.xml | 22 + .../app/src/main/res/values-night/styles.xml | 22 + .../app/src/main/res/values-v31/styles.xml | 22 + .../app/src/main/res/values/colors.xml | 4 + .../app/src/main/res/values/styles.xml | 22 + .../app/src/profile/AndroidManifest.xml | 7 + frontend/android/build.gradle.kts | 24 + frontend/android/gradle.properties | 6 + .../gradle/wrapper/gradle-wrapper.properties | 5 + frontend/android/settings.gradle.kts | 26 + frontend/assets/icons/icon.png | Bin 0 -> 40360 bytes frontend/assets/icons/icon_foreground.png | Bin 0 -> 27193 bytes frontend/assets/icons/logo.svg | 17 + frontend/assets/icons/splash.png | Bin 0 -> 35932 bytes frontend/bricks/api_endpoint/CHANGELOG.md | 3 + frontend/bricks/api_endpoint/LICENSE | 1 + frontend/bricks/api_endpoint/README.md | 83 ++ frontend/bricks/api_endpoint/brick.yaml | 25 + .../bricks/api_endpoint/hooks/post_gen.dart | 146 ++ .../bricks/api_endpoint/hooks/pubspec.yaml | 10 + frontend/bricks/api_endpoint/setup_brick.sh | 20 + frontend/bricks/internal_memory/CHANGELOG.md | 3 + frontend/bricks/internal_memory/LICENSE | 1 + frontend/bricks/internal_memory/README.md | 92 ++ frontend/bricks/internal_memory/brick.yaml | 11 + .../internal_memory/hooks/post_gen.dart | 186 +++ .../bricks/internal_memory/hooks/pubspec.yaml | 8 + frontend/bricks/mvvc_template/CHANGELOG.md | 3 + frontend/bricks/mvvc_template/LICENSE | 1 + frontend/bricks/mvvc_template/README.md | 27 + .../Connect{{screen}}.dart | 2 + .../View{{screen}}.dart | 14 + .../{{screen.lowerCase()}}/{{screen}}.dart | 11 + .../{{screen}}State.dart | 77 + frontend/bricks/mvvc_template/brick.yaml | 38 + frontend/design.md | 55 + frontend/fonts/LICENSE-GeneralSans.txt | 57 + frontend/fonts/bold.ttf | Bin 0 -> 65960 bytes frontend/fonts/light.ttf | Bin 0 -> 66692 bytes frontend/fonts/medium.ttf | Bin 0 -> 66128 bytes frontend/fonts/regular.ttf | Bin 0 -> 66520 bytes frontend/ios/.gitignore | 34 + frontend/ios/Flutter/AppFrameworkInfo.plist | 24 + frontend/ios/Flutter/Debug.xcconfig | 1 + frontend/ios/Flutter/Release.xcconfig | 1 + frontend/ios/Runner.xcodeproj/project.pbxproj | 644 +++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 119 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + frontend/ios/Runner/AppDelegate.swift | 16 + .../AppIcon.appiconset/Contents.json | 1 + .../Icon-App-1024x1024@1x.png | Bin 0 -> 40572 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 473 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 934 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1418 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 683 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1414 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 2105 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 934 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1866 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2902 bytes .../AppIcon.appiconset/Icon-App-50x50@1x.png | Bin 0 -> 1171 bytes .../AppIcon.appiconset/Icon-App-50x50@2x.png | Bin 0 -> 2440 bytes .../AppIcon.appiconset/Icon-App-57x57@1x.png | Bin 0 -> 1340 bytes .../AppIcon.appiconset/Icon-App-57x57@2x.png | Bin 0 -> 2750 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2902 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 4507 bytes .../AppIcon.appiconset/Icon-App-72x72@1x.png | Bin 0 -> 1760 bytes .../AppIcon.appiconset/Icon-App-72x72@2x.png | Bin 0 -> 3562 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1909 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3862 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 4165 bytes .../LaunchBackground.imageset/Contents.json | 21 + .../LaunchBackground.imageset/background.png | Bin 0 -> 69 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 7513 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 16720 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 30002 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 44 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + frontend/ios/Runner/Info.plist | 72 + frontend/ios/Runner/Runner-Bridging-Header.h | 1 + frontend/ios/Runner/SceneDelegate.swift | 6 + frontend/ios/RunnerTests/RunnerTests.swift | 12 + .../about/external/data/Commitment.dart | 129 ++ .../about/external/data/CommitmentEvent.dart | 53 + .../about/external/data/DebtEntry.dart | 46 + .../about/external/data/ExcuseCluster.dart | 72 + .../external/data/ExercisePrescription.dart | 91 ++ .../Grounded/about/external/data/Goal.dart | 110 ++ .../about/external/data/GroundedError.dart | 45 + .../Grounded/about/external/data/Habit.dart | 73 + .../about/external/data/LiveSession.dart | 118 ++ .../Grounded/about/external/data/Program.dart | 66 + .../about/external/data/ReportCard.dart | 192 +++ .../about/external/data/ResponseState.dart | 1 + .../about/external/data/RoutineChain.dart | 97 ++ .../about/external/data/SessionLog.dart | 105 ++ .../about/external/data/SessionTemplate.dart | 53 + .../Grounded/about/external/data/SetLog.dart | 76 + .../about/external/data/Severity.dart | 1 + .../about/external/data/StandingChange.dart | 41 + .../about/external/data/SystemResponse.dart | 40 + .../pages/request/CommitmentsRequest.dart | 21 + .../data/pages/request/HistoryRequest.dart | 14 + .../data/pages/request/PageAndSort.dart | 17 + .../external/data/pages/request/Pageable.dart | 20 + .../external/data/pages/request/Sort.dart | 14 + .../pages/response/CommitmentEventPage.dart | 47 + .../data/pages/response/CommitmentPage.dart | 47 + .../data/pages/response/SessionLogPage.dart | 47 + .../external/initial/AbandonRequest.dart | 16 + .../external/initial/AmnestyRequest.dart | 13 + .../external/initial/CommitmentRequest.dart | 58 + .../external/initial/CompletionRequest.dart | 38 + .../external/initial/DeferralRequest.dart | 27 + .../about/external/initial/DeviceRequest.dart | 25 + .../about/external/initial/GoalRequest.dart | 41 + .../about/external/initial/IdRequest.dart | 11 + .../about/external/initial/LoginData.dart | 14 + .../external/initial/ReportCardRequest.dart | 14 + .../external/initial/SessionLogRequest.dart | 45 + .../external/initial/SickModeRequest.dart | 18 + .../about/external/initial/ToneRequest.dart | 11 + .../internal/application/CapacityProfile.dart | 57 + .../internal/application/CommitmentClass.dart | 44 + .../application/CommitmentStatus.dart | 18 + .../internal/application/CommitmentType.dart | 10 + .../internal/application/DialogData.dart | 7 + .../internal/application/EnergyCost.dart | 10 + .../internal/application/EscalationTier.dart | 11 + .../about/internal/application/EventType.dart | 20 + .../internal/application/MeDescription.dart | 27 + .../internal/application/NavigatorType.dart | 1 + .../application/NotificationType.dart | 1 + .../about/internal/application/Pair.dart | 7 + .../internal/application/ProgressionRule.dart | 42 + .../about/internal/application/ProofType.dart | 46 + .../about/internal/application/Standing.dart | 35 + .../about/internal/application/TextType.dart | 6 + .../about/internal/application/Token.dart | 34 + .../about/internal/application/ToneLevel.dart | 23 + .../internal/application/UserDetails.dart | 57 + .../internal/file/ConnectFileStorage.dart | 13 + .../about/internal/file/FileStorage.dart | 53 + frontend/lib/Grounded/comms/Comms.dart | 508 +++++++ .../lib/Grounded/comms/CommsDirections.dart | 174 +++ frontend/lib/Grounded/comms/ConnectComms.dart | 145 ++ frontend/lib/Grounded/configs/Env.dart | 7 + frontend/lib/Grounded/configs/Navigator.dart | 61 + .../configs/NotificationServiceConfig.dart | 204 +++ frontend/lib/Grounded/designs/Component.dart | 202 +++ frontend/lib/Grounded/designs/Responsive.dart | 53 + frontend/lib/Grounded/designs/Shell.dart | 287 ++++ .../lib/Grounded/designs/buttons/Buttons.dart | 178 +++ .../Grounded/designs/input/InputFields.dart | 206 +++ frontend/lib/Grounded/designs/text/Text.dart | 39 + .../Grounded/informatics/AppDataManager.dart | 362 +++++ .../lib/Grounded/informatics/DataManager.dart | 6 + .../memory/ConnectInternalMemory.dart | 65 + .../lib/Grounded/memory/InternalMemory.dart | 270 ++++ .../see/commitment/ConnectNewCommitment.dart | 12 + .../see/commitment/NewCommitment.dart | 10 + .../see/commitment/NewCommitmentState.dart | 552 ++++++++ .../see/commitment/ViewNewCommitment.dart | 93 ++ .../see/excuse/ConnectExcuseReport.dart | 5 + .../lib/Grounded/see/excuse/ExcuseReport.dart | 10 + .../see/excuse/ExcuseReportState.dart | 216 +++ .../Grounded/see/excuse/ViewExcuseReport.dart | 37 + .../Grounded/see/goal/ConnectGoalDetail.dart | 9 + .../lib/Grounded/see/goal/ConnectGoals.dart | 7 + .../lib/Grounded/see/goal/GoalDetail.dart | 13 + .../Grounded/see/goal/GoalDetailState.dart | 310 ++++ frontend/lib/Grounded/see/goal/Goals.dart | 10 + .../lib/Grounded/see/goal/GoalsState.dart | 311 +++++ .../lib/Grounded/see/goal/ViewGoalDetail.dart | 37 + frontend/lib/Grounded/see/goal/ViewGoals.dart | 51 + .../lib/Grounded/see/home/ConnectHome.dart | 24 + frontend/lib/Grounded/see/home/Home.dart | 10 + frontend/lib/Grounded/see/home/HomeState.dart | 696 +++++++++ frontend/lib/Grounded/see/home/ViewHome.dart | 164 +++ .../Grounded/see/live/ConnectLiveTask.dart | 8 + frontend/lib/Grounded/see/live/LiveTask.dart | 18 + .../lib/Grounded/see/live/LiveTaskState.dart | 456 ++++++ .../lib/Grounded/see/live/ViewLiveTask.dart | 75 + .../lib/Grounded/see/login/ConnectLogin.dart | 5 + frontend/lib/Grounded/see/login/Login.dart | 10 + .../lib/Grounded/see/login/LoginState.dart | 187 +++ .../lib/Grounded/see/login/ViewLogin.dart | 39 + .../see/overdue/ConnectOverdueQueue.dart | 16 + .../Grounded/see/overdue/OverdueQueue.dart | 10 + .../see/overdue/OverdueQueueState.dart | 691 +++++++++ .../see/overdue/ViewOverdueQueue.dart | 190 +++ .../Grounded/see/parent/ParentViewModel.dart | 652 +++++++++ .../reportcard/ConnectReportCardScreen.dart | 5 + .../see/reportcard/ReportCardScreen.dart | 10 + .../see/reportcard/ReportCardScreenState.dart | 374 +++++ .../see/reportcard/ViewReportCardScreen.dart | 37 + .../see/settings/ConnectSettings.dart | 11 + .../lib/Grounded/see/settings/Settings.dart | 10 + .../Grounded/see/settings/SettingsState.dart | 344 +++++ .../Grounded/see/settings/ViewSettings.dart | 91 ++ .../Grounded/see/splash/ConnectSplash.dart | 7 + frontend/lib/Grounded/see/splash/Splash.dart | 10 + .../lib/Grounded/see/splash/SplashState.dart | 124 ++ .../lib/Grounded/see/splash/ViewSplash.dart | 29 + .../sessionexpired/ConnectSessionExpired.dart | 3 + .../system/sessionexpired/SessionExpired.dart | 10 + .../sessionexpired/SessionExpiredState.dart | 117 ++ .../sessionexpired/ViewSessionExpired.dart | 18 + .../see/system/updateme/ConnectUpdateMe.dart | 3 + .../see/system/updateme/UpdateMe.dart | 10 + .../see/system/updateme/UpdateMeState.dart | 116 ++ .../see/system/updateme/ViewUpdateMe.dart | 12 + .../see/training/ConnectTraining.dart | 16 + .../lib/Grounded/see/training/Training.dart | 10 + .../Grounded/see/training/TrainingState.dart | 350 +++++ .../Grounded/see/training/ViewTraining.dart | 129 ++ .../lib/Grounded/utils/CapacityEngine.dart | 134 ++ frontend/lib/Grounded/utils/Colors.dart | 115 ++ frontend/lib/Grounded/utils/CommonUtils.dart | 188 +++ frontend/lib/Grounded/utils/DebtEngine.dart | 159 +++ .../lib/Grounded/utils/ExcuseAnalyser.dart | 211 +++ .../lib/Grounded/utils/GuardrailEngine.dart | 44 + frontend/lib/Grounded/utils/Images.dart | 12 + .../lib/Grounded/utils/IntegrityEngine.dart | 191 +++ .../lib/Grounded/utils/ObjectConvertors.dart | 116 ++ .../lib/Grounded/utils/StandingEngine.dart | 91 ++ frontend/lib/Grounded/utils/Thresholds.dart | 39 + frontend/lib/Grounded/utils/ToneEngine.dart | 117 ++ frontend/lib/Grounded/utils/Validators.dart | 80 ++ frontend/lib/main.dart | 58 + frontend/mason-lock.json | 1 + frontend/mason.yaml | 7 + frontend/pubspec.lock | 1242 +++++++++++++++++ frontend/pubspec.yaml | 132 ++ frontend/web/favicon.png | Bin 0 -> 390 bytes frontend/web/icons/Icon-192.png | Bin 0 -> 4920 bytes frontend/web/icons/Icon-512.png | Bin 0 -> 16268 bytes frontend/web/icons/Icon-maskable-192.png | Bin 0 -> 4920 bytes frontend/web/icons/Icon-maskable-512.png | Bin 0 -> 16268 bytes frontend/web/index.html | 116 ++ frontend/web/manifest.json | 35 + frontend/web/splash/img/dark-1x.png | Bin 0 -> 7513 bytes frontend/web/splash/img/dark-2x.png | Bin 0 -> 16720 bytes frontend/web/splash/img/dark-3x.png | Bin 0 -> 30002 bytes frontend/web/splash/img/dark-4x.png | Bin 0 -> 39722 bytes frontend/web/splash/img/light-1x.png | Bin 0 -> 7513 bytes frontend/web/splash/img/light-2x.png | Bin 0 -> 16720 bytes frontend/web/splash/img/light-3x.png | Bin 0 -> 30002 bytes frontend/web/splash/img/light-4x.png | Bin 0 -> 39722 bytes frontend/windows/.gitignore | 17 + frontend/windows/CMakeLists.txt | 108 ++ frontend/windows/flutter/CMakeLists.txt | 109 ++ frontend/windows/runner/CMakeLists.txt | 40 + frontend/windows/runner/Runner.rc | 121 ++ frontend/windows/runner/flutter_window.cpp | 71 + frontend/windows/runner/flutter_window.h | 33 + frontend/windows/runner/main.cpp | 43 + frontend/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 6730 bytes frontend/windows/runner/runner.exe.manifest | 14 + frontend/windows/runner/utils.cpp | 69 + frontend/windows/runner/utils.h | 19 + frontend/windows/runner/win32_window.cpp | 288 ++++ frontend/windows/runner/win32_window.h | 102 ++ 315 files changed, 19132 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README.md create mode 100644 frontend/.env.example create mode 100644 frontend/.gitignore create mode 100644 frontend/.metadata create mode 100644 frontend/CLAUDE.md create mode 100644 frontend/README.md create mode 100644 frontend/analysis_options.yaml create mode 100644 frontend/android/.gitignore create mode 100644 frontend/android/app/build.gradle.kts create mode 100644 frontend/android/app/src/debug/AndroidManifest.xml create mode 100644 frontend/android/app/src/main/AndroidManifest.xml create mode 100644 frontend/android/app/src/main/kotlin/nya/grounded/MainActivity.kt create mode 100644 frontend/android/app/src/main/res/drawable-hdpi/android12splash.png create mode 100644 frontend/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png create mode 100644 frontend/android/app/src/main/res/drawable-hdpi/splash.png create mode 100644 frontend/android/app/src/main/res/drawable-mdpi/android12splash.png create mode 100644 frontend/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png create mode 100644 frontend/android/app/src/main/res/drawable-mdpi/splash.png create mode 100644 frontend/android/app/src/main/res/drawable-night-hdpi/android12splash.png create mode 100644 frontend/android/app/src/main/res/drawable-night-mdpi/android12splash.png create mode 100644 frontend/android/app/src/main/res/drawable-night-xhdpi/android12splash.png create mode 100644 frontend/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png create mode 100644 frontend/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png create mode 100644 frontend/android/app/src/main/res/drawable-v21/background.png create mode 100644 frontend/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 frontend/android/app/src/main/res/drawable-xhdpi/android12splash.png create mode 100644 frontend/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png create mode 100644 frontend/android/app/src/main/res/drawable-xhdpi/splash.png create mode 100644 frontend/android/app/src/main/res/drawable-xxhdpi/android12splash.png create mode 100644 frontend/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png create mode 100644 frontend/android/app/src/main/res/drawable-xxhdpi/splash.png create mode 100644 frontend/android/app/src/main/res/drawable-xxxhdpi/android12splash.png create mode 100644 frontend/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png create mode 100644 frontend/android/app/src/main/res/drawable-xxxhdpi/splash.png create mode 100644 frontend/android/app/src/main/res/drawable/background.png create mode 100644 frontend/android/app/src/main/res/drawable/launch_background.xml create mode 100644 frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 frontend/android/app/src/main/res/values-night-v31/styles.xml create mode 100644 frontend/android/app/src/main/res/values-night/styles.xml create mode 100644 frontend/android/app/src/main/res/values-v31/styles.xml create mode 100644 frontend/android/app/src/main/res/values/colors.xml create mode 100644 frontend/android/app/src/main/res/values/styles.xml create mode 100644 frontend/android/app/src/profile/AndroidManifest.xml create mode 100644 frontend/android/build.gradle.kts create mode 100644 frontend/android/gradle.properties create mode 100644 frontend/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 frontend/android/settings.gradle.kts create mode 100644 frontend/assets/icons/icon.png create mode 100644 frontend/assets/icons/icon_foreground.png create mode 100644 frontend/assets/icons/logo.svg create mode 100644 frontend/assets/icons/splash.png create mode 100644 frontend/bricks/api_endpoint/CHANGELOG.md create mode 100644 frontend/bricks/api_endpoint/LICENSE create mode 100644 frontend/bricks/api_endpoint/README.md create mode 100644 frontend/bricks/api_endpoint/brick.yaml create mode 100644 frontend/bricks/api_endpoint/hooks/post_gen.dart create mode 100644 frontend/bricks/api_endpoint/hooks/pubspec.yaml create mode 100644 frontend/bricks/api_endpoint/setup_brick.sh create mode 100644 frontend/bricks/internal_memory/CHANGELOG.md create mode 100644 frontend/bricks/internal_memory/LICENSE create mode 100644 frontend/bricks/internal_memory/README.md create mode 100644 frontend/bricks/internal_memory/brick.yaml create mode 100644 frontend/bricks/internal_memory/hooks/post_gen.dart create mode 100644 frontend/bricks/internal_memory/hooks/pubspec.yaml create mode 100644 frontend/bricks/mvvc_template/CHANGELOG.md create mode 100644 frontend/bricks/mvvc_template/LICENSE create mode 100644 frontend/bricks/mvvc_template/README.md create mode 100644 frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/Connect{{screen}}.dart create mode 100644 frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/View{{screen}}.dart create mode 100644 frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/{{screen}}.dart create mode 100644 frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/{{screen}}State.dart create mode 100644 frontend/bricks/mvvc_template/brick.yaml create mode 100644 frontend/design.md create mode 100644 frontend/fonts/LICENSE-GeneralSans.txt create mode 100644 frontend/fonts/bold.ttf create mode 100644 frontend/fonts/light.ttf create mode 100644 frontend/fonts/medium.ttf create mode 100644 frontend/fonts/regular.ttf create mode 100644 frontend/ios/.gitignore create mode 100644 frontend/ios/Flutter/AppFrameworkInfo.plist create mode 100644 frontend/ios/Flutter/Debug.xcconfig create mode 100644 frontend/ios/Flutter/Release.xcconfig create mode 100644 frontend/ios/Runner.xcodeproj/project.pbxproj create mode 100644 frontend/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 frontend/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 frontend/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 frontend/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 frontend/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 frontend/ios/Runner/AppDelegate.swift create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json create mode 100644 frontend/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png create mode 100644 frontend/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 frontend/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 frontend/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 frontend/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 frontend/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 frontend/ios/Runner/Base.lproj/Main.storyboard create mode 100644 frontend/ios/Runner/Info.plist create mode 100644 frontend/ios/Runner/Runner-Bridging-Header.h create mode 100644 frontend/ios/Runner/SceneDelegate.swift create mode 100644 frontend/ios/RunnerTests/RunnerTests.swift create mode 100644 frontend/lib/Grounded/about/external/data/Commitment.dart create mode 100644 frontend/lib/Grounded/about/external/data/CommitmentEvent.dart create mode 100644 frontend/lib/Grounded/about/external/data/DebtEntry.dart create mode 100644 frontend/lib/Grounded/about/external/data/ExcuseCluster.dart create mode 100644 frontend/lib/Grounded/about/external/data/ExercisePrescription.dart create mode 100644 frontend/lib/Grounded/about/external/data/Goal.dart create mode 100644 frontend/lib/Grounded/about/external/data/GroundedError.dart create mode 100644 frontend/lib/Grounded/about/external/data/Habit.dart create mode 100644 frontend/lib/Grounded/about/external/data/LiveSession.dart create mode 100644 frontend/lib/Grounded/about/external/data/Program.dart create mode 100644 frontend/lib/Grounded/about/external/data/ReportCard.dart create mode 100644 frontend/lib/Grounded/about/external/data/ResponseState.dart create mode 100644 frontend/lib/Grounded/about/external/data/RoutineChain.dart create mode 100644 frontend/lib/Grounded/about/external/data/SessionLog.dart create mode 100644 frontend/lib/Grounded/about/external/data/SessionTemplate.dart create mode 100644 frontend/lib/Grounded/about/external/data/SetLog.dart create mode 100644 frontend/lib/Grounded/about/external/data/Severity.dart create mode 100644 frontend/lib/Grounded/about/external/data/StandingChange.dart create mode 100644 frontend/lib/Grounded/about/external/data/SystemResponse.dart create mode 100644 frontend/lib/Grounded/about/external/data/pages/request/CommitmentsRequest.dart create mode 100644 frontend/lib/Grounded/about/external/data/pages/request/HistoryRequest.dart create mode 100644 frontend/lib/Grounded/about/external/data/pages/request/PageAndSort.dart create mode 100644 frontend/lib/Grounded/about/external/data/pages/request/Pageable.dart create mode 100644 frontend/lib/Grounded/about/external/data/pages/request/Sort.dart create mode 100644 frontend/lib/Grounded/about/external/data/pages/response/CommitmentEventPage.dart create mode 100644 frontend/lib/Grounded/about/external/data/pages/response/CommitmentPage.dart create mode 100644 frontend/lib/Grounded/about/external/data/pages/response/SessionLogPage.dart create mode 100644 frontend/lib/Grounded/about/external/initial/AbandonRequest.dart create mode 100644 frontend/lib/Grounded/about/external/initial/AmnestyRequest.dart create mode 100644 frontend/lib/Grounded/about/external/initial/CommitmentRequest.dart create mode 100644 frontend/lib/Grounded/about/external/initial/CompletionRequest.dart create mode 100644 frontend/lib/Grounded/about/external/initial/DeferralRequest.dart create mode 100644 frontend/lib/Grounded/about/external/initial/DeviceRequest.dart create mode 100644 frontend/lib/Grounded/about/external/initial/GoalRequest.dart create mode 100644 frontend/lib/Grounded/about/external/initial/IdRequest.dart create mode 100644 frontend/lib/Grounded/about/external/initial/LoginData.dart create mode 100644 frontend/lib/Grounded/about/external/initial/ReportCardRequest.dart create mode 100644 frontend/lib/Grounded/about/external/initial/SessionLogRequest.dart create mode 100644 frontend/lib/Grounded/about/external/initial/SickModeRequest.dart create mode 100644 frontend/lib/Grounded/about/external/initial/ToneRequest.dart create mode 100644 frontend/lib/Grounded/about/internal/application/CapacityProfile.dart create mode 100644 frontend/lib/Grounded/about/internal/application/CommitmentClass.dart create mode 100644 frontend/lib/Grounded/about/internal/application/CommitmentStatus.dart create mode 100644 frontend/lib/Grounded/about/internal/application/CommitmentType.dart create mode 100644 frontend/lib/Grounded/about/internal/application/DialogData.dart create mode 100644 frontend/lib/Grounded/about/internal/application/EnergyCost.dart create mode 100644 frontend/lib/Grounded/about/internal/application/EscalationTier.dart create mode 100644 frontend/lib/Grounded/about/internal/application/EventType.dart create mode 100644 frontend/lib/Grounded/about/internal/application/MeDescription.dart create mode 100644 frontend/lib/Grounded/about/internal/application/NavigatorType.dart create mode 100644 frontend/lib/Grounded/about/internal/application/NotificationType.dart create mode 100644 frontend/lib/Grounded/about/internal/application/Pair.dart create mode 100644 frontend/lib/Grounded/about/internal/application/ProgressionRule.dart create mode 100644 frontend/lib/Grounded/about/internal/application/ProofType.dart create mode 100644 frontend/lib/Grounded/about/internal/application/Standing.dart create mode 100644 frontend/lib/Grounded/about/internal/application/TextType.dart create mode 100644 frontend/lib/Grounded/about/internal/application/Token.dart create mode 100644 frontend/lib/Grounded/about/internal/application/ToneLevel.dart create mode 100644 frontend/lib/Grounded/about/internal/application/UserDetails.dart create mode 100644 frontend/lib/Grounded/about/internal/file/ConnectFileStorage.dart create mode 100644 frontend/lib/Grounded/about/internal/file/FileStorage.dart create mode 100644 frontend/lib/Grounded/comms/Comms.dart create mode 100644 frontend/lib/Grounded/comms/CommsDirections.dart create mode 100644 frontend/lib/Grounded/comms/ConnectComms.dart create mode 100644 frontend/lib/Grounded/configs/Env.dart create mode 100644 frontend/lib/Grounded/configs/Navigator.dart create mode 100644 frontend/lib/Grounded/configs/NotificationServiceConfig.dart create mode 100644 frontend/lib/Grounded/designs/Component.dart create mode 100644 frontend/lib/Grounded/designs/Responsive.dart create mode 100644 frontend/lib/Grounded/designs/Shell.dart create mode 100644 frontend/lib/Grounded/designs/buttons/Buttons.dart create mode 100644 frontend/lib/Grounded/designs/input/InputFields.dart create mode 100644 frontend/lib/Grounded/designs/text/Text.dart create mode 100644 frontend/lib/Grounded/informatics/AppDataManager.dart create mode 100644 frontend/lib/Grounded/informatics/DataManager.dart create mode 100644 frontend/lib/Grounded/memory/ConnectInternalMemory.dart create mode 100644 frontend/lib/Grounded/memory/InternalMemory.dart create mode 100644 frontend/lib/Grounded/see/commitment/ConnectNewCommitment.dart create mode 100644 frontend/lib/Grounded/see/commitment/NewCommitment.dart create mode 100644 frontend/lib/Grounded/see/commitment/NewCommitmentState.dart create mode 100644 frontend/lib/Grounded/see/commitment/ViewNewCommitment.dart create mode 100644 frontend/lib/Grounded/see/excuse/ConnectExcuseReport.dart create mode 100644 frontend/lib/Grounded/see/excuse/ExcuseReport.dart create mode 100644 frontend/lib/Grounded/see/excuse/ExcuseReportState.dart create mode 100644 frontend/lib/Grounded/see/excuse/ViewExcuseReport.dart create mode 100644 frontend/lib/Grounded/see/goal/ConnectGoalDetail.dart create mode 100644 frontend/lib/Grounded/see/goal/ConnectGoals.dart create mode 100644 frontend/lib/Grounded/see/goal/GoalDetail.dart create mode 100644 frontend/lib/Grounded/see/goal/GoalDetailState.dart create mode 100644 frontend/lib/Grounded/see/goal/Goals.dart create mode 100644 frontend/lib/Grounded/see/goal/GoalsState.dart create mode 100644 frontend/lib/Grounded/see/goal/ViewGoalDetail.dart create mode 100644 frontend/lib/Grounded/see/goal/ViewGoals.dart create mode 100644 frontend/lib/Grounded/see/home/ConnectHome.dart create mode 100644 frontend/lib/Grounded/see/home/Home.dart create mode 100644 frontend/lib/Grounded/see/home/HomeState.dart create mode 100644 frontend/lib/Grounded/see/home/ViewHome.dart create mode 100644 frontend/lib/Grounded/see/live/ConnectLiveTask.dart create mode 100644 frontend/lib/Grounded/see/live/LiveTask.dart create mode 100644 frontend/lib/Grounded/see/live/LiveTaskState.dart create mode 100644 frontend/lib/Grounded/see/live/ViewLiveTask.dart create mode 100644 frontend/lib/Grounded/see/login/ConnectLogin.dart create mode 100644 frontend/lib/Grounded/see/login/Login.dart create mode 100644 frontend/lib/Grounded/see/login/LoginState.dart create mode 100644 frontend/lib/Grounded/see/login/ViewLogin.dart create mode 100644 frontend/lib/Grounded/see/overdue/ConnectOverdueQueue.dart create mode 100644 frontend/lib/Grounded/see/overdue/OverdueQueue.dart create mode 100644 frontend/lib/Grounded/see/overdue/OverdueQueueState.dart create mode 100644 frontend/lib/Grounded/see/overdue/ViewOverdueQueue.dart create mode 100644 frontend/lib/Grounded/see/parent/ParentViewModel.dart create mode 100644 frontend/lib/Grounded/see/reportcard/ConnectReportCardScreen.dart create mode 100644 frontend/lib/Grounded/see/reportcard/ReportCardScreen.dart create mode 100644 frontend/lib/Grounded/see/reportcard/ReportCardScreenState.dart create mode 100644 frontend/lib/Grounded/see/reportcard/ViewReportCardScreen.dart create mode 100644 frontend/lib/Grounded/see/settings/ConnectSettings.dart create mode 100644 frontend/lib/Grounded/see/settings/Settings.dart create mode 100644 frontend/lib/Grounded/see/settings/SettingsState.dart create mode 100644 frontend/lib/Grounded/see/settings/ViewSettings.dart create mode 100644 frontend/lib/Grounded/see/splash/ConnectSplash.dart create mode 100644 frontend/lib/Grounded/see/splash/Splash.dart create mode 100644 frontend/lib/Grounded/see/splash/SplashState.dart create mode 100644 frontend/lib/Grounded/see/splash/ViewSplash.dart create mode 100644 frontend/lib/Grounded/see/system/sessionexpired/ConnectSessionExpired.dart create mode 100644 frontend/lib/Grounded/see/system/sessionexpired/SessionExpired.dart create mode 100644 frontend/lib/Grounded/see/system/sessionexpired/SessionExpiredState.dart create mode 100644 frontend/lib/Grounded/see/system/sessionexpired/ViewSessionExpired.dart create mode 100644 frontend/lib/Grounded/see/system/updateme/ConnectUpdateMe.dart create mode 100644 frontend/lib/Grounded/see/system/updateme/UpdateMe.dart create mode 100644 frontend/lib/Grounded/see/system/updateme/UpdateMeState.dart create mode 100644 frontend/lib/Grounded/see/system/updateme/ViewUpdateMe.dart create mode 100644 frontend/lib/Grounded/see/training/ConnectTraining.dart create mode 100644 frontend/lib/Grounded/see/training/Training.dart create mode 100644 frontend/lib/Grounded/see/training/TrainingState.dart create mode 100644 frontend/lib/Grounded/see/training/ViewTraining.dart create mode 100644 frontend/lib/Grounded/utils/CapacityEngine.dart create mode 100644 frontend/lib/Grounded/utils/Colors.dart create mode 100644 frontend/lib/Grounded/utils/CommonUtils.dart create mode 100644 frontend/lib/Grounded/utils/DebtEngine.dart create mode 100644 frontend/lib/Grounded/utils/ExcuseAnalyser.dart create mode 100644 frontend/lib/Grounded/utils/GuardrailEngine.dart create mode 100644 frontend/lib/Grounded/utils/Images.dart create mode 100644 frontend/lib/Grounded/utils/IntegrityEngine.dart create mode 100644 frontend/lib/Grounded/utils/ObjectConvertors.dart create mode 100644 frontend/lib/Grounded/utils/StandingEngine.dart create mode 100644 frontend/lib/Grounded/utils/Thresholds.dart create mode 100644 frontend/lib/Grounded/utils/ToneEngine.dart create mode 100644 frontend/lib/Grounded/utils/Validators.dart create mode 100644 frontend/lib/main.dart create mode 100644 frontend/mason-lock.json create mode 100644 frontend/mason.yaml create mode 100644 frontend/pubspec.lock create mode 100644 frontend/pubspec.yaml create mode 100644 frontend/web/favicon.png create mode 100644 frontend/web/icons/Icon-192.png create mode 100644 frontend/web/icons/Icon-512.png create mode 100644 frontend/web/icons/Icon-maskable-192.png create mode 100644 frontend/web/icons/Icon-maskable-512.png create mode 100644 frontend/web/index.html create mode 100644 frontend/web/manifest.json create mode 100644 frontend/web/splash/img/dark-1x.png create mode 100644 frontend/web/splash/img/dark-2x.png create mode 100644 frontend/web/splash/img/dark-3x.png create mode 100644 frontend/web/splash/img/dark-4x.png create mode 100644 frontend/web/splash/img/light-1x.png create mode 100644 frontend/web/splash/img/light-2x.png create mode 100644 frontend/web/splash/img/light-3x.png create mode 100644 frontend/web/splash/img/light-4x.png create mode 100644 frontend/windows/.gitignore create mode 100644 frontend/windows/CMakeLists.txt create mode 100644 frontend/windows/flutter/CMakeLists.txt create mode 100644 frontend/windows/runner/CMakeLists.txt create mode 100644 frontend/windows/runner/Runner.rc create mode 100644 frontend/windows/runner/flutter_window.cpp create mode 100644 frontend/windows/runner/flutter_window.h create mode 100644 frontend/windows/runner/main.cpp create mode 100644 frontend/windows/runner/resource.h create mode 100644 frontend/windows/runner/resources/app_icon.ico create mode 100644 frontend/windows/runner/runner.exe.manifest create mode 100644 frontend/windows/runner/utils.cpp create mode 100644 frontend/windows/runner/utils.h create mode 100644 frontend/windows/runner/win32_window.cpp create mode 100644 frontend/windows/runner/win32_window.h diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4141ca8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,27 @@ +# Normalise line endings so the repo stays clean across Windows and CI. +* text=auto eol=lf + +# Windows-only tooling keeps CRLF. +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf + +# Binaries — never touched, never diffed as text. +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.ttf binary +*.otf binary +*.woff binary +*.woff2 binary +*.pdf binary +*.zip binary +*.jar binary +*.keystore binary +*.jks binary + +# Generated / vendored — collapse in diffs and exclude from language stats. +pubspec.lock linguist-generated=true -diff +**/ios/Runner.xcodeproj/project.pbxproj -diff diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0549613 --- /dev/null +++ b/.gitignore @@ -0,0 +1,209 @@ +# ───────────────────────────────────────────────────────────────────────────── +# Grounded — repository ignore rules +# ───────────────────────────────────────────────────────────────────────────── + +# ── Secrets & environment ──────────────────────────────────────────────────── +# .env carries the backend hosts and app version. Committing it leaks the +# deployment topology, so the template is tracked and the real file is not. +.env +.env.* +!.env.example +*.pem +*.key +*.p12 +*.jks +*.keystore +*.mobileprovision +key.properties +secrets.properties +google-services.json +GoogleService-Info.plist +firebase_options.dart +service-account*.json +credentials.json +*.retrofit.dart.log + +# ── Dart / Flutter ─────────────────────────────────────────────────────────── +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ +coverage/ +doc/api/ +**/doc/api/ +*.dart.js +*.info.json +*.js_ +*.js.deps +*.js.map +.flutter-versions +pubspec_overrides.yaml + +# Generated sources +*.g.dart +*.freezed.dart +*.mocks.dart +*.config.dart + +# Symbolication / obfuscation +app.*.symbols +app.*.map.json + +# ── Mason ──────────────────────────────────────────────────────────────────── +# Bricks themselves are tracked; the resolved cache is not. +.mason/ +**/hooks/.dart_tool/ +**/hooks/pubspec.lock + +# ── Android ────────────────────────────────────────────────────────────────── +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java +**/android/key.properties +**/android/app/release/ +**/android/app/debug/ +**/android/app/profile/ +*.apk +*.aab +*.ap_ +*.dex +.cxx/ + +# ── iOS ────────────────────────────────────────────────────────────────────── +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral/ +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/Flutter/.last_build_id +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* +*.ipa +*.dSYM.zip +*.dSYM + +# ── macOS ──────────────────────────────────────────────────────────────────── +**/macos/Flutter/GeneratedPluginRegistrant.swift +**/macos/Flutter/ephemeral/ +**/macos/Flutter/Flutter-Debug.xcconfig +**/macos/Flutter/Flutter-Release.xcconfig +**/macos/Flutter/Flutter-Profile.xcconfig +**/macos/Pods/ +**/xcuserdata/ +**/DerivedData/ + +# ── Windows ────────────────────────────────────────────────────────────────── +**/windows/flutter/generated_plugin_registrant.cc +**/windows/flutter/generated_plugin_registrant.h +**/windows/flutter/generated_plugins.cmake +**/windows/flutter/ephemeral/ +*.exe +*.msix +*.pdb +*.ilk + +# ── Linux ──────────────────────────────────────────────────────────────────── +**/linux/flutter/generated_plugin_registrant.cc +**/linux/flutter/generated_plugin_registrant.h +**/linux/flutter/generated_plugins.cmake +**/linux/flutter/ephemeral/ + +# ── Web ────────────────────────────────────────────────────────────────────── +**/web/*.g.dart +lib/generated_plugin_registrant.dart + +# ── IDEs & editors ─────────────────────────────────────────────────────────── +.idea/ +*.iml +*.ipr +*.iws +.vscode/ +!.vscode/launch.json +!.vscode/settings.json +*.swp +*.swo +*~ +.history/ +.atom/ +.buildlog/ +.build/ +migrate_working_dir/ +.metals/ +.bloop/ + +# ── OS noise ───────────────────────────────────────────────────────────────── +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +Thumbs.db:encryptable +desktop.ini +$RECYCLE.BIN/ +*.lnk + +# ── Logs, temp, caches ─────────────────────────────────────────────────────── +*.log +*.tmp +*.temp +*.bak +*.orig +*.rej +*.pyc +__pycache__/ +*.class +.cache/ +tmp/ +temp/ +node_modules/ + +# ── Archives & large binaries ──────────────────────────────────────────────── +*.zip +*.tar +*.tar.gz +*.tgz +*.rar +*.7z +*.iso +*.dmg + +# ── Working scratch ────────────────────────────────────────────────────────── +# Icon exploration output and other throwaways. +**/assets/icons/_*.png +**/assets/icons/try_*.png +scratch/ +scratchpad/ +*.local + +# ── Agent tooling ──────────────────────────────────────────────────────────── +.claude/settings.local.json +.agents/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..099b1a1 --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +# Grounded + +*A to-do app that doesn't believe you.* + +Most to-do apps are neutral ledgers — they record intent and never object when +you ignore it. Grounded is an **enforcement layer**: it holds a model of what +you committed to, notices when reality diverges, and imposes escalating +consequences you agreed to in advance. + +Design axiom: **the checkbox is the enemy.** Every feature exists because +self-reported completion is worthless to someone trying to stop lying to +themselves. + +--- + +## Repository + +``` +frontend/ Flutter app (Android · iOS · Web · Windows) +``` + +See [`frontend/CLAUDE.md`](frontend/CLAUDE.md) for the architecture and +conventions, and [`frontend/design.md`](frontend/design.md) for the design +language. + +--- + +## The model + +Three primitives; everything else is derived. + +| Primitive | Meaning | +|---|---| +| **Commitment** | Something you said you'd do. Has a due *window*, a class, and a proof requirement. | +| **Debt** | The weight of what you've missed — a decaying, weighted score rather than a count. | +| **Standing** | `Good → Warned → Grounded → Lockdown`. Derived from debt; decides what the app lets you do. | + +Streaks reward perfection and collapse permanently on one miss. Debt is +continuous, forgiving in shape, hard to ignore, and gives you a way back. + +**Goals** contain commitments — "Workout" holds "Monday shoulders". A goal +never carries debt; the tasks inside it do. + +--- + +## What makes it different + +- **Due windows, not due dates.** `Mon 06:00–08:00` can actually close. A miss + is a permanent event, never a silent rollover to today. +- **Capacity blocking.** Chronic overdue is usually overcommitment misdiagnosed + as laziness. The app plans against what you *historically complete*, learns + your per-category estimation multiplier, and refuses plans that don't fit. +- **Excuses get clustered.** *"'Too tired' has appeared 14 times this month, 11 + of them on gym days, 9 of them after 7pm. Consider moving gym to morning."* +- **Proof of completion.** Honour · Photo · Timer · Location · Health · Witness. + Completing outside the window records as *late complete* — distinct in + history, and it never reduces debt to zero. +- **A live runner.** Starting a task takes over the screen, keeps time from + wall-clock so screen-off can't cheat it, and pauses when you leave the app. +- **Plyometrics is where it stops you.** Weekly ground-contact ceilings and + enforced recovery gaps — connective tissue doesn't recover on a motivation + schedule. + +## And what keeps it usable + +An app built on guilt has an obvious failure mode: the people who need it most +delete it during their worst week. So — rationed **amnesty tokens**, **sick and +travel mode** that pauses debt entirely, a **tone slider** hard-capped so no +copy ever attacks the person rather than the behaviour, and **distress +detection** that drops the strict persona completely when debt spikes while +engagement and readiness fall. Strictness is never the response to someone who +is actually struggling. + +--- + +## Running it + +```bash +cd frontend +cp .env.example .env # then point it at your backend +flutter pub get +flutter run +``` + +Requires Flutter with Dart `^3.6.2`. + +--- + +## Status + +Frontend scaffold complete: the full architecture, the debt/standing/capacity/ +integrity engines, and every screen in the MVP cut. The backend is not in this +repository yet — `CommsDirections.dart` defines the contract it needs to +satisfy. diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000..1524555 --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1,10 @@ +# Copy to `.env` before running. The real file is gitignored. +# +# GROUNDED_LOCAL_PATH is the host only — CommsDirections appends the service +# port and context path (Prospect 40003, Training 40004, Discipline 40005). + +GROUNDED_LOCAL_PATH="http://192.168.0.100" + +GROUNDED_PRODUCTION_PATH="https://app.grounded.com" + +LOCALISED_APP_VERSION="1.0.0" diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..3820a95 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/frontend/.metadata b/frontend/.metadata new file mode 100644 index 0000000..f8eee6d --- /dev/null +++ b/frontend/.metadata @@ -0,0 +1,39 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "ee80f08bbf97172ec030b8751ceab557177a34a6" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6 + base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6 + - platform: android + create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6 + base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6 + - platform: ios + create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6 + base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6 + - platform: web + create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6 + base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6 + - platform: windows + create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6 + base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md new file mode 100644 index 0000000..daa73c3 --- /dev/null +++ b/frontend/CLAUDE.md @@ -0,0 +1,179 @@ +# Grounded — Flutter Frontend + +*A to-do app that doesn't believe you.* + +Architecture ported from `Documents/Autoreceptives/Frontend/Receptive`. Same +structure, conventions and infrastructure; the business logic is the debt and +enforcement engine. + +--- + +## 1. Stack + +Flutter (Dart `^3.6.2`), **stacked** MVVM, **dio**, **flutter_secure_storage**, +**flutter_dotenv**, **fl_chart**, **flutter_local_notifications**. +Package name and `lib/` root are both `Grounded`, so imports read +`package:Grounded/Grounded/...`. Org is `nya` (`nya.grounded`). + +Codegen is via **Mason** — screens, endpoints and storage fields are generated, +never hand-scaffolded. + +--- + +## 2. Layout (`lib/Grounded/`) + +``` +about/external/data/ responses & domain models (+ pages/ for pagination) +about/external/initial/ request bodies +about/internal/application/ enums and app-internal models +about/internal/file/ FileStorage +comms/ CommsDirections · ConnectComms · Comms +informatics/ DataManager · AppDataManager +memory/ ConnectInternalMemory · InternalMemory +configs/ Navigator · Env · NotificationServiceConfig +designs/ Component · Shell · Responsive · buttons/ input/ text/ +see// screens, 4 files each +utils/ Colors · CommonUtils · engines · validators +``` + +--- + +## 3. The 4-file screen pattern (mandatory) + +| File | Role | +|---|---| +| `Foo.dart` | `StatefulWidget` shell — creates `FooState` only. | +| `FooState.dart` | All UI + local state; implements `ConnectFoo`. | +| `ViewFoo.dart` | `extends ParentViewModel` — business logic, calls `DataManager`, pushes results back through `ConnectFoo`. | +| `ConnectFoo.dart` | Abstract callbacks so the ViewModel never touches widgets. | + +`ViewModelBuilder.reactive` wires them. `onViewModelReady` assigns +`_model` and calls one named `_initiate()`. **No logic in `builder:` before the +`return`.** + +Generate with: `mason make mvvc_template --project Grounded --screen Foo` + +--- + +## 4. ParentViewModel + +Constructs the single `AppDataManager` and owns everything cross-cutting: +`showLoading` / `closeLoading`, `hasNetwork(retry)`, `showError`, +`showApplicationNotification`, and `handleError` — whose decision tree routes +`401 → sessionExpired()` and error code `5000.901 → updateMe()`. + +Guard every network call: + +```dart +void loadThing(ThingRequest request) async { + if (!await hasNetwork(() => loadThing(request))) return; + showLoading('Loading…'); + try { + final response = await getDataManager().getThing(request); + closeLoading(); + connection.onThingLoaded(Thing.fromJson(response.data)); + } catch (e) { + handleError(e, () => loadThing(request), () => dismissError(), 'Retry'); + } +} +``` + +--- + +## 5. Domain — the enforcement engine + +Three primitives: **Commitment** (what you said you'd do), **Debt** (weighted, +decaying score), **Standing** (`Good → Warned → Grounded → Lockdown`). + +**Goals** contain commitments via `Commitment.parentId` — "Workout" holds +"Monday shoulders". Goals never carry debt; the tasks inside them do. + +Engines in `utils/`, each mapping 1:1 to the spec's formulas: + +| Engine | Owns | +|---|---| +| `DebtEngine` | `w(class) × severity(d) × decay(t)`; abandonment 2×, no decay 30d; late complete retains 30% | +| `StandingEngine` | tier derivation; what each tier permits | +| `CapacityEngine` | blocks over-scheduling against p50 of historical completed minutes × 0.85 | +| `IntegrityEngine` | session integrity, weekly volume, plyo contact ceiling, recovery gate | +| `ExcuseAnalyser` | on-device excuse clustering + the confrontation copy | +| `GuardrailEngine` | distress detection, amnesty tokens | +| `ToneEngine` | **all** enforcement copy — the one place the tone cap is enforced | + +Thresholds live in `Thresholds.dart`. Never inline a limit. + +`CommitmentEvent` is append-only and is the source of truth — **not** the +status field. That's what makes honest history and excuse analysis possible. + +--- + +## 6. Design language + +See `design.md`. In short: black chrome → white sheet with a 28 radius → +oversized **Light** display title → tiny grey all-caps labels above values. + +- Every screen is built from `Sheet` (`designs/Shell.dart`). +- Display titles are `TextType.Light`. Bold is for labels and metrics only. +- Never raw `Text`/`TextStyle` — use `text()` from `designs/text/Text.dart`. +- Never inline hex — use `utils/Colors.dart`. +- Standing owns a colour used consistently everywhere (chrome, chip, meter). +- One primary button per screen; everything else outlined or text. + +Font: **General Sans** (Fontshare, FFL — see `fonts/LICENSE-GeneralSans.txt`), +wrapped as `TextType.{Bold,Medium,Regular,Light}`. + +--- + +## 7. Live task runner + +`see/live/` is the full-screen runner: a task being run takes over the screen. +Elapsed time is derived from **wall-clock**, not counted by the ticker, so a +screen-off period cannot lose time. Backgrounding pauses the clock and +increments `backgroundedCount` — that's what makes Timer proof mean anything. + +`LocalNotificationEngine` mirrors it into an ongoing notification and fires +alarm-class **full-screen intents** for non-negotiables. Android manifest +carries `USE_FULL_SCREEN_INTENT`, `SCHEDULE_EXACT_ALARM`, and the activity is +`showWhenLocked` + `turnScreenOn`. + +--- + +## 8. Hard conventions + +1. Named event handlers only — `onPressed: _onSave`, never `() => _save()`. +2. `selectField` loads on tap; never pre-load picker options. +3. List parsing via `utils/ObjectConvertors.dart` — never inline `.map(...fromJson)`. +4. Pagination: `HistoryRequest { PageAndSort }`; responses are `*Page`; `last == true` stops loading. +5. Reload-on-return: child `Navigator.pop(context, true)`, parent refreshes on the result. +6. New endpoint → `mason make api_endpoint` (injects into all four HTTP files). +7. New secure field → `mason make internal_memory`. +8. One class per file, named after the class. + +--- + +## 9. Environment + +`.env` is a bundled asset, loaded in `main()` before `runApp`: + +``` +GROUNDED_PRODUCTION_PATH · GROUNDED_LOCAL_PATH · LOCALISED_APP_VERSION +``` + +`isProd` in `CommsDirections.dart` flips local↔production. Requests carry the +three identity headers: `what`, `whom`, `version`. + +--- + +## 10. Commands + +```bash +flutter pub get +flutter analyze lib/ # bricks/ are excluded — templates aren't valid Dart +flutter run + +mason get +mason make mvvc_template --project Grounded --screen MyScreen + +dart run flutter_launcher_icons +dart run flutter_native_splash:create +``` diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..852aafc --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,17 @@ +# grounded + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter) +- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/frontend/analysis_options.yaml b/frontend/analysis_options.yaml new file mode 100644 index 0000000..138d779 --- /dev/null +++ b/frontend/analysis_options.yaml @@ -0,0 +1,24 @@ +include: package:flutter_lints/flutter.yaml + +analyzer: + exclude: + # Mason templates are not valid Dart until they are rendered. + - bricks/** + - build/** + + errors: + # The project deliberately uses PascalCase file names and a PascalCase + # package root, matching the house architecture. These are conventions, + # not defects. + file_names: ignore + camel_case_types: ignore + constant_identifier_names: ignore + non_constant_identifier_names: ignore + library_prefixes: ignore + +linter: + rules: + use_super_parameters: false + prefer_const_constructors: false + prefer_const_literals_to_create_immutables: false + library_private_types_in_public_api: false diff --git a/frontend/android/.gitignore b/frontend/android/.gitignore new file mode 100644 index 0000000..be3943c --- /dev/null +++ b/frontend/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/frontend/android/app/build.gradle.kts b/frontend/android/app/build.gradle.kts new file mode 100644 index 0000000..72ec855 --- /dev/null +++ b/frontend/android/app/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("com.android.application") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "nya.grounded" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "nya.grounded" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 + } +} + +flutter { + source = "../.." +} diff --git a/frontend/android/app/src/debug/AndroidManifest.xml b/frontend/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/frontend/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/frontend/android/app/src/main/AndroidManifest.xml b/frontend/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9772895 --- /dev/null +++ b/frontend/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/android/app/src/main/kotlin/nya/grounded/MainActivity.kt b/frontend/android/app/src/main/kotlin/nya/grounded/MainActivity.kt new file mode 100644 index 0000000..73d19a4 --- /dev/null +++ b/frontend/android/app/src/main/kotlin/nya/grounded/MainActivity.kt @@ -0,0 +1,5 @@ +package nya.grounded + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/frontend/android/app/src/main/res/drawable-hdpi/android12splash.png b/frontend/android/app/src/main/res/drawable-hdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..2d047e474d6019c2dfc85ac54f610824115e5c4b GIT binary patch literal 9673 zcmeHtXIE2Q)NK$I6zL)$O+-+X-dhMLh=PECh}1lYbOTaD52%0?K|w%z??RB?BLYSU zMS2NEdMAV$Ldre9zuH&B zn&lQlM`*MqtRnPFL?nx$xl`!X7)^;y&Llv^9ZjxNdW^ zysWINH|ZX&nrDtbE2!4oq9i;(>-sNuJ(_uZ@-tDMZWyT{?|>`P_&d3jy6&W61jR51;&8z3hWV-pkYZ1v!- zA7QjFmzI`l&~g2ZHzg!?**aPH3^J5#ABk?wUHCwZLhTxg7Mjox^~m+hzGs2-^!GPu z>SDLNn-wI?tGyS>kps|XKYYSY3mkV%SXelW!b*b@1Ua#@v9W2{*kpaq%1U1FWxk&M z>Cet}O!d#vAb0a3kNvGHol@;*(Za(4R7E|COZ0&2scBd&hXcVJWTVi7j$_ zB1y!A&eTX+eP1eFIEHeO-xlDQ2=Q)w*NjJ`g=A@*iI`gU=9=W%8Z(LnK?x-`yzgIeq73hSb z>%?TmgoRh?j(3Cx+3xcmp{2uC?}J!IN0SHVsUa4NsgA$aGFw|3E)|Sy$ni0u*Y5v@ z#c)ba3BMPlW9nNUuCTmcW^djW&LX&(bq@OODFXasWaAePI#xB{a5Ax#K|@LiMDM~3 zKK!(Bq=Hk_m+hKJ^zM`z?Lzr%3>P9wE99Z(-(PlznG8_6hYK;A?c3EDiI@+uS5Rs~ zc0URl8z(#T>BEFXiq2-=U1TJ(>1nfKN%zgnitX@ z%bf!GA-T@C2YUg`=H~V!_Q}ZA6l>p&690qMLFJ=7Sr#$iK}o#Io&-=Gapd>J)Yf=K zX5Gu~Xd%k=-{K83H|GO!vpP$Qi*B`IRjGUS)nEGoz3VP>|(7V=`47G^d}ex6n=3LU8_BrD~B_n$U8fi zP=s#l5P^82+S|s)0qfBO13R1Iyjz8)m?_BrtXRUZnhSRL^%KzO66eoO)f4x)mE_&o z7M`=?V6QlP^Xkv=^9&=0&rZF>LQ(&c#K@zr;N^9|Y>}M$=j()dvJXB-bHg?~JX}76 zKY>>&2Vr3ci8i*DRa(nfmSkXXKRS35S_JG;T^CG&?QfWIuUm28uZngcUCQF(u&>x1 zo9#igRs#e9Q2x9+Z!t02Yf!((rO&*sLc+qzXPhX4 ze>SiktM~W!MLuc<^W*C#Y}ge%mwyLdKeNgsGWfv?-)jMU?XB7u7eo#-+I92`FOrempvHe6lz}CDY(+ zA+i{XfOm5OXnPfssz zAq6ZmzD|1e;Skg=eh!+tg2dERf6sKMS!q`n7cIX=qD^*GBJ|)Od;aS4r*Fnafuzme zD13dmjFbT3W?LE>zawca*nk*~9S2N`4Xk(HC{0)-uefcSHxh~K>m>)s=DXDVAEexI$C)Jh2x72fq%h8&OQhB z`({=WDT=plZ4Yi9oDcE`|6v2xB`sRRW{zVp+smLDn-U1rAfO7TB_sqP=G)h1D@44J z6Xg`Rdqz-Eyox0R*Q)+tc$hdZRJco24o)^!}T62X_jh0woS8<_m3o2&mqc)%=wbRt2+(ztJ?yaA3-

hkR}uMjgEKF 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 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/frontend/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..80bc7cf29ff66d88682ff60423c1bd50b650a460 GIT binary patch literal 3781 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 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-hdpi/splash.png b/frontend/android/app/src/main/res/drawable-hdpi/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..adc1931efae21bbf67d9da822d83f610039b8883 GIT binary patch literal 12481 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 LRV`P3_WJ(-AbHLv literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-mdpi/android12splash.png b/frontend/android/app/src/main/res/drawable-mdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..fa747f7018e721e7850d8770a94580649822ca0b GIT binary patch literal 5784 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


cw3_x4YX)TE@rCchXf2&-I##Q&X~W z8>6FvTpI)eH-v{07dH~RoE!4ypZ37Ogu}PAA7@P!Uyu;kC($Q=8@!!_GWETmtq&d7 zf#1q?J)$=ZMouj+FW;M7yiro!6Oye0<9G zM!%>8C0Um@6(I2I&NbHP-+r ztzAoux?FwvBygV2CF%0i>#v#jI^8V#qUp?7|Ej*)lj?G|Uy8e5#zY!_@bDZ%_Gt!DO^#f>9| zon;nIRA?bf>Zooo!J~(tRw+vd_~F4#FzhC zLkOcwc8g&9>Dg4;NPt(%HWtx z(B8S1_B(E>FY!Ddmosx}O$jm8>E2IUzShNQ{igEw*;X;xePP=BkcmU-(?x6~(D2bG zFNF>&CAd=%N!GdLGr`k=-kYYowHHd_RSaD!gFoCfrxeLQx-h4lM&}nemM`Fos{E6r zE$bZYvxID8gd5GX|JP%B`c5=t?mHG|L||K4YFB$jfKI{)yVz6b_kT#(eA zYrA3OwX*B6*h2*?KQn*14`?s|^=iP}*Xq)^53J2IX_O$Njm;?BT@`EWTWSU?{>SdP z)nABiiz!N?&=bi#FH}kQ)t#s|yK{CLig{`f4-X1-R6};=Z%-TCAmnu}N_NbSto%wa z(I}D_8}}YR3oZs*maBPQpek3lv-6#57P8KG9~&%SzB0f1Vo}#BSE>a=|At*TD`^sM z--S?GB;4dC&C^o6s!lZZaQLF)@4n^txkImqgZloV*FboYg4_e12m=xOG?GEH^{Z>i z&Ab3Hue3VO*Dtg~;-Jf+p{iI5OR-P=zf;Qpw`nbg3``7((gZnEb$}CR0IFs9phDwG G#Qy;R6wF5e literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/frontend/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..9a6e840ae87441922baedf61d64b5b83054730bc GIT binary patch literal 2328 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 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-mdpi/splash.png b/frontend/android/app/src/main/res/drawable-mdpi/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..92ef668aaf65fe585d80082cf2edf4bf20abec72 GIT binary patch literal 7513 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

hkR}uMjgEKF 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 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-night-mdpi/android12splash.png b/frontend/android/app/src/main/res/drawable-night-mdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..fa747f7018e721e7850d8770a94580649822ca0b GIT binary patch literal 5784 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


cw3_x4YX)TE@rCchXf2&-I##Q&X~W z8>6FvTpI)eH-v{07dH~RoE!4ypZ37Ogu}PAA7@P!Uyu;kC($Q=8@!!_GWETmtq&d7 zf#1q?J)$=ZMouj+FW;M7yiro!6Oye0<9G zM!%>8C0Um@6(I2I&NbHP-+r ztzAoux?FwvBygV2CF%0i>#v#jI^8V#qUp?7|Ej*)lj?G|Uy8e5#zY!_@bDZ%_Gt!DO^#f>9| zon;nIRA?bf>Zooo!J~(tRw+vd_~F4#FzhC zLkOcwc8g&9>Dg4;NPt(%HWtx z(B8S1_B(E>FY!Ddmosx}O$jm8>E2IUzShNQ{igEw*;X;xePP=BkcmU-(?x6~(D2bG zFNF>&CAd=%N!GdLGr`k=-kYYowHHd_RSaD!gFoCfrxeLQx-h4lM&}nemM`Fos{E6r zE$bZYvxID8gd5GX|JP%B`c5=t?mHG|L||K4YFB$jfKI{)yVz6b_kT#(eA zYrA3OwX*B6*h2*?KQn*14`?s|^=iP}*Xq)^53J2IX_O$Njm;?BT@`EWTWSU?{>SdP z)nABiiz!N?&=bi#FH}kQ)t#s|yK{CLig{`f4-X1-R6};=Z%-TCAmnu}N_NbSto%wa z(I}D_8}}YR3oZs*maBPQpek3lv-6#57P8KG9~&%SzB0f1Vo}#BSE>a=|At*TD`^sM z--S?GB;4dC&C^o6s!lZZaQLF)@4n^txkImqgZloV*FboYg4_e12m=xOG?GEH^{Z>i z&Ab3Hue3VO*Dtg~;-Jf+p{iI5OR-P=zf;Qpw`nbg3``7((gZnEb$}CR0IFs9phDwG G#Qy;R6wF5e literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-night-xhdpi/android12splash.png b/frontend/android/app/src/main/res/drawable-night-xhdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..3c00d5ff8bcdbe80e1f18bd4152ec57169344710 GIT binary patch literal 13033 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 VmS!#nHK0I1cMZ(+>vSK){SW6@$~XW3 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png b/frontend/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..a28a19f9fa1a4b21e6fd25d81272da2b806a3799 GIT binary patch literal 23073 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 Nut(jF)HwU!{2!+`x=8>4 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png b/frontend/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..043fa99e93db54a647153b2960942d97df2f4097 GIT binary patch literal 30894 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 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-v21/background.png b/frontend/android/app/src/main/res/drawable-v21/background.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0e06906ac00d1393af9a64ee309545dc07814a GIT binary patch literal 69 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1SBVv2j2ryJf1F&Ar*6y1qB6xay<-=nLV}f PKye07S3j3^P6 + + + + + + + + diff --git a/frontend/android/app/src/main/res/drawable-xhdpi/android12splash.png b/frontend/android/app/src/main/res/drawable-xhdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..3c00d5ff8bcdbe80e1f18bd4152ec57169344710 GIT binary patch literal 13033 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 VmS!#nHK0I1cMZ(+>vSK){SW6@$~XW3 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/frontend/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..2ea8b4d6dec3ad01ab291d85f1be30b18a872d10 GIT binary patch literal 4968 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 Nut(jF)HwU!{2!+`x=8>4 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/frontend/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..a95c3efc957cc9c9844a3809c5e5a5b16e6f9a13 GIT binary patch literal 7777 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 KER#2T_x}I@wh~kT literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-xxxhdpi/android12splash.png b/frontend/android/app/src/main/res/drawable-xxxhdpi/android12splash.png new file mode 100644 index 0000000000000000000000000000000000000000..043fa99e93db54a647153b2960942d97df2f4097 GIT binary patch literal 30894 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 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/frontend/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..429a9c16654b4d3c5b4507405ee6041ab187177b GIT binary patch literal 11109 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 G_rCxj>ilT{ literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable-xxxhdpi/splash.png b/frontend/android/app/src/main/res/drawable-xxxhdpi/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..82f7f01952010b44079517d86b75d2be91110caf GIT binary patch literal 39722 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 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/drawable/background.png b/frontend/android/app/src/main/res/drawable/background.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0e06906ac00d1393af9a64ee309545dc07814a GIT binary patch literal 69 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1SBVv2j2ryJf1F&Ar*6y1qB6xay<-=nLV}f PKye07S3j3^P6 + + + + + + + + diff --git a/frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..c79c58a --- /dev/null +++ b/frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..5822822256eb8c3a49962efc9ca4580671ac6bca GIT binary patch literal 1975 zcmV;o2T1sdP)@Ezlv$GqVUqZ;d{ob2ze*3=Py!XCu zCDzu~{lEkQ0BVTqkIF^@0)~V@5DNr?SRfgQJ5{K*eY7?bLlZ?qvk^j!1qMZswY4=M zgrKFR1t?1O%heC`u0czdEJhnQ2Ep99Ux0;$1ttb{cXz|hn^jO)cmXb5`jfQr<&VK= zkh!@z96g$a7B60;W@Bn<3JVs@Cx0O!n^8nW6xG_=s#AXkv(Zb485s3JQj{wl>~;q@fprl9Kkq?Af!`+6Nhf&Tt}@bOsy(b2oW%F0S%vuf2!m^EuAR9D~S)nDF4dj0x9 z5R1i%2ANETHg67rd-v`uZ0hUlp}f2tii!#eNoZi&1O)}r1dU#i_VDmfgc5%6!ong- znH;)LeSHI*J(~wHF}o2p(81HwgL8k_YqYBl}WHOn~{2C0rYfxQX9ZZ?xrf3KV@JD%h`5>3e)%sw)f`L%P|Lf}hrDxCR z)gb()^7C8$t^px5b?Oua)5<}lw7yBx07v$GFn<%$<~8!@J>))UmqkTrNFs!xge3ezda@g zhYy}Cs^nm#rR{^x&Q6}a)zeD4qM`y45)vUZ^Ct#Lnt&IM`}h9{rKP33`KJuM?dD5L z{ve^2mX?a}3Rz*G^bW7BttI4Ky?X8A+s$L#zI_J;1O!qgCB-m(x;r6<7UZU;CRn+0 zHLv~{gh;CbPZ%6Vs?gM?59pmBLxyju?o+lsusy(qC*!aB~x ziLUe>OQAuwwziO;pUZkL9Ew#{RVX_Ktsb5PRHe( zAX8Hl#(@}Lz_SV`0=Nd}BK`8^E4Xo^0_M$|t0uUb1ehzE$B*mK!-tPX6Fsi$*@KG?c28y8@p%N z+0o+S3ovu$3`SdHVR$}5OWDm z2r@A-fo02dyI9PxbEHv7fO|U~vzuyGt zr_}p>z`e8uKcX$c3AQVm}2v&sz-YB+wui zilfm+IJ9ShKoAQAf>>ZMX&*zi5k#8BMhGz$2n4Y}&>$9@{{gUwr|m#}5)J?W002ov JPDHLkV1hj-$M*mL literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..fede84973e1cc9fee09acd2b5eec4c4eb7758e01 GIT binary patch literal 1183 zcmV;Q1YrA#P)(0OwnPwUW@6dOi$em5PEfMM50YUwkwwN9_W`S9FQKD{kHDfz4*;u8TW? z%F1`4)iy&WlM1qul46`W^FCTyK1XL~7gv2;2^>EBDlT5sK`a*2|32SZgcgZJkV>U! zZvF%=mkamq{m4xxR{~8*pFUkpiI6@61A|n~1KbFtr>8@y zRMNV|@*5f&8ih8k_A?wfphRI|A=1*)pj5ty(NQZGz1#@w+m|BL&1Una_Tk|XN`QQY zG+bt7WkID~~&j`4J#1e(91PH*Q>~Mjtz-rsZjn)xUgMzv0V-3a(M;!GoXC z*w{qRf#~RGHnc4*Ezxt}?%f}Fs^ou=krVgf!(Y(X*Nc>tHD{fjokdyMQM%1|V_Q;* z$;nB~&CLn~$m(y0@z~YLTxsbWsIC17a(T4h_SDo=p&lO}k2`n1_uFrAaS?589YLoT zK@pJ2WVmwW3&h361*Rk_iiX#%K;|kcdId8xGt9OGML;5vP!haA2$EzksIkEeT3A?s zR;$Cgb00%4m;3EQ9X+P_$)VatH>CkfIQa;!uqIFYMkhx>FD#5N<>Wgd4ky07)T=i(f-|`BCWgt#CS> zT#QIaNPt%RDf;`rqu0=tm6d?UG7&JDZbPLyNb9Sst2lqYF60A>q!|}3)S;~GEn4h$ zJB}QAGvI3{6M?=ylR!YNejDTCx_4{{`rDEt^tdt94Asg-HG1h>eX!Lqk2Ps@@Z1Fem~>W0bG51Y+KzX-Y9PfScuziDs(iuNTdOJ1(k>T*<9pF~npk}x;% zCynMqEH5u_9|3}?sVP)cRHCiTfc*S?DkDyK_G~RJ4u=C~^Dw;wdpy(B=u@XoQp0m| zbCH>uv1!YVj994gPUoMj<^&f;N$!5)L=`le51`T1ASNb8Xd{M_5(vm;zrDQ!SFakF zSAHE#1Y{}CVoy#wL{Ba%-&%(-pc_HflSf@vkRaf4xv{roZM@Ix!(Wz1SaJn^wPYf& zNA*19X^9(T^YG$V`zXsa|3CsnKeu;-NF)})J!lDd3MK+`HW#D`u@FhbEW-kZ&+En9 xIGZ0Z5m<0|XxT~FUAfRsM1(-N5g`z6{0%6wgZLj_Gxz`i002ovPDHLkV1myBE6)G` literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..fd9748ff8d81a8708ba745a850a6d7b416773781 GIT binary patch literal 2405 zcmZ{mdpHw{8^?#1Sx4?63$>G6_Djd5AuD5UX~=cN;w+5D8ezw^tQ^-((&&)LwYenb z-du8xGlv#wWA3>o$z3>pfA+`u<9*-f`#kUS{`G!8?;CGxZ7vEv2?hWFqLvmYyTj`7 z{{S65%##_RdH{e3%o2rkAZLAfCSvROQNHWIP#kG%=b#EL7OBg*SA&`|puQ}OIqP>% zhb~t}%Ki1aR_v%a@=mVmgW-6PBvJ~9Q^5J;Q@EW4g%Y}`0whvGyGFgPQct;myDu*9 z&F(ns&)MumMDx>2Z6h-yUn38~|3UPIUM_{4I(6f;MC=p1(EivXL68|(Sx)VqM?N6t zCovx)h{0f5I2D7zGE5UbV6!Z7k_CnasG01HU#T142SVFclt`jg6y( zeSFTTv`;Z>Y9@!uZVa<_t!{aHPi%bQy(Q>A3^?7A@f$AX(W5>H5hU3@Fd$)-ejaIS ztL#)7*D}#&?oiEv=xDaA&2v`Xol|P#a=CN2cQ(ExW@Ql~wkFJn>w{#ScN-E4fh+6l zUN%i?eMFNYj_3Nq5x3^3Q^I13lfRE~jH(2Ox!f~Qs3;kPQ$?OBb{P@{rLbx{2+erz6!9oynKjfZc|-b8(KuXPou%ZZ*ZG|SH1iM&|o8q-_^gLUl9WW zfp`MJ&d}QWCKI1KsW(s`)Z>+4_E^yE4Ozm`(NRwC&bZX>=;-L9w6q6vb8@WfHi0`m zPe^i?LQ1iJR96S#?=ssTcJ_4;$tSyK7+e*^Nll z+}hc<`zCa43YnaotnNZQ`Iq#)ob2V1`sRn3nLMf8cdb|~^wUu1RCMhuxBB)lbKb_y z{|oppnVj5QfnNGU=#}Uq7Yd0yte;4w4le15GhqNs=DzTogMDy*5fMdGL|kj2XFboz zctC8@|C`-ZbiWX&yzy1DJy~JU0`2|}18M?&P77LJh!&TUN`#tI8NcJ4``-XiB+_r# zKRU#|&o(~{IDmHib8C*ljXRiPBQCfZ7 z4~UxDqVE=aZWW)=P`N4z1~;M5=w!)8yj(do0d zhe}Ocxku4WKR68Y zYsLaNQJ>qmdHZpB_BJBE>Wq}x36&rw>%RK&wQ>E zG172Ndm*bTVjuD2D$2A2@4ZZCbQc$c=-?`Zlatdm$PU7AX-1`Gs7xOgo|`)Gs#!7V z`>dw&xmj&&EnFnw1C}}GPwA?vstpXs9F99)sF(Sv%12F+mYC?NgmB&T>Y**FPPb=W zNO!&|Y7`il+B(I2pX#$?RjxEKJ*}IeM~eA`q+83aqqFmM zQCU^(M51Ph=kf@AttYR-+;8%vP<6MRz5V#agy#w*Ec4Jal@7v&6MV58pqPkA$)k+A zo}3_ecVT7trZ#;nsFx0tmVK^+!MeD}8=JIMc%!urQPs6E+88SK?OO?^24<3CqE<3F z*+Fa@8++B3Vqvh!v4Q7RtPn>Oz>K@?s>`W?SM06Y`o&)CaSHbm{L|=x|72guD=v;J z+w+fR=bA)!O~13p#7YNF*9R5A_)Nrfw}8;milxRXZ=lE8+`u#L)sF;zR{Vz#$qeTE z!>i1Bnn%aF`)4^pKPG;at zk4!1{+22RUQ0qPQU=Pm%lUK;SoGM4G?e&m@W-?Uk7wsQW{uf*{G}U`FFbz|}Y_a|> z9{O7reHUHa?3O%KNPXN90t`4j=nLaIIO)=8?~n8KUFGyjwEt4%8V6eYXM253^Mm+S zp#Z_*<4d>GzE@M9utb*(vq)c;Y$AGQ?bOi$Yvd3W-dzN&{t26HC35Pw$d-tV0*p%>czwN31DezjVd?xi1`--SCnG_ literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..5c853664ae3963bb579b1e723f8dd28198426dd6 GIT binary patch literal 3923 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 literal 0 HcmV?d00001 diff --git a/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..01894035c44c4cf0c178b12aa6ff3919383e31dd GIT binary patch literal 4920 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 Jg|cn + + + + + + diff --git a/frontend/android/app/src/main/res/values-night/styles.xml b/frontend/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..dbc9ea9 --- /dev/null +++ b/frontend/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/frontend/android/app/src/main/res/values-v31/styles.xml b/frontend/android/app/src/main/res/values-v31/styles.xml new file mode 100644 index 0000000..271e57e --- /dev/null +++ b/frontend/android/app/src/main/res/values-v31/styles.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/frontend/android/app/src/main/res/values/colors.xml b/frontend/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..cfde9b4 --- /dev/null +++ b/frontend/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #141414 + \ No newline at end of file diff --git a/frontend/android/app/src/main/res/values/styles.xml b/frontend/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..0d1fa8f --- /dev/null +++ b/frontend/android/app/src/main/res/values/styles.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/frontend/android/app/src/profile/AndroidManifest.xml b/frontend/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/frontend/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/frontend/android/build.gradle.kts b/frontend/android/build.gradle.kts new file mode 100644 index 0000000..dbee657 --- /dev/null +++ b/frontend/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/frontend/android/gradle.properties b/frontend/android/gradle.properties new file mode 100644 index 0000000..e96108c --- /dev/null +++ b/frontend/android/gradle.properties @@ -0,0 +1,6 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +# This newDsl flag was added by the Flutter template +android.newDsl=false +# This builtInKotlin flag was added by the Flutter template +android.builtInKotlin=false diff --git a/frontend/android/gradle/wrapper/gradle-wrapper.properties b/frontend/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2d428bf --- /dev/null +++ b/frontend/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip diff --git a/frontend/android/settings.gradle.kts b/frontend/android/settings.gradle.kts new file mode 100644 index 0000000..c21f0c5 --- /dev/null +++ b/frontend/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "9.0.1" apply false + id("org.jetbrains.kotlin.android") version "2.3.20" apply false +} + +include(":app") diff --git a/frontend/assets/icons/icon.png b/frontend/assets/icons/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..397b0415da3ed557b0dd2fb1dc1a89c1ef58f649 GIT binary patch literal 40360 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 RkkF}nR!9F-mX=-k{{q!;`o91G literal 0 HcmV?d00001 diff --git a/frontend/assets/icons/icon_foreground.png b/frontend/assets/icons/icon_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..9c9d291bdf3d034769c4c280fe222e18b2436598 GIT binary patch literal 27193 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 + + + + + + + + + + + + + + + diff --git a/frontend/assets/icons/splash.png b/frontend/assets/icons/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..f0347965d8d564aac412f4c9a9ac51ac0146b371 GIT binary patch literal 35932 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 $cName($sharedParams) async { + Pair navigation = await getRequestHeaders(${cName}Path, ""); + dio.options.headers = navigation.value; + return await dio.$requestType( + navigation.key, + $dioData + ); + }'''; + await _inject(filesToUpdate[1], commsMethod, logger, + importLine: importLine, importFileName: importFileName, duplicateCheck: "Future $cName"); + + // 3. ConnectComms.dart + await _inject(filesToUpdate[2], " Future $cName($sharedParams);", logger, + importLine: importLine, importFileName: importFileName, duplicateCheck: "Future $cName"); + + // 4. AppDataManager.dart + final String dataManagerMethod = ''' + @override + Future $cName($sharedParams) async { + return await connectComms.$cName($callParams); + }'''; + await _inject(filesToUpdate[3], dataManagerMethod, logger, + importLine: importLine, importFileName: importFileName, duplicateCheck: "Future $cName"); + + progress.complete('✨ Injection & Deduplication complete.'); + + final shouldFormat = logger.confirm('🎨 Run "dart format" on updated files?', defaultValue: false); + if (shouldFormat) { + for (final path in filesToUpdate) { + await Process.run('dart', ['format', path]); + } + logger.success('✅ Files formatted.'); + } +} + +/// Returns both the full import line and just the filename (e.g. user_request.dart) +Future?> _findClassData(String className, String projectName) async { + final directory = Directory('lib'); + if (!directory.existsSync()) return null; + final List files = directory.listSync(recursive: true); + for (var file in files) { + if (file is File && file.path.endsWith('.dart')) { + final content = await file.readAsString(); + if (content.contains('class $className') || content.contains('enum $className')) { + final fileName = file.path.split('/').last; + final packagePath = file.path.replaceFirst('lib/Grounded/', ''); + return { + 'line': "import 'package:$projectName/$packagePath';", + 'fileName': fileName, + }; + } + } + } + return null; +} + +Future _inject(String path, String content, Logger logger, + {bool isClass = true, String? importLine, String? importFileName, String? duplicateCheck}) async { + final file = File(path); + if (!await file.exists()) return; + String fileContent = await file.readAsString(); + + // 1. Prevent Duplicate Method/Variable + if (duplicateCheck != null && fileContent.contains(duplicateCheck)) { + return; // Silently skip methods that exist + } + + // 2. Smart Import Check: Look for the filename inside any import statement + if (importLine != null && importFileName != null) { + // Regex matches: import 'any/path/filename.dart'; or "any/path/filename.dart"; + final importRegex = RegExp("import ['\"].*?$importFileName['\"];"); + if (!importRegex.hasMatch(fileContent)) { + fileContent = "$importLine\n$fileContent"; + } + } + + if (isClass) { + final lastBrace = fileContent.lastIndexOf('}'); + if (lastBrace != -1) { + fileContent = fileContent.substring(0, lastBrace).trimRight() + '\n\n$content\n' + fileContent.substring(lastBrace); + } else { + fileContent += '\n\n$content'; + } + } else { + fileContent = fileContent.trimRight() + '\n\n$content\n'; + } + await file.writeAsString(fileContent); +} \ No newline at end of file diff --git a/frontend/bricks/api_endpoint/hooks/pubspec.yaml b/frontend/bricks/api_endpoint/hooks/pubspec.yaml new file mode 100644 index 0000000..2d5b58a --- /dev/null +++ b/frontend/bricks/api_endpoint/hooks/pubspec.yaml @@ -0,0 +1,10 @@ +name: api_endpoint_hooks +description: Hooks for the api_endpoint brick. +publish_to: none + +environment: + sdk: ">=2.12.0 <4.0.0" + +dependencies: + mason: ^0.1.0-dev.51 # Or the latest version + yaml: ^3.1.2 \ No newline at end of file diff --git a/frontend/bricks/api_endpoint/setup_brick.sh b/frontend/bricks/api_endpoint/setup_brick.sh new file mode 100644 index 0000000..cc0140f --- /dev/null +++ b/frontend/bricks/api_endpoint/setup_brick.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# setup_brick.sh +echo "🧹 Cleaning old Mason cache (Hard Refresh)..." +rm -rf .mason + +echo "📦 Fetching Hook dependencies (Yaml/Mason)..." +if [ -d "bricks/api_endpoint/hooks" ]; then + cd bricks/api_endpoint/hooks + dart pub get + cd ../../../ +else + echo "❌ Error: Hooks directory not found!" + exit 1 +fi + +echo "🧱 Getting latest Brick definition..." +mason get + +echo "✅ Ready! Run 'mason make api_endpoint'" \ No newline at end of file diff --git a/frontend/bricks/internal_memory/CHANGELOG.md b/frontend/bricks/internal_memory/CHANGELOG.md new file mode 100644 index 0000000..f0640d6 --- /dev/null +++ b/frontend/bricks/internal_memory/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.0+1 + +- TODO: Describe initial release. diff --git a/frontend/bricks/internal_memory/LICENSE b/frontend/bricks/internal_memory/LICENSE new file mode 100644 index 0000000..ba75c69 --- /dev/null +++ b/frontend/bricks/internal_memory/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/frontend/bricks/internal_memory/README.md b/frontend/bricks/internal_memory/README.md new file mode 100644 index 0000000..305fd07 --- /dev/null +++ b/frontend/bricks/internal_memory/README.md @@ -0,0 +1,92 @@ + + +# 🔐 Internal Memory Brick + +An advanced **Mason** brick designed for the **Autoreceptive** architecture. It automates the boilerplate for persistent local storage using `flutter_secure_storage` (via `autoreceptiveStorage`), handling JSON serialization for Objects and Lists, and raw storage for Strings. + +## 🚀 Purpose +This brick eliminates the manual task of: +1. Generating high-entropy, randomized secure keys. +2. Writing `get` and `set` interfaces in `ConnectInternalMemory`. +3. Implementing JSON encoding/decoding logic in `InternalMemory`. +4. Wrapping calls in `AppDataManager` to maintain the **Repository Pattern**. + +--- + +## 📂 Target Architecture +The brick targets the following specific paths in your project: +* **Interface**: `lib/Autoreceptive/memory/ConnectInternalMemory.dart` +* **Implementation**: `lib/Autoreceptive/memory/InternalMemory.dart` +* **Global Access**: `lib/Autoreceptive/informatics/AppDataManager.dart` + +--- + +## 🛠 Installation + +### 1. Register the Brick +From your project root, run: +```bash +mason add internal_memory --path ./bricks/internal_memory +``` + +### 2. Setup Hooks +The brick uses specialized Dart hooks for smart code injection and auto-formatting. +```bash +cd bricks/internal_memory/hooks && dart pub get && cd ../../../ +mason get +``` + +--- + +## 📖 Usage Guide + +Run the following command and follow the interactive prompts: +```bash +mason make internal_memory +``` + +### Variable Inputs: +| Variable | Description | Example | +| :--- | :--- | :--- | +| `function_name` | The camelCase name of the feature. | `userProfile` or `orderLocations` | +| `request_object` | The Dart type to store. | `MeDescription`, `String`, or `List` | + +--- + +## 📋 Logic Breakdown + +### 1. High-Entropy Keys +The brick generates a randomized `static const String` key at the **top** of the `InternalMemory` class to prevent key collisions and reverse-engineering: +```dart +static const String USER_PROFILE = "USER_PROFILE_x8H2_9kLp_mQz1"; +``` + +### 2. Intelligent Type Handling +The hook detects the `request_object` type and adjusts the logic: + +* **Standard Objects**: Uses `Type.fromJson(jsonDecode(s))` and `jsonEncode(data.toJson())`. +* **Lists**: Uses `.map((item) => Type.fromJson(item)).toList()` for deep serialization. +* **Strings**: Bypasses JSON logic for raw string storage. + +### 3. Smart Injection & Spacing +* **Clean Pairs**: In the interface, the getter and setter are injected as a tight block with a double-newline buffer from the previous feature. +* **Auto-Imports**: Automatically adds `import 'dart:convert';` and searches the `lib/` folder to find and add the correct model imports for your Objects. +* **Formatting**: Automatically triggers `dart format` on the `Autoreceptive` folder after injection. + +--- + +## ⚠️ Requirements +* **`autoreceptiveStorage`**: The `InternalMemory` class must have an instance of your secure storage wrapper named `autoreceptiveStorage`. +* **Model Factories**: For Objects and Lists, your models must implement: + * `factory Model.fromJson(Map json)` + * `Map toJson()` + +--- + +## 🔄 Refreshing Logic +If you update the `post_gen.dart` hook code, always run this to ensure Mason uses the latest version: +```bash +rm -rf .mason && mason get +``` + +--- diff --git a/frontend/bricks/internal_memory/brick.yaml b/frontend/bricks/internal_memory/brick.yaml new file mode 100644 index 0000000..ea86c9a --- /dev/null +++ b/frontend/bricks/internal_memory/brick.yaml @@ -0,0 +1,11 @@ +name: internal_memory +description: Generates Secure Storage logic with JSON serialization and AppDataManager sync. +version: 0.1.0+1 + +vars: + function_name: + type: string + prompt: "🖋️ What is the function name? (e.g., myDescription)" + request_object: + type: string + prompt: "📦 What is the Object type to store? (e.g., MeDescription)" \ No newline at end of file diff --git a/frontend/bricks/internal_memory/hooks/post_gen.dart b/frontend/bricks/internal_memory/hooks/post_gen.dart new file mode 100644 index 0000000..02165eb --- /dev/null +++ b/frontend/bricks/internal_memory/hooks/post_gen.dart @@ -0,0 +1,186 @@ +import 'dart:io'; +import 'dart:convert'; +import 'dart:math'; +import 'package:mason/mason.dart'; +import 'package:yaml/yaml.dart'; + +void run(HookContext context) async { + final logger = context.logger; + final String functionName = context.vars['function_name'] ?? ''; + final String requestObject = context.vars['request_object'] ?? ''; + + final cName = functionName.camelCase; + final pName = functionName.pascalCase; + final uName = functionName.snakeCase.toUpperCase(); + + final secureKeyValue = _generateSecureKey(uName); + + String projectName = 'your_project'; + final pubspecFile = File('pubspec.yaml'); + if (pubspecFile.existsSync()) { + final doc = loadYaml(pubspecFile.readAsStringSync()); + projectName = doc['name'] ?? projectName; + } + + final bool isList = requestObject.startsWith('List<'); + final bool isString = requestObject.toLowerCase() == 'string'; + + String baseType = requestObject; + if (isList) { + baseType = requestObject.substring(5, requestObject.length - 1); + } + + final files = [ + 'lib/Grounded/memory/ConnectInternalMemory.dart', + 'lib/Grounded/memory/InternalMemory.dart', + 'lib/Grounded/informatics/AppDataManager.dart' + ]; + + String? importLine; + String? importFileName; + if (!isString) { + final result = await _findClassData(baseType, projectName); + importLine = result?['line']; + importFileName = result?['fileName']; + } + + // --- Implementation Logic --- + String getLogic = ""; + String setLogic = ""; + + if (isString) { + getLogic = "return await groundedStorage.read(key: $uName) ?? '';"; + setLogic = "await groundedStorage.write(key: $uName, value: data);"; + } else if (isList) { + getLogic = ''' + final dataString = await groundedStorage.read(key: $uName); + if (dataString != null && dataString.isNotEmpty) { + return (jsonDecode(dataString) as List) + .map((item) => $baseType.fromJson(item)) + .toList(); + } + return [];'''; + setLogic = ''' + final jsonString = jsonEncode(data.map((item) => item.toJson()).toList()); + await groundedStorage.write(key: $uName, value: jsonString);'''; + } else { + getLogic = ''' + final jsonString = await groundedStorage.read(key: $uName); + if (jsonString != null && jsonString.isNotEmpty) { + return $baseType.fromJson(jsonDecode(jsonString)); + } + return $baseType();'''; + setLogic = ''' + await groundedStorage.write(key: $uName, value: jsonEncode(data.toJson()));'''; + } + + // --- 1. ConnectInternalMemory (No space between the pair) --- + final interfaceCode = " Future<$requestObject> get$pName();\n Future set$pName($requestObject data);"; + await _inject(files[0], interfaceCode, logger, importLine: importLine, importFileName: importFileName, duplicateCheck: "get$pName()"); + + // --- 2. InternalMemory (Key spacing at TOP) --- + final String keyLine = '\n\n static const String $uName = "$secureKeyValue";\n'; + final String methods = ''' + @override + Future<$requestObject> get$pName() async { + $getLogic + } + + @override + Future set$pName($requestObject data) async { + $setLogic + }'''; + + await _injectInternalMemory(files[1], keyLine, methods, logger, importLine: importLine, importFileName: importFileName, duplicateCheck: "get$pName()"); + + // --- 3. AppDataManager --- + final String wrapper = ''' + @override + Future<$requestObject> get$pName() async { + return await connectInternalMemory.get$pName(); + } + + @override + Future set$pName($requestObject data) async { + return await connectInternalMemory.set$pName(data); + }'''; + await _inject(files[2], wrapper, logger, importLine: importLine, importFileName: importFileName, duplicateCheck: "get$pName()"); + + // --- 4. Format --- + try { + await Process.run('dart', ['format', 'lib/Grounded/']); + } catch (_) {} + + logger.success('✨ Internal Memory synced with corrected spacing.'); +} + +String _generateSecureKey(String base) { + final random = Random.secure(); + final values = List.generate(12, (i) => random.nextInt(256)); + return "${base}_${base64Url.encode(values).replaceAll('=', '').substring(0, 16)}"; +} + +Future?> _findClassData(String className, String projectName) async { + final dir = Directory('lib'); + if (!dir.existsSync()) return null; + for (var f in dir.listSync(recursive: true)) { + if (f is File && f.path.endsWith('.dart')) { + final content = await f.readAsString(); + if (content.contains('class $className')) { + return { + 'line': "import 'package:$projectName/${f.path.replaceFirst('lib/', '')}';", + 'fileName': f.path.split('/').last, + }; + } + } + } + return null; +} + +Future _inject(String path, String content, Logger logger, {String? importLine, String? importFileName, String? duplicateCheck}) async { + final file = File(path); + if (!await file.exists()) return; + String fileContent = await file.readAsString(); + if (duplicateCheck != null && fileContent.contains(duplicateCheck)) return; + + if (importLine != null && importFileName != null && !fileContent.contains(importFileName)) { + fileContent = "$importLine\n$fileContent"; + } + + final lastBrace = fileContent.lastIndexOf('}'); + if (lastBrace != -1) { + // Inject with double newlines between previous content and new content + fileContent = fileContent.substring(0, lastBrace).trimRight() + '\n\n$content\n' + fileContent.substring(lastBrace); + await file.writeAsString(fileContent); + } +} + +Future _injectInternalMemory(String path, String keyLine, String methods, Logger logger, {String? importLine, String? importFileName, String? duplicateCheck}) async { + final file = File(path); + if (!await file.exists()) return; + String content = await file.readAsString(); + + if (duplicateCheck != null && content.contains(duplicateCheck)) return; + + if (importLine != null && importFileName != null && !content.contains(importFileName)) { + content = "$importLine\n$content"; + } + + if (!content.contains("import 'dart:convert';")) { + content = "import 'dart:convert';\n$content"; + } + + // Inject Key at the top with forced extra spacing + final classStart = content.indexOf('{'); + if (classStart != -1) { + content = content.substring(0, classStart + 1) + "$keyLine" + content.substring(classStart + 1); + } + + // Inject Methods at the bottom + final lastBrace = content.lastIndexOf('}'); + if (lastBrace != -1) { + content = content.substring(0, lastBrace).trimRight() + "\n\n$methods\n" + content.substring(lastBrace); + } + + await file.writeAsString(content); +} \ No newline at end of file diff --git a/frontend/bricks/internal_memory/hooks/pubspec.yaml b/frontend/bricks/internal_memory/hooks/pubspec.yaml new file mode 100644 index 0000000..4f79439 --- /dev/null +++ b/frontend/bricks/internal_memory/hooks/pubspec.yaml @@ -0,0 +1,8 @@ +name: internal_memory_hooks +description: Hooks for internal_memory. +publish_to: none +environment: + sdk: ">=3.0.0 <4.0.0" +dependencies: + mason: ^0.1.0-dev.51 + yaml: ^3.1.2 \ No newline at end of file diff --git a/frontend/bricks/mvvc_template/CHANGELOG.md b/frontend/bricks/mvvc_template/CHANGELOG.md new file mode 100644 index 0000000..f0640d6 --- /dev/null +++ b/frontend/bricks/mvvc_template/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.0+1 + +- TODO: Describe initial release. diff --git a/frontend/bricks/mvvc_template/LICENSE b/frontend/bricks/mvvc_template/LICENSE new file mode 100644 index 0000000..ba75c69 --- /dev/null +++ b/frontend/bricks/mvvc_template/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/frontend/bricks/mvvc_template/README.md b/frontend/bricks/mvvc_template/README.md new file mode 100644 index 0000000..319f2e8 --- /dev/null +++ b/frontend/bricks/mvvc_template/README.md @@ -0,0 +1,27 @@ +# mvvc_template + +[![Powered by Mason](https://img.shields.io/endpoint?url=https%3A%2F%2Ftinyurl.com%2Fmason-badge)](https://github.com/felangel/mason) + +A new brick created with the Mason CLI. + +_Generated by [mason][1] 🧱_ + +## Getting Started 🚀 + +This is a starting point for a new brick. +A few resources to get you started if this is your first brick template: + +- [Official Mason Documentation][2] +- [Code generation with Mason Blog][3] +- [Very Good Livestream: Felix Angelov Demos Mason][4] +- [Flutter Package of the Week: Mason][5] +- [Observable Flutter: Building a Mason brick][6] +- [Meet Mason: Flutter Vikings 2022][7] + +[1]: https://github.com/felangel/mason +[2]: https://docs.brickhub.dev +[3]: https://verygood.ventures/blog/code-generation-with-mason +[4]: https://youtu.be/G4PTjA6tpTU +[5]: https://youtu.be/qjA0JFiPMnQ +[6]: https://youtu.be/o8B1EfcUisw +[7]: https://youtu.be/LXhgiF5HiQg diff --git a/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/Connect{{screen}}.dart b/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/Connect{{screen}}.dart new file mode 100644 index 0000000..6692240 --- /dev/null +++ b/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/Connect{{screen}}.dart @@ -0,0 +1,2 @@ +abstract class Connect{{screen}}{ +} \ No newline at end of file diff --git a/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/View{{screen}}.dart b/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/View{{screen}}.dart new file mode 100644 index 0000000..9ae0130 --- /dev/null +++ b/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/View{{screen}}.dart @@ -0,0 +1,14 @@ +import 'package:flutter/cupertino.dart'; + +import '../parent/ParentViewModel.dart'; +import 'Connect{{screen}}.dart'; + +class View{{screen}} extends ParentViewModel { + Connect{{screen}} connection; + + View{{screen}}(BuildContext context, this.connection) : super(context); + + // Business logic goes here. Guard every network call with hasNetwork, wrap + // it in showLoading/closeLoading, and push results back through + // `connection` rather than touching widgets directly. +} diff --git a/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/{{screen}}.dart b/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/{{screen}}.dart new file mode 100644 index 0000000..03046fb --- /dev/null +++ b/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/{{screen}}.dart @@ -0,0 +1,11 @@ + +import 'package:flutter/material.dart'; +import '{{screen}}State.dart'; + +class {{screen}} extends StatefulWidget{ + const {{screen}} ({Key? key}) : super(key: key); + + @override + State<{{screen}}> createState() => {{screen}}State(); + +} \ No newline at end of file diff --git a/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/{{screen}}State.dart b/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/{{screen}}State.dart new file mode 100644 index 0000000..69aa4f8 --- /dev/null +++ b/frontend/bricks/mvvc_template/__brick__/lib/{{project}}/see/{{screen.lowerCase()}}/{{screen}}State.dart @@ -0,0 +1,77 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import 'package:Grounded/Grounded/about/internal/application/TextType.dart'; +import 'package:Grounded/Grounded/designs/Responsive.dart'; +import 'package:Grounded/Grounded/designs/Shell.dart'; +import 'package:Grounded/Grounded/designs/text/Text.dart'; +import 'package:Grounded/Grounded/utils/Colors.dart'; + +import 'Connect{{screen}}.dart'; +import '{{screen}}.dart'; +import 'View{{screen}}.dart'; + +class {{screen}}State extends State<{{screen}}> implements Connect{{screen}} { + View{{screen}}? _model; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => View{{screen}}(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => LayoutBuilder( + builder: (BuildContext context, BoxConstraints viewportConstraints) { + return Responsive( + mobile: _mobileView(viewportConstraints), + tablet: _tabletView(viewportConstraints), + desktop: _desktopView(viewportConstraints), + ); + }, + ), + ); + } + + void _initiate() { + // Load initial data through _model here; push results back via the + // Connect{{screen}} callbacks below. + } + + void _onBack() { + Navigator.pop(context); + } + + Widget _mobileView(BoxConstraints viewportConstraints) { + return Sheet( + eyebrow: "Grounded", + title: "{{screen}}", + onBack: _onBack, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + displayTitle("{{screen}}"), + const SizedBox(height: 14), + text( + "Replace this with the screen content.", + 14, + TextType.Regular, + color: colorGrey2, + height: 1.55, + ), + ], + ), + ); + } + + Widget _tabletView(BoxConstraints viewportConstraints) { + return _mobileView(viewportConstraints); + } + + Widget _desktopView(BoxConstraints viewportConstraints) { + return _mobileView(viewportConstraints); + } +} diff --git a/frontend/bricks/mvvc_template/brick.yaml b/frontend/bricks/mvvc_template/brick.yaml new file mode 100644 index 0000000..19ac2cd --- /dev/null +++ b/frontend/bricks/mvvc_template/brick.yaml @@ -0,0 +1,38 @@ +name: mvvc_template +description: A new brick created with the Mason CLI. + +# The following defines the brick repository url. +# Uncomment and update the following line before publishing the brick. +# repository: https://github.com/my_org/my_repo + +# The following defines the version and build number for your brick. +# A version number is three numbers separated by dots, like 1.2.34 +# followed by an optional build number (separated by a +). +version: 0.1.0+1 + +# The following defines the environment for the current brick. +# It includes the version of mason that the brick requires. +environment: + mason: ^0.1.1 + +# Variables specify dynamic values that your brick depends on. +# Zero or more variables can be specified for a given brick. +# Each variable has: +# * a type (string, number, boolean, enum, array, or list) +# * an optional short description +# * an optional default value +# * an optional list of default values (array only) +# * an optional prompt phrase used when asking for the variable +# * a list of values (enums only) +# * an optional separator (list only) +vars: + project: + type: string + description: The project name + default: Grounded + prompt: What is your project name? + screen: + type: string + description: The name of the new screen + default: ActivityNew + prompt: What is your file name? diff --git a/frontend/design.md b/frontend/design.md new file mode 100644 index 0000000..dd0aada --- /dev/null +++ b/frontend/design.md @@ -0,0 +1,55 @@ +# Grounded — Design Language + +Reference: black device chrome, a white content sheet that rises into it with a +large corner radius, oversized light display titles, tiny grey all-caps metadata +labels, pill chips, and a lot of whitespace. The app reads as a record, not a +dashboard. + +## Type — General Sans + +| Role | Family | Size | Use | +|---|---|---|---| +| Display | `TextType.Light` | 30–44 | Screen titles. Always light, never bold. | +| Title | `TextType.Bold` | 17–20 | Card and section titles. | +| Body | `TextType.Regular` | 13–14, `height: 1.5` | Descriptions, copy. | +| Label | `TextType.Bold` | 10–11, `letterSpacing: 0.8–1.2`, uppercase, `colorGrey2` | Metadata above a value. | +| Metric | `TextType.Bold` | 24–34 | Debt score, counts. | + +The label/value pair is the core unit: a tiny grey all-caps label sitting +directly above a large dark value. Used everywhere numbers appear. + +## Geometry + +- Sheet radius `28`, card radius `16`, chip radius `999`, control radius `12`. +- Screen padding `20` horizontal. Card padding `16`. Section gap `28`. +- Hairlines at `colorDivider` (black 7%), borders at `colorBorder` (black 8%). +- One soft shadow only: `0 4 18 rgba(0,0,0,0.03)`. No coloured glows. + +## Colour + +Near-black ink (`colorPrimaryDark #141414`) on warm off-white +(`colorPrimaryLight #EDEEEA`), with a single green accent (`colorPrimary +#2F6F4E`). Enforcement tiers own the rest of the palette: + +| Standing | Colour | +|---|---| +| Good | green `#2F6F4E` | +| Warned | amber `#C98A04` | +| Grounded | rust `#B5442F` | +| Lockdown | oxblood `#7A1F14` | + +The tier colour is used consistently — header, chip, meter, queue accent — so +the standing is legible without reading the word. + +## Structure + +Every screen is: black `headerBar` (back, title, action) → white sheet with a +`28` top radius → `sectionHeader` (label + light display title) → content. + +## Rules + +- Display titles are Light. Bold is for labels and metrics only. +- Numbers get a label above them, never beside them. +- Chips carry state, never actions. +- One primary button per screen; everything else is outlined or text. +- Empty states are plain. An empty overdue queue is good news, not a party. diff --git a/frontend/fonts/LICENSE-GeneralSans.txt b/frontend/fonts/LICENSE-GeneralSans.txt new file mode 100644 index 0000000..f14ebba --- /dev/null +++ b/frontend/fonts/LICENSE-GeneralSans.txt @@ -0,0 +1,57 @@ +Fontshare EULA + +---—---------------------------------—------------------------------ +Free Font - End User License Agreement (FF EULA) +---—---------------------------------—------------------------------ +Notice to User +Indian Type Foundry designs, produces and distributes font software as digital fonts to end users worldwide. In addition to commercial fonts that are available for a fee, ITF also offers several fonts which can be used free of charge. The free fonts are distributed through a dedicated platform called www.fontshare.com (“Fontshare”) to end users worldwide. These free fonts are subject to this legally binding EULA between the Indian Type Foundry (“Indian Type Foundry” or “Licensor”) and you (“Licensee”).  +You acknowledge that the Font Software and designs embodied therein are protected by the copyright, other intellectual property rights and industrial property rights and by international treaties. They are and remain at all times the intellectual property of the Indian Type Foundry. +In addition to direct download, Fontshare also offers these free fonts via Fonthsare API using a code. In this case, the Font Software is delivered directly from the servers used by Indian Type Foundry to the Licensee's website, without the Licensee having to download the Font Software. +By downloading, accessing the API, installing, storing, copying or using one of any Font Software, you agree to the following terms.  + +Definitions +“Font Software” refers to the set of computer files or programs released under this license that instructs your computer to display and/or print each letters, characters, typographic designs, ornament and so forth. Font Software includes all bitmap and vector representations of fonts and typographic representations and embellishments created by or derived from the Font Software.  +“Original Version” refers to the Font Software as distributed by the Indian Type Foundry as the copyright holder.  +“Derivative Work” refers to the pictorial representation of the font created by the Font Software, including typographic characters such as letters, numerals, ornaments, symbols, or punctuation and special characters. + +01. Grant of License +You are hereby granted a non-exclusive, non-assignable, non-transferrable, terminable license to access, download and use the Font Software for your personal or commercial use for an unlimited period of time for free of charge.  +You may use the font Software in any media (including Print, Web, Mobile, Digital, Apps, ePub, Broadcasting and OEM) at any scale, at any location worldwide.  +You may use the Font Software to create logos and other graphic elements, images on any surface, vector files or other scalable drawings and static images.  +You may use the Font Software on any number of devices (computer, tablet, phone). The number of output devices (Printers) is not restricted.  +You may make only such reasonable number of back-up copies suitable to your permitted use.  +You may but are not required to identify Indian Type Foundry Fonts in your work credits.  + +02. Limitations of usage +You may not modify, edit, adapt, translate, reverse engineer, decompile or disassemble, alter or otherwise copy the Font Software or the designs embodied therein in whole or in part, without the prior written consent of the Licensor.  +The Fonts may not - beyond the permitted copies and the uses defined herein - be distributed, duplicated, loaned, resold or licensed in any way, whether by lending, donating or give otherwise to a person or entity. This includes the distribution of the Fonts by e-mail, on USB sticks, CD-ROMs, or other media, uploading them in a public server or making the fonts available on peer-to-peer networks. A passing on to external designers or service providers (design agencies, repro studios, printers, etc.) is also not permitted.  +You are not allowed to transmit the Font Software over the Internet in font serving or for font replacement by means of technologies such as but not limited to EOT, Cufon, sIFR or similar technologies that may be developed in the future without the prior written consent of the Licensor.  + +03. Embedding +You may embed the Font Software in PDF and other digital documents provided that is done in a secured, read-only mode. It must be ensured beyond doubt that the recipient cannot use the Font Software to edit or to create new documents. The design data (PDFs) created in this way and under these created design data (PDFs) may be distributed in any number.  +The extraction of the Font Software in whole or in part is prohibited.  + +04. Third party use, Commercial print service provider +You may include the Font Software in a non-editable electronic document solely for printing and display purposes and provide that electronic document to the commercial print service provider for the purpose of printing. If the print service needs to install the fonts, they too need to download the Font Software from the Licensor's website. + +05. Derivative Work +You are allowed to make derivative works as far as you use them for your personal or commercial use. However, you cannot modify, make changes or reverse engineer the original font software provided to you. Any derivative works are the exclusive property of the Licensor and shall be subject to the terms and conditions of this EULA. Derivative works may not be sub-licensed, sold, leased, rented, loaned, or given away without the express written permission of the Licensor.  + +06. Warranty and Liability +BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, INDIAN TYPE FOUNDRY MAKES NO WARRANTIES, EXPRESS OR IMPLIED AS TO THE MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR OTHERWISE. THE FONT SOFTWARE WAS NOT MANUFACTURED FOR USE IN MANUFACTURING CONTROL DEVICES OR NAVIGATION DEVICES OR IN CIRCUMSTANCES THAT COULD RESULT IN ENVIRONMENTAL DAMAGE OR PERSONAL INJURY. WITHOUT LIMITING THE FOREGOING, INDIAN TYPE FOUNDRY SHALL IN NO EVENT BE LIABLE TO THE LICENSED USER OR ANY OTHER THIRD PARTY FOR ANY DIRECT, CONSEQUENTIAL OR INCIDENTAL DAMAGES, INCLUDING DAMAGES FROM LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION NOR FOR LOST PROFITS OR SAVINGS ARISING OUT OF THE USE OR INABILITY TO USE THE PRODUCT EVEN IF NOTIFIED IN ADVANCE, UNDER NO CIRCUMSTANCES SHALL INDIAN TYPE FOUNDRY’S LIABILITY EXCEED THE REPLACEMENT COST OF THE SOFTWARE.  +IF LICENSEE CHOOSES TO ACCESS THE FONT SOFTWARE THROUGH A CODE (API), IT MAY HAVE A DIRECT IMPACT ON LICENSEE'S WEBSITE OR APPLICATIONS. INDIAN TYPE FOUNDRY IS NOT RESPONSIBLE OR LIABLE FOR ANY INTERRUPTION, MALFUNCTION, DOWNTIME OR OTHER FAILURE OF THE WEBSITE OR ITS API. + +07. Updates, Maintenance and Support Services +Licensor will not provide you with any support services for the Software under this Agreement. + +08. Termination  +Any breach of the terms of this agreement shall be a cause for termination, provided that such breach is notified in writing to the Licensee by the Licensor and the Licensee failed to rectify the breach within 30 days of the receipt of such notification.  +In the event of termination and without limitation of any remedies under law or equity, you must delete the Font Software and all copies thereof. Proof of this must be provided upon request of the Licensor.   +We reserve the right to claim damages for the violation of the conditions.  + +09. Final Provisions +If individual provisions of this agreement are or become invalid, the validity of the remaining provisions shall remain unaffected. Invalid provisions shall be replaced by mutual agreement by such provisions that are suitable to achieve the desired economic purpose, taking into account the interests of both parties. The same shall apply mutatis mutandis to the filling of any gaps which may arise in this agreement. +This contract is subject to laws of the Republic of India. Place of performance and exclusive place of jurisdiction for all disputes between the parties arising out of or in connection with this contract is, as far as legally permissible, Ahmedabad, India. +-  +Last Updated on 22 March 2021 +Copyright 2021 Indian Type Foundry. All rights reserved.  \ No newline at end of file diff --git a/frontend/fonts/bold.ttf b/frontend/fonts/bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8fcaeca4b8743f8cf005df0120228aff339d7ee2 GIT binary patch literal 65960 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 i% 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 W!@~WizAsH$)0fYTh}-YV8~zW(HX=v> literal 0 HcmV?d00001 diff --git a/frontend/ios/.gitignore b/frontend/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/frontend/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/frontend/ios/Flutter/AppFrameworkInfo.plist b/frontend/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..391a902 --- /dev/null +++ b/frontend/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/frontend/ios/Flutter/Debug.xcconfig b/frontend/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/frontend/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/frontend/ios/Flutter/Release.xcconfig b/frontend/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/frontend/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/frontend/ios/Runner.xcodeproj/project.pbxproj b/frontend/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d20f5e9 --- /dev/null +++ b/frontend/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,644 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + 7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = nya.grounded; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = nya.grounded.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = nya.grounded.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = nya.grounded.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = nya.grounded; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = nya.grounded; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/frontend/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/frontend/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/frontend/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/frontend/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/frontend/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/frontend/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..c3fedb2 --- /dev/null +++ b/frontend/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/ios/Runner.xcworkspace/contents.xcworkspacedata b/frontend/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/frontend/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/frontend/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/frontend/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/frontend/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/frontend/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/frontend/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/frontend/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/frontend/ios/Runner/AppDelegate.swift b/frontend/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..c30b367 --- /dev/null +++ b/frontend/ios/Runner/AppDelegate.swift @@ -0,0 +1,16 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } +} diff --git a/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d0d98aa --- /dev/null +++ b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1 @@ +{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}} \ No newline at end of file diff --git a/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..a9992689e75bae1139a751b1bcbfb7bab28b1159 GIT binary patch literal 40572 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

ojY5t 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 KER#2T_x}I@wh~kT literal 0 HcmV?d00001 diff --git a/frontend/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/frontend/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/frontend/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/frontend/ios/Runner/Base.lproj/LaunchScreen.storyboard b/frontend/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..8d2b7d5 --- /dev/null +++ b/frontend/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/ios/Runner/Base.lproj/Main.storyboard b/frontend/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/frontend/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/ios/Runner/Info.plist b/frontend/ios/Runner/Info.plist new file mode 100644 index 0000000..899903d --- /dev/null +++ b/frontend/ios/Runner/Info.plist @@ -0,0 +1,72 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Grounded + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + grounded + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIStatusBarHidden + + + diff --git a/frontend/ios/Runner/Runner-Bridging-Header.h b/frontend/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/frontend/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/frontend/ios/Runner/SceneDelegate.swift b/frontend/ios/Runner/SceneDelegate.swift new file mode 100644 index 0000000..b9ce8ea --- /dev/null +++ b/frontend/ios/Runner/SceneDelegate.swift @@ -0,0 +1,6 @@ +import Flutter +import UIKit + +class SceneDelegate: FlutterSceneDelegate { + +} diff --git a/frontend/ios/RunnerTests/RunnerTests.swift b/frontend/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/frontend/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/frontend/lib/Grounded/about/external/data/Commitment.dart b/frontend/lib/Grounded/about/external/data/Commitment.dart new file mode 100644 index 0000000..0e12def --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/Commitment.dart @@ -0,0 +1,129 @@ +import '../../internal/application/CommitmentClass.dart'; +import '../../internal/application/CommitmentStatus.dart'; +import '../../internal/application/CommitmentType.dart'; +import '../../internal/application/EnergyCost.dart'; +import '../../internal/application/ProofType.dart'; + +/// Something the user said they would do. A due *window*, not a due date — +/// "Mon 06:00-08:00" beats "Monday", because a window can actually close. +class Commitment { + String? id; + + CommitmentType type; + + CommitmentClass commitmentClass; + + String title; + + String category; + + DateTime? dueStart; + + DateTime? dueEnd; + + int estMinutes; + + EnergyCost energy; + + ProofType proofType; + + /// Minimum foreground minutes when proofType is Timer. + int proofTimerMinutes; + + String rrule; + + String? parentId; + + CommitmentStatus status; + + int deferralCount; + + Commitment({ + this.id, + this.type = CommitmentType.TASK, + this.commitmentClass = CommitmentClass.Standard, + this.title = "", + this.category = "", + this.dueStart, + this.dueEnd, + this.estMinutes = 0, + this.energy = EnergyCost.Medium, + this.proofType = ProofType.Honour, + this.proofTimerMinutes = 0, + this.rrule = "", + this.parentId, + this.status = CommitmentStatus.Open, + this.deferralCount = 0, + }); + + factory Commitment.fromJson(Map json) { + return Commitment( + id: json['id'], + type: getCommitmentType(json['type']), + commitmentClass: getCommitmentClass(json['commitmentClass']), + title: json['title'] ?? "", + category: json['category'] ?? "", + dueStart: DateTime.tryParse(json['dueStart'] ?? ""), + dueEnd: DateTime.tryParse(json['dueEnd'] ?? ""), + estMinutes: json['estMinutes'] ?? 0, + energy: getEnergyCost(json['energy']), + proofType: getProofType(json['proofType']), + proofTimerMinutes: json['proofTimerMinutes'] ?? 0, + rrule: json['rrule'] ?? "", + parentId: json['parentId'], + status: getCommitmentStatus(json['status']), + deferralCount: json['deferralCount'] ?? 0, + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['type'] = type.name; + data['commitmentClass'] = commitmentClass.name; + data['title'] = title; + data['category'] = category; + data['dueStart'] = dueStart?.toIso8601String(); + data['dueEnd'] = dueEnd?.toIso8601String(); + data['estMinutes'] = estMinutes; + data['energy'] = energy.name; + data['proofType'] = proofType.name; + data['proofTimerMinutes'] = proofTimerMinutes; + data['rrule'] = rrule; + data['parentId'] = parentId; + data['status'] = status.name; + data['deferralCount'] = deferralCount; + return data; + } + + /// The window has closed. A commitment goes Overdue at close — it never + /// silently rolls over to today. + bool get windowClosed { + if (dueEnd == null) { + return false; + } + return DateTime.now().isAfter(dueEnd!); + } + + /// Whole days past the close of the window; 0 while still open. + int get daysOverdue { + if (dueEnd == null || !windowClosed) { + return 0; + } + return DateTime.now().difference(dueEnd!).inDays; + } + + /// Whether completing right now would count as a late complete rather than a + /// clean one. Late is recorded distinctly and never reduces debt to zero. + bool get wouldBeLate { + return windowClosed; + } + + /// Non-negotiables are never deferrable, at any count. + bool deferrableUnder(int maxDeferrals) { + if (commitmentClass == CommitmentClass.NonNegotiable) { + return false; + } + return deferralCount < maxDeferrals; + } +} diff --git a/frontend/lib/Grounded/about/external/data/CommitmentEvent.dart b/frontend/lib/Grounded/about/external/data/CommitmentEvent.dart new file mode 100644 index 0000000..90a447f --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/CommitmentEvent.dart @@ -0,0 +1,53 @@ +import '../../internal/application/EventType.dart'; + +/// Append-only history. This log — not the status field — is the source of +/// truth, and it is what makes excuse analysis and honest history possible. +class CommitmentEvent { + String? id; + + String commitmentId; + + EventType event; + + DateTime? at; + + String excuseText; + + String? excuseClusterId; + + String? proofRef; + + CommitmentEvent({ + this.id, + this.commitmentId = "", + this.event = EventType.CREATED, + this.at, + this.excuseText = "", + this.excuseClusterId, + this.proofRef, + }); + + factory CommitmentEvent.fromJson(Map json) { + return CommitmentEvent( + id: json['id'], + commitmentId: json['commitmentId'] ?? "", + event: getEventType(json['event']), + at: DateTime.tryParse(json['at'] ?? ""), + excuseText: json['excuseText'] ?? "", + excuseClusterId: json['excuseClusterId'], + proofRef: json['proofRef'], + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['commitmentId'] = commitmentId; + data['event'] = event.name; + data['at'] = at?.toIso8601String(); + data['excuseText'] = excuseText; + data['excuseClusterId'] = excuseClusterId; + data['proofRef'] = proofRef; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/DebtEntry.dart b/frontend/lib/Grounded/about/external/data/DebtEntry.dart new file mode 100644 index 0000000..6663053 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/DebtEntry.dart @@ -0,0 +1,46 @@ +/// One line of the debt ledger. [decayedValue] is what the entry is worth +/// today, after recency decay has been applied. +class DebtEntry { + String? id; + + double delta; + + String reason; + + String? commitmentId; + + DateTime? at; + + double decayedValue; + + DebtEntry({ + this.id, + this.delta = 0, + this.reason = "", + this.commitmentId, + this.at, + this.decayedValue = 0, + }); + + factory DebtEntry.fromJson(Map json) { + return DebtEntry( + id: json['id'], + delta: (json['delta'] ?? 0).toDouble(), + reason: json['reason'] ?? "", + commitmentId: json['commitmentId'], + at: DateTime.tryParse(json['at'] ?? ""), + decayedValue: (json['decayedValue'] ?? 0).toDouble(), + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['delta'] = delta; + data['reason'] = reason; + data['commitmentId'] = commitmentId; + data['at'] = at?.toIso8601String(); + data['decayedValue'] = decayedValue; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/ExcuseCluster.dart b/frontend/lib/Grounded/about/external/data/ExcuseCluster.dart new file mode 100644 index 0000000..fea7b16 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/ExcuseCluster.dart @@ -0,0 +1,72 @@ +/// A recurring excuse plus the pattern the app confronts you with, e.g. +/// "Too tired has appeared 14 times this month, 11 of them on gym days, +/// 9 of them after 7pm. Consider moving gym to morning." +class ExcuseCluster { + String? id; + + String label; + + int occurrences; + + /// Weekday histogram (1 = Monday) — where this excuse concentrates. + Map byWeekday; + + /// Hour-of-day histogram — when it concentrates. + Map byHour; + + /// The category this excuse most often attaches to. + String dominantCategory; + + /// The confrontation copy rendered to the user. + String insight; + + ExcuseCluster({ + this.id, + this.label = "", + this.occurrences = 0, + Map? byWeekday, + Map? byHour, + this.dominantCategory = "", + this.insight = "", + }) : byWeekday = byWeekday ?? {}, + byHour = byHour ?? {}; + + factory ExcuseCluster.fromJson(Map json) { + final Map weekdays = {}; + if (json['byWeekday'] != null) { + (json['byWeekday'] as Map).forEach((key, value) { + weekdays[int.tryParse(key) ?? 1] = value ?? 0; + }); + } + + final Map hours = {}; + if (json['byHour'] != null) { + (json['byHour'] as Map).forEach((key, value) { + hours[int.tryParse(key) ?? 0] = value ?? 0; + }); + } + + return ExcuseCluster( + id: json['id'], + label: json['label'] ?? "", + occurrences: json['occurrences'] ?? 0, + byWeekday: weekdays, + byHour: hours, + dominantCategory: json['dominantCategory'] ?? "", + insight: json['insight'] ?? "", + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['label'] = label; + data['occurrences'] = occurrences; + data['byWeekday'] = + byWeekday.map((key, value) => MapEntry(key.toString(), value)); + data['byHour'] = byHour.map((key, value) => MapEntry(key.toString(), value)); + data['dominantCategory'] = dominantCategory; + data['insight'] = insight; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/ExercisePrescription.dart b/frontend/lib/Grounded/about/external/data/ExercisePrescription.dart new file mode 100644 index 0000000..f14e05e --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/ExercisePrescription.dart @@ -0,0 +1,91 @@ +import '../../internal/application/ProgressionRule.dart'; + +class ExercisePrescription { + String? id; + + String sessionTemplateId; + + String exerciseId; + + String exerciseName; + + /// Primary muscle group, for weekly volume tracking. + String muscleGroup; + + int sets; + + int targetReps; + + /// For timed holds and intervals; 0 when the work is rep-based. + int targetTimeSeconds; + + int restSeconds; + + /// Eccentric-pause-concentric-pause, e.g. "3010". + String tempo; + + ProgressionRule progressionRule; + + /// Ground contacts per set, for the plyometric weekly ceiling. + int contactsPerSet; + + /// Whether this is a hard exercise for integrity scoring. Derived from + /// historical RPE rather than set statically. + bool hard; + + ExercisePrescription({ + this.id, + this.sessionTemplateId = "", + this.exerciseId = "", + this.exerciseName = "", + this.muscleGroup = "", + this.sets = 0, + this.targetReps = 0, + this.targetTimeSeconds = 0, + this.restSeconds = 0, + this.tempo = "", + this.progressionRule = ProgressionRule.Reps, + this.contactsPerSet = 0, + this.hard = false, + }); + + factory ExercisePrescription.fromJson(Map json) { + return ExercisePrescription( + id: json['id'], + sessionTemplateId: json['sessionTemplateId'] ?? "", + exerciseId: json['exerciseId'] ?? "", + exerciseName: json['exerciseName'] ?? "", + muscleGroup: json['muscleGroup'] ?? "", + sets: json['sets'] ?? 0, + targetReps: json['targetReps'] ?? 0, + targetTimeSeconds: json['targetTimeSeconds'] ?? 0, + restSeconds: json['restSeconds'] ?? 0, + tempo: json['tempo'] ?? "", + progressionRule: getProgressionRule(json['progressionRule']), + contactsPerSet: json['contactsPerSet'] ?? 0, + hard: json['hard'] ?? false, + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['sessionTemplateId'] = sessionTemplateId; + data['exerciseId'] = exerciseId; + data['exerciseName'] = exerciseName; + data['muscleGroup'] = muscleGroup; + data['sets'] = sets; + data['targetReps'] = targetReps; + data['targetTimeSeconds'] = targetTimeSeconds; + data['restSeconds'] = restSeconds; + data['tempo'] = tempo; + data['progressionRule'] = progressionRule.name; + data['contactsPerSet'] = contactsPerSet; + data['hard'] = hard; + return data; + } + + int get plannedContacts { + return contactsPerSet * sets; + } +} diff --git a/frontend/lib/Grounded/about/external/data/Goal.dart b/frontend/lib/Grounded/about/external/data/Goal.dart new file mode 100644 index 0000000..ed3bd26 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/Goal.dart @@ -0,0 +1,110 @@ +import '../../internal/application/CommitmentClass.dart'; + +/// The container a set of commitments belongs to — "Workout", "Thesis", +/// "Get the flat sorted". A goal owns its tasks through +/// [Commitment.parentId]; it never carries debt itself, because debt belongs +/// to the specific thing you said you would do, not the ambition behind it. +class Goal { + String? id; + + String title; + + String description; + + String category; + + /// The default class inherited by tasks created inside this goal. + CommitmentClass defaultClass; + + DateTime? startDate; + + /// Optional deadline for the goal as a whole. + DateTime? targetDate; + + /// Colour accent, stored as a hex string so the goal reads consistently + /// wherever it appears. + String colourHex; + + bool archived; + + // ── Derived, supplied by the server ───────────────────────────────────── + + int totalTasks; + + int completedTasks; + + int overdueTasks; + + /// Debt accrued across every task under this goal. + double debtContribution; + + Goal({ + this.id, + this.title = "", + this.description = "", + this.category = "", + this.defaultClass = CommitmentClass.Standard, + this.startDate, + this.targetDate, + this.colourHex = "", + this.archived = false, + this.totalTasks = 0, + this.completedTasks = 0, + this.overdueTasks = 0, + this.debtContribution = 0, + }); + + factory Goal.fromJson(Map json) { + return Goal( + id: json['id'], + title: json['title'] ?? "", + description: json['description'] ?? "", + category: json['category'] ?? "", + defaultClass: getCommitmentClass(json['defaultClass']), + startDate: DateTime.tryParse(json['startDate'] ?? ""), + targetDate: DateTime.tryParse(json['targetDate'] ?? ""), + colourHex: json['colourHex'] ?? "", + archived: json['archived'] ?? false, + totalTasks: json['totalTasks'] ?? 0, + completedTasks: json['completedTasks'] ?? 0, + overdueTasks: json['overdueTasks'] ?? 0, + debtContribution: (json['debtContribution'] ?? 0).toDouble(), + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['title'] = title; + data['description'] = description; + data['category'] = category; + data['defaultClass'] = defaultClass.name; + data['startDate'] = startDate?.toIso8601String(); + data['targetDate'] = targetDate?.toIso8601String(); + data['colourHex'] = colourHex; + data['archived'] = archived; + return data; + } + + /// 0..1 across the goal's tasks. + double get progress { + if (totalTasks == 0) { + return 0; + } + return completedTasks / totalTasks; + } + + /// A goal is in trouble when a meaningful share of its tasks are past their + /// windows, not merely because one slipped. + bool get slipping { + if (totalTasks == 0) { + return false; + } + return overdueTasks / totalTasks >= 0.34; + } + + int get remainingTasks { + final int remaining = totalTasks - completedTasks; + return remaining > 0 ? remaining : 0; + } +} diff --git a/frontend/lib/Grounded/about/external/data/GroundedError.dart b/frontend/lib/Grounded/about/external/data/GroundedError.dart new file mode 100644 index 0000000..18e0cb4 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/GroundedError.dart @@ -0,0 +1,45 @@ +import 'Severity.dart'; + +class GroundedError { + double code; + String message; + String helper; + String title; + String severity; + + GroundedError( + {required this.code, + required this.message, + required this.helper, + required this.title, + required this.severity}); + + factory GroundedError.fromJson(Map json) { + return GroundedError( + code: json['code'], + message: json['message'], + helper: json['helper'], + title: json['title'], + severity: json['severity'], + ); + } + + Map toJson() { + final Map data = {}; + data['code'] = code; + data['message'] = message; + data['helper'] = helper; + data['title'] = title; + data['severity'] = severity; + return data; + } + + Severity getSeverityEnum(String severityString) { + for (Severity severity in Severity.values) { + if (severityString == severity.name) { + return severity; + } + } + return Severity.error; + } +} diff --git a/frontend/lib/Grounded/about/external/data/Habit.dart b/frontend/lib/Grounded/about/external/data/Habit.dart new file mode 100644 index 0000000..833f02c --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/Habit.dart @@ -0,0 +1,73 @@ +/// Frequency-based rather than instance-based: a single miss costs nothing, +/// falling below the target in the rolling window is what accrues debt. +class Habit { + String? id; + + String title; + + String category; + + /// Target completions per rolling window, e.g. 5. + int targetPerWindow; + + /// Rolling window length in days, e.g. 7. + int windowDays; + + /// Completions inside the current window. + int completionsInWindow; + + /// Marked keystone once the data says its completion predicts day quality — + /// the app works this out after ~60 days rather than taking your word. + bool keystone; + + /// 0..1 correlation with overall day quality; -1 until there is enough data. + double predictiveStrength; + + Habit({ + this.id, + this.title = "", + this.category = "", + this.targetPerWindow = 0, + this.windowDays = 7, + this.completionsInWindow = 0, + this.keystone = false, + this.predictiveStrength = -1, + }); + + factory Habit.fromJson(Map json) { + return Habit( + id: json['id'], + title: json['title'] ?? "", + category: json['category'] ?? "", + targetPerWindow: json['targetPerWindow'] ?? 0, + windowDays: json['windowDays'] ?? 7, + completionsInWindow: json['completionsInWindow'] ?? 0, + keystone: json['keystone'] ?? false, + predictiveStrength: (json['predictiveStrength'] ?? -1).toDouble(), + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['title'] = title; + data['category'] = category; + data['targetPerWindow'] = targetPerWindow; + data['windowDays'] = windowDays; + data['completionsInWindow'] = completionsInWindow; + data['keystone'] = keystone; + data['predictiveStrength'] = predictiveStrength; + return data; + } + + /// Behind target for the window — the only condition under which a habit + /// accrues debt. + bool get behindTarget { + return completionsInWindow < targetPerWindow; + } + + int get shortfall { + final int gap = targetPerWindow - completionsInWindow; + return gap > 0 ? gap : 0; + } +} diff --git a/frontend/lib/Grounded/about/external/data/LiveSession.dart b/frontend/lib/Grounded/about/external/data/LiveSession.dart new file mode 100644 index 0000000..a403fbf --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/LiveSession.dart @@ -0,0 +1,118 @@ +/// A task actively being run. This is what the full-screen runner and the +/// ongoing notification are both driven from. +class LiveSession { + String commitmentId; + + String title; + + String goalTitle; + + /// Wall-clock start of the run. + DateTime startedAt; + + /// Seconds of *foreground* work accumulated. Backgrounding the app stops + /// this accruing — that is the whole point of Timer proof. + int accumulatedSeconds; + + /// When the current active stretch began; null while paused. + DateTime? resumedAt; + + /// Seconds required before the run counts as proof. + int requiredSeconds; + + /// How many times the user left the app mid-run. Surfaced afterwards rather + /// than hidden, because leaving repeatedly is the behaviour worth seeing. + int backgroundedCount; + + LiveSession({ + this.commitmentId = "", + this.title = "", + this.goalTitle = "", + required this.startedAt, + this.accumulatedSeconds = 0, + this.resumedAt, + this.requiredSeconds = 0, + this.backgroundedCount = 0, + }); + + factory LiveSession.fromJson(Map json) { + return LiveSession( + commitmentId: json['commitmentId'] ?? "", + title: json['title'] ?? "", + goalTitle: json['goalTitle'] ?? "", + startedAt: DateTime.tryParse(json['startedAt'] ?? "") ?? DateTime.now(), + accumulatedSeconds: json['accumulatedSeconds'] ?? 0, + resumedAt: DateTime.tryParse(json['resumedAt'] ?? ""), + requiredSeconds: json['requiredSeconds'] ?? 0, + backgroundedCount: json['backgroundedCount'] ?? 0, + ); + } + + Map toJson() { + final Map data = {}; + data['commitmentId'] = commitmentId; + data['title'] = title; + data['goalTitle'] = goalTitle; + data['startedAt'] = startedAt.toIso8601String(); + data['accumulatedSeconds'] = accumulatedSeconds; + data['resumedAt'] = resumedAt?.toIso8601String(); + data['requiredSeconds'] = requiredSeconds; + data['backgroundedCount'] = backgroundedCount; + return data; + } + + bool get running { + return resumedAt != null; + } + + /// Foreground seconds as of now, including the stretch in progress. This is + /// derived from wall-clock rather than counted by the ticker, so the value + /// stays correct across a screen-off period where timers are throttled. + int elapsedSeconds({DateTime? now}) { + if (resumedAt == null) { + return accumulatedSeconds; + } + final DateTime moment = now ?? DateTime.now(); + return accumulatedSeconds + moment.difference(resumedAt!).inSeconds; + } + + int remainingSeconds({DateTime? now}) { + final int remaining = requiredSeconds - elapsedSeconds(now: now); + return remaining > 0 ? remaining : 0; + } + + /// 0..1 toward the requirement. + double progress({DateTime? now}) { + if (requiredSeconds <= 0) { + return 0; + } + final double value = elapsedSeconds(now: now) / requiredSeconds; + return value > 1 ? 1 : value; + } + + /// The requirement has been met — completion is now allowed. + bool satisfied({DateTime? now}) { + if (requiredSeconds <= 0) { + return true; + } + return elapsedSeconds(now: now) >= requiredSeconds; + } + + /// Pause and bank the stretch that just ended. + void pause({DateTime? now}) { + if (resumedAt == null) { + return; + } + final DateTime moment = now ?? DateTime.now(); + accumulatedSeconds = + accumulatedSeconds + moment.difference(resumedAt!).inSeconds; + resumedAt = null; + } + + void resume({DateTime? now}) { + if (resumedAt != null) { + return; + } + resumedAt = now ?? DateTime.now(); + } +} diff --git a/frontend/lib/Grounded/about/external/data/Program.dart b/frontend/lib/Grounded/about/external/data/Program.dart new file mode 100644 index 0000000..b1ff8b4 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/Program.dart @@ -0,0 +1,66 @@ +/// A training program. Deload weeks are scheduled and enforced — training +/// through a deload logs as non-compliance, same as skipping. +class Program { + String? id; + + String name; + + int weeks; + + int sessionsPerWeek; + + /// 1-based week indices that are deloads. + List deloadWeeks; + + /// Weekly ceiling on plyometric ground contacts. Plyo is the one modality + /// where the app stops you rather than pushes you. + int weeklyContactCeiling; + + /// Mandatory hours between high-intensity lower-body sessions. + int lowerBodyRecoveryHours; + + bool active; + + Program({ + this.id, + this.name = "", + this.weeks = 0, + this.sessionsPerWeek = 0, + List? deloadWeeks, + this.weeklyContactCeiling = 0, + this.lowerBodyRecoveryHours = 48, + this.active = false, + }) : deloadWeeks = deloadWeeks ?? []; + + factory Program.fromJson(Map json) { + return Program( + id: json['id'], + name: json['name'] ?? "", + weeks: json['weeks'] ?? 0, + sessionsPerWeek: json['sessionsPerWeek'] ?? 0, + deloadWeeks: json['deloadWeeks'] == null + ? [] + : (json['deloadWeeks'] as List).map((item) => item as int).toList(), + weeklyContactCeiling: json['weeklyContactCeiling'] ?? 0, + lowerBodyRecoveryHours: json['lowerBodyRecoveryHours'] ?? 48, + active: json['active'] ?? false, + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['name'] = name; + data['weeks'] = weeks; + data['sessionsPerWeek'] = sessionsPerWeek; + data['deloadWeeks'] = deloadWeeks; + data['weeklyContactCeiling'] = weeklyContactCeiling; + data['lowerBodyRecoveryHours'] = lowerBodyRecoveryHours; + data['active'] = active; + return data; + } + + bool isDeloadWeek(int week) { + return deloadWeeks.contains(week); + } +} diff --git a/frontend/lib/Grounded/about/external/data/ReportCard.dart b/frontend/lib/Grounded/about/external/data/ReportCard.dart new file mode 100644 index 0000000..ea52635 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/ReportCard.dart @@ -0,0 +1,192 @@ +import 'ExcuseCluster.dart'; + +/// The weekly parent-teacher conference. One assigned action for next week — +/// not five. +class ReportCard { + String? id; + + DateTime? periodStart; + + DateTime? periodEnd; + + /// Completion rate 0..1, keyed by commitment class name. + Map completionByClass; + + /// Completion rate 0..1, keyed by category. + Map completionByCategory; + + /// Completion rate 0..1, keyed by weekday (1 = Monday). + Map completionByWeekday; + + /// The recurring window where things go to die, as an hour of the day. + int worstHour; + + /// Debt score sampled per day across the period. + List debtTrend; + + /// The commitments dodged most, highest first. + List deferralLeaderboard; + + List excuseTaxonomy; + + /// Estimation multiplier per category, surfaced here rather than hidden. + Map estimationAccuracy; + + /// Training adherence 0..1 across the period. + double trainingAdherence; + + double programIntegrity; + + /// The single assigned action for next week. + String assignedAction; + + /// Cosmetic but effective. + String grade; + + /// Rationed, specific praise — empty when nothing was genuinely earned. + String praise; + + ReportCard({ + this.id, + this.periodStart, + this.periodEnd, + Map? completionByClass, + Map? completionByCategory, + Map? completionByWeekday, + this.worstHour = -1, + List? debtTrend, + List? deferralLeaderboard, + List? excuseTaxonomy, + Map? estimationAccuracy, + this.trainingAdherence = 0, + this.programIntegrity = 0, + this.assignedAction = "", + this.grade = "", + this.praise = "", + }) : completionByClass = completionByClass ?? {}, + completionByCategory = completionByCategory ?? {}, + completionByWeekday = completionByWeekday ?? {}, + debtTrend = debtTrend ?? [], + deferralLeaderboard = deferralLeaderboard ?? [], + excuseTaxonomy = excuseTaxonomy ?? [], + estimationAccuracy = estimationAccuracy ?? {}; + + factory ReportCard.fromJson(Map json) { + final Map byClass = {}; + if (json['completionByClass'] != null) { + (json['completionByClass'] as Map).forEach((key, value) { + byClass[key] = (value ?? 0).toDouble(); + }); + } + + final Map byCategory = {}; + if (json['completionByCategory'] != null) { + (json['completionByCategory'] as Map) + .forEach((key, value) { + byCategory[key] = (value ?? 0).toDouble(); + }); + } + + final Map byWeekday = {}; + if (json['completionByWeekday'] != null) { + (json['completionByWeekday'] as Map) + .forEach((key, value) { + byWeekday[int.tryParse(key) ?? 1] = (value ?? 0).toDouble(); + }); + } + + final Map estimation = {}; + if (json['estimationAccuracy'] != null) { + (json['estimationAccuracy'] as Map) + .forEach((key, value) { + estimation[key] = (value ?? 1).toDouble(); + }); + } + + return ReportCard( + id: json['id'], + periodStart: DateTime.tryParse(json['periodStart'] ?? ""), + periodEnd: DateTime.tryParse(json['periodEnd'] ?? ""), + completionByClass: byClass, + completionByCategory: byCategory, + completionByWeekday: byWeekday, + worstHour: json['worstHour'] ?? -1, + debtTrend: json['debtTrend'] == null + ? [] + : (json['debtTrend'] as List) + .map((item) => (item ?? 0).toDouble() as double) + .toList(), + deferralLeaderboard: json['deferralLeaderboard'] == null + ? [] + : (json['deferralLeaderboard'] as List) + .map((item) => DeferralCount.fromJson(item)) + .toList(), + excuseTaxonomy: json['excuseTaxonomy'] == null + ? [] + : (json['excuseTaxonomy'] as List) + .map((item) => ExcuseCluster.fromJson(item)) + .toList(), + estimationAccuracy: estimation, + trainingAdherence: (json['trainingAdherence'] ?? 0).toDouble(), + programIntegrity: (json['programIntegrity'] ?? 0).toDouble(), + assignedAction: json['assignedAction'] ?? "", + grade: json['grade'] ?? "", + praise: json['praise'] ?? "", + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['periodStart'] = periodStart?.toIso8601String(); + data['periodEnd'] = periodEnd?.toIso8601String(); + data['completionByClass'] = completionByClass; + data['completionByCategory'] = completionByCategory; + data['completionByWeekday'] = + completionByWeekday.map((key, value) => MapEntry(key.toString(), value)); + data['worstHour'] = worstHour; + data['debtTrend'] = debtTrend; + data['deferralLeaderboard'] = + deferralLeaderboard.map((item) => item.toJson()).toList(); + data['excuseTaxonomy'] = + excuseTaxonomy.map((item) => item.toJson()).toList(); + data['estimationAccuracy'] = estimationAccuracy; + data['trainingAdherence'] = trainingAdherence; + data['programIntegrity'] = programIntegrity; + data['assignedAction'] = assignedAction; + data['grade'] = grade; + data['praise'] = praise; + return data; + } +} + +/// One row of the deferral leaderboard — the tasks dodged most. +class DeferralCount { + String commitmentId; + + String title; + + int count; + + DeferralCount({ + this.commitmentId = "", + this.title = "", + this.count = 0, + }); + + factory DeferralCount.fromJson(Map json) { + return DeferralCount( + commitmentId: json['commitmentId'] ?? "", + title: json['title'] ?? "", + count: json['count'] ?? 0, + ); + } + + Map toJson() { + final Map data = {}; + data['commitmentId'] = commitmentId; + data['title'] = title; + data['count'] = count; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/ResponseState.dart b/frontend/lib/Grounded/about/external/data/ResponseState.dart new file mode 100644 index 0000000..e0e09fd --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/ResponseState.dart @@ -0,0 +1 @@ +enum ResponseState { Success, Failure, Pending } diff --git a/frontend/lib/Grounded/about/external/data/RoutineChain.dart b/frontend/lib/Grounded/about/external/data/RoutineChain.dart new file mode 100644 index 0000000..14cb5e4 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/RoutineChain.dart @@ -0,0 +1,97 @@ +/// An ordered sequence where the chain, not the step, is the unit of +/// completion. Breaking mid-way logs partial. +class RoutineChain { + String? id; + + String title; + + List steps; + + /// Index of the step reached when the chain last broke; -1 when clean. + int lastBreakIndex; + + RoutineChain({ + this.id, + this.title = "", + List? steps, + this.lastBreakIndex = -1, + }) : steps = steps ?? []; + + factory RoutineChain.fromJson(Map json) { + return RoutineChain( + id: json['id'], + title: json['title'] ?? "", + steps: json['steps'] == null + ? [] + : (json['steps'] as List) + .map((item) => RoutineStep.fromJson(item)) + .toList(), + lastBreakIndex: json['lastBreakIndex'] ?? -1, + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['title'] = title; + data['steps'] = steps.map((item) => item.toJson()).toList(); + data['lastBreakIndex'] = lastBreakIndex; + return data; + } + + int get completedSteps { + return steps.where((step) => step.completed).length; + } + + /// 0..1 — a partially run chain is recorded as partial, not as done. + double get partialCompletion { + if (steps.isEmpty) { + return 0; + } + return completedSteps / steps.length; + } + + bool get complete { + return steps.isNotEmpty && completedSteps == steps.length; + } +} + +class RoutineStep { + String? id; + + String title; + + int order; + + int timerSeconds; + + bool completed; + + RoutineStep({ + this.id, + this.title = "", + this.order = 0, + this.timerSeconds = 0, + this.completed = false, + }); + + factory RoutineStep.fromJson(Map json) { + return RoutineStep( + id: json['id'], + title: json['title'] ?? "", + order: json['order'] ?? 0, + timerSeconds: json['timerSeconds'] ?? 0, + completed: json['completed'] ?? false, + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['title'] = title; + data['order'] = order; + data['timerSeconds'] = timerSeconds; + data['completed'] = completed; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/SessionLog.dart b/frontend/lib/Grounded/about/external/data/SessionLog.dart new file mode 100644 index 0000000..a6b3bf8 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/SessionLog.dart @@ -0,0 +1,105 @@ +import 'SetLog.dart'; + +class SessionLog { + String? id; + + String templateId; + + String templateName; + + DateTime? startedAt; + + DateTime? endedAt; + + /// Session RPE 1..10. + double sessionRpe; + + /// Readiness check-in at session start. + int sleepScore; + + int sorenessScore; + + int motivationScore; + + /// Did you do the session, or a watered-down version of it? + double integrityScore; + + bool duringDeload; + + List sets; + + SessionLog({ + this.id, + this.templateId = "", + this.templateName = "", + this.startedAt, + this.endedAt, + this.sessionRpe = 0, + this.sleepScore = 0, + this.sorenessScore = 0, + this.motivationScore = 0, + this.integrityScore = 0, + this.duringDeload = false, + List? sets, + }) : sets = sets ?? []; + + factory SessionLog.fromJson(Map json) { + return SessionLog( + id: json['id'], + templateId: json['templateId'] ?? "", + templateName: json['templateName'] ?? "", + startedAt: DateTime.tryParse(json['startedAt'] ?? ""), + endedAt: DateTime.tryParse(json['endedAt'] ?? ""), + sessionRpe: (json['sessionRpe'] ?? 0).toDouble(), + sleepScore: json['sleepScore'] ?? 0, + sorenessScore: json['sorenessScore'] ?? 0, + motivationScore: json['motivationScore'] ?? 0, + integrityScore: (json['integrityScore'] ?? 0).toDouble(), + duringDeload: json['duringDeload'] ?? false, + sets: json['sets'] == null + ? [] + : (json['sets'] as List).map((item) => SetLog.fromJson(item)).toList(), + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['templateId'] = templateId; + data['templateName'] = templateName; + data['startedAt'] = startedAt?.toIso8601String(); + data['endedAt'] = endedAt?.toIso8601String(); + data['sessionRpe'] = sessionRpe; + data['sleepScore'] = sleepScore; + data['sorenessScore'] = sorenessScore; + data['motivationScore'] = motivationScore; + data['integrityScore'] = integrityScore; + data['duringDeload'] = duringDeload; + data['sets'] = sets.map((item) => item.toJson()).toList(); + return data; + } + + int get durationMinutes { + if (startedAt == null || endedAt == null) { + return 0; + } + return endedAt!.difference(startedAt!).inMinutes; + } + + /// Weekly tonnage contribution for loaded work. + double get tonnage { + double total = 0; + for (SetLog entry in sets) { + total = total + (entry.loadKg * entry.reps); + } + return total; + } + + int get contacts { + int total = 0; + for (SetLog entry in sets) { + total = total + entry.contacts; + } + return total; + } +} diff --git a/frontend/lib/Grounded/about/external/data/SessionTemplate.dart b/frontend/lib/Grounded/about/external/data/SessionTemplate.dart new file mode 100644 index 0000000..28dcadb --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/SessionTemplate.dart @@ -0,0 +1,53 @@ +import 'ExercisePrescription.dart'; + +class SessionTemplate { + String? id; + + String programId; + + /// 0-based day within the training week. + int dayIndex; + + String name; + + /// True when this session loads the lower body hard enough to require the + /// program recovery gap before the next one. + bool highIntensityLowerBody; + + List prescriptions; + + SessionTemplate({ + this.id, + this.programId = "", + this.dayIndex = 0, + this.name = "", + this.highIntensityLowerBody = false, + List? prescriptions, + }) : prescriptions = prescriptions ?? []; + + factory SessionTemplate.fromJson(Map json) { + return SessionTemplate( + id: json['id'], + programId: json['programId'] ?? "", + dayIndex: json['dayIndex'] ?? 0, + name: json['name'] ?? "", + highIntensityLowerBody: json['highIntensityLowerBody'] ?? false, + prescriptions: json['prescriptions'] == null + ? [] + : (json['prescriptions'] as List) + .map((item) => ExercisePrescription.fromJson(item)) + .toList(), + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['programId'] = programId; + data['dayIndex'] = dayIndex; + data['name'] = name; + data['highIntensityLowerBody'] = highIntensityLowerBody; + data['prescriptions'] = prescriptions.map((item) => item.toJson()).toList(); + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/SetLog.dart b/frontend/lib/Grounded/about/external/data/SetLog.dart new file mode 100644 index 0000000..7f4a8c8 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/SetLog.dart @@ -0,0 +1,76 @@ +class SetLog { + String? id; + + String sessionLogId; + + String exerciseId; + + String exerciseName; + + String muscleGroup; + + int setNo; + + int reps; + + double loadKg; + + int timeSeconds; + + double rpe; + + /// False when the set was improvised rather than prescribed — this is what + /// separates doing the session from doing something adjacent to it. + bool isPrescribed; + + int contacts; + + SetLog({ + this.id, + this.sessionLogId = "", + this.exerciseId = "", + this.exerciseName = "", + this.muscleGroup = "", + this.setNo = 0, + this.reps = 0, + this.loadKg = 0, + this.timeSeconds = 0, + this.rpe = 0, + this.isPrescribed = true, + this.contacts = 0, + }); + + factory SetLog.fromJson(Map json) { + return SetLog( + id: json['id'], + sessionLogId: json['sessionLogId'] ?? "", + exerciseId: json['exerciseId'] ?? "", + exerciseName: json['exerciseName'] ?? "", + muscleGroup: json['muscleGroup'] ?? "", + setNo: json['setNo'] ?? 0, + reps: json['reps'] ?? 0, + loadKg: (json['loadKg'] ?? 0).toDouble(), + timeSeconds: json['timeSeconds'] ?? 0, + rpe: (json['rpe'] ?? 0).toDouble(), + isPrescribed: json['isPrescribed'] ?? true, + contacts: json['contacts'] ?? 0, + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['sessionLogId'] = sessionLogId; + data['exerciseId'] = exerciseId; + data['exerciseName'] = exerciseName; + data['muscleGroup'] = muscleGroup; + data['setNo'] = setNo; + data['reps'] = reps; + data['loadKg'] = loadKg; + data['timeSeconds'] = timeSeconds; + data['rpe'] = rpe; + data['isPrescribed'] = isPrescribed; + data['contacts'] = contacts; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/Severity.dart b/frontend/lib/Grounded/about/external/data/Severity.dart new file mode 100644 index 0000000..07d0890 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/Severity.dart @@ -0,0 +1 @@ +enum Severity { warning, alert, message, error } diff --git a/frontend/lib/Grounded/about/external/data/StandingChange.dart b/frontend/lib/Grounded/about/external/data/StandingChange.dart new file mode 100644 index 0000000..81f7100 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/StandingChange.dart @@ -0,0 +1,41 @@ +import '../../internal/application/Standing.dart'; + +class StandingChange { + String? id; + + Standing from; + + Standing to; + + DateTime? at; + + String trigger; + + StandingChange({ + this.id, + this.from = Standing.Good, + this.to = Standing.Good, + this.at, + this.trigger = "", + }); + + factory StandingChange.fromJson(Map json) { + return StandingChange( + id: json['id'], + from: getStanding(json['from']), + to: getStanding(json['to']), + at: DateTime.tryParse(json['at'] ?? ""), + trigger: json['trigger'] ?? "", + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['from'] = from.name; + data['to'] = to.name; + data['at'] = at?.toIso8601String(); + data['trigger'] = trigger; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/SystemResponse.dart b/frontend/lib/Grounded/about/external/data/SystemResponse.dart new file mode 100644 index 0000000..dabd57e --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/SystemResponse.dart @@ -0,0 +1,40 @@ +import 'ResponseState.dart'; + +class SystemResponse { + String key; + + String value; + + String description; + + ResponseState state; + + SystemResponse(this.key, this.value, this.description, this.state); + + factory SystemResponse.fromJsonMap(Map json) { + return SystemResponse( + json['key'] ?? "", + json['value'] ?? "", + json['description'] ?? "", + _state(json['state']), + ); + } + + static ResponseState _state(String? name) { + for (ResponseState value in ResponseState.values) { + if (value.name == name) { + return value; + } + } + return ResponseState.Success; + } + + Map toJson() { + final Map data = {}; + data['key'] = key; + data['value'] = value; + data['description'] = description; + data['state'] = state.name; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/pages/request/CommitmentsRequest.dart b/frontend/lib/Grounded/about/external/data/pages/request/CommitmentsRequest.dart new file mode 100644 index 0000000..2e5b1bb --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/pages/request/CommitmentsRequest.dart @@ -0,0 +1,21 @@ +import 'PageAndSort.dart'; + +class CommitmentsRequest { + PageAndSort? query; + + /// ISO day the plan is being requested for; empty means today. + String day; + + /// Filter by status name; empty means all. + String status; + + CommitmentsRequest({this.query, this.day = "", this.status = ""}); + + Map toJson() { + final Map data = {}; + data['query'] = query?.toJson(); + data['day'] = day; + data['status'] = status; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/pages/request/HistoryRequest.dart b/frontend/lib/Grounded/about/external/data/pages/request/HistoryRequest.dart new file mode 100644 index 0000000..3269daa --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/pages/request/HistoryRequest.dart @@ -0,0 +1,14 @@ +import 'PageAndSort.dart'; + +/// Every list endpoint takes this shape. Never inline flat sort/page fields. +class HistoryRequest { + PageAndSort? query; + + HistoryRequest({this.query}); + + Map toJson() { + final Map data = {}; + data['query'] = query?.toJson(); + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/pages/request/PageAndSort.dart b/frontend/lib/Grounded/about/external/data/pages/request/PageAndSort.dart new file mode 100644 index 0000000..b718485 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/pages/request/PageAndSort.dart @@ -0,0 +1,17 @@ +import 'Pageable.dart'; +import 'Sort.dart'; + +class PageAndSort { + Sort? sort; + + Pageable? page; + + PageAndSort({this.sort, this.page}); + + Map toJson() { + final Map data = {}; + data['sort'] = sort?.toJson(); + data['page'] = page?.toJson(); + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/pages/request/Pageable.dart b/frontend/lib/Grounded/about/external/data/pages/request/Pageable.dart new file mode 100644 index 0000000..24e5976 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/pages/request/Pageable.dart @@ -0,0 +1,20 @@ +class Pageable { + int offset; + + int pageNumber; + + int pageSize; + + int paged; + + Pageable(this.offset, this.pageNumber, this.pageSize, this.paged); + + Map toJson() { + final Map data = {}; + data['offset'] = offset; + data['pageNumber'] = pageNumber; + data['pageSize'] = pageSize; + data['paged'] = paged; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/pages/request/Sort.dart b/frontend/lib/Grounded/about/external/data/pages/request/Sort.dart new file mode 100644 index 0000000..9af5b4d --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/pages/request/Sort.dart @@ -0,0 +1,14 @@ +class Sort { + String direction; + + String field; + + Sort(this.direction, this.field); + + Map toJson() { + final Map data = {}; + data['direction'] = direction; + data['field'] = field; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/data/pages/response/CommitmentEventPage.dart b/frontend/lib/Grounded/about/external/data/pages/response/CommitmentEventPage.dart new file mode 100644 index 0000000..e4fa937 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/pages/response/CommitmentEventPage.dart @@ -0,0 +1,47 @@ +import '../../CommitmentEvent.dart'; + +class CommitmentEventPage { + int number; + + int size; + + int totalElements; + + int totalPages; + + int numberOfElements; + + bool first; + + bool last; + + List content; + + CommitmentEventPage({ + this.number = 0, + this.size = 0, + this.totalElements = 0, + this.totalPages = 0, + this.numberOfElements = 0, + this.first = true, + this.last = true, + List? content, + }) : content = content ?? []; + + factory CommitmentEventPage.fromJson(Map json) { + return CommitmentEventPage( + number: json['number'] ?? 0, + size: json['size'] ?? 0, + totalElements: json['totalElements'] ?? 0, + totalPages: json['totalPages'] ?? 0, + numberOfElements: json['numberOfElements'] ?? 0, + first: json['first'] ?? true, + last: json['last'] ?? true, + content: json['content'] == null + ? [] + : (json['content'] as List) + .map((item) => CommitmentEvent.fromJson(item)) + .toList(), + ); + } +} diff --git a/frontend/lib/Grounded/about/external/data/pages/response/CommitmentPage.dart b/frontend/lib/Grounded/about/external/data/pages/response/CommitmentPage.dart new file mode 100644 index 0000000..b30af0d --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/pages/response/CommitmentPage.dart @@ -0,0 +1,47 @@ +import '../../Commitment.dart'; + +class CommitmentPage { + int number; + + int size; + + int totalElements; + + int totalPages; + + int numberOfElements; + + bool first; + + bool last; + + List content; + + CommitmentPage({ + this.number = 0, + this.size = 0, + this.totalElements = 0, + this.totalPages = 0, + this.numberOfElements = 0, + this.first = true, + this.last = true, + List? content, + }) : content = content ?? []; + + factory CommitmentPage.fromJson(Map json) { + return CommitmentPage( + number: json['number'] ?? 0, + size: json['size'] ?? 0, + totalElements: json['totalElements'] ?? 0, + totalPages: json['totalPages'] ?? 0, + numberOfElements: json['numberOfElements'] ?? 0, + first: json['first'] ?? true, + last: json['last'] ?? true, + content: json['content'] == null + ? [] + : (json['content'] as List) + .map((item) => Commitment.fromJson(item)) + .toList(), + ); + } +} diff --git a/frontend/lib/Grounded/about/external/data/pages/response/SessionLogPage.dart b/frontend/lib/Grounded/about/external/data/pages/response/SessionLogPage.dart new file mode 100644 index 0000000..757dd22 --- /dev/null +++ b/frontend/lib/Grounded/about/external/data/pages/response/SessionLogPage.dart @@ -0,0 +1,47 @@ +import '../../SessionLog.dart'; + +class SessionLogPage { + int number; + + int size; + + int totalElements; + + int totalPages; + + int numberOfElements; + + bool first; + + bool last; + + List content; + + SessionLogPage({ + this.number = 0, + this.size = 0, + this.totalElements = 0, + this.totalPages = 0, + this.numberOfElements = 0, + this.first = true, + this.last = true, + List? content, + }) : content = content ?? []; + + factory SessionLogPage.fromJson(Map json) { + return SessionLogPage( + number: json['number'] ?? 0, + size: json['size'] ?? 0, + totalElements: json['totalElements'] ?? 0, + totalPages: json['totalPages'] ?? 0, + numberOfElements: json['numberOfElements'] ?? 0, + first: json['first'] ?? true, + last: json['last'] ?? true, + content: json['content'] == null + ? [] + : (json['content'] as List) + .map((item) => SessionLog.fromJson(item)) + .toList(), + ); + } +} diff --git a/frontend/lib/Grounded/about/external/initial/AbandonRequest.dart b/frontend/lib/Grounded/about/external/initial/AbandonRequest.dart new file mode 100644 index 0000000..01aad94 --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/AbandonRequest.dart @@ -0,0 +1,16 @@ +/// Abandoning costs the most debt of all, so it is always explicit and always +/// carries a reason. +class AbandonRequest { + String commitmentId; + + String reason; + + AbandonRequest({this.commitmentId = "", this.reason = ""}); + + Map toJson() { + final Map data = {}; + data['commitmentId'] = commitmentId; + data['reason'] = reason; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/AmnestyRequest.dart b/frontend/lib/Grounded/about/external/initial/AmnestyRequest.dart new file mode 100644 index 0000000..7e51f36 --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/AmnestyRequest.dart @@ -0,0 +1,13 @@ +/// Spend one of the rationed monthly tokens to wipe an item debt, no questions +/// asked. +class AmnestyRequest { + String commitmentId; + + AmnestyRequest({this.commitmentId = ""}); + + Map toJson() { + final Map data = {}; + data['commitmentId'] = commitmentId; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/CommitmentRequest.dart b/frontend/lib/Grounded/about/external/initial/CommitmentRequest.dart new file mode 100644 index 0000000..d112505 --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/CommitmentRequest.dart @@ -0,0 +1,58 @@ +/// Create or update a commitment. +class CommitmentRequest { + String? id; + + String type; + + String commitmentClass; + + String title; + + String category; + + String dueStart; + + String dueEnd; + + int estMinutes; + + String energy; + + String proofType; + + int proofTimerMinutes; + + String rrule; + + CommitmentRequest({ + this.id, + this.type = "TASK", + this.commitmentClass = "Standard", + this.title = "", + this.category = "", + this.dueStart = "", + this.dueEnd = "", + this.estMinutes = 0, + this.energy = "Medium", + this.proofType = "Honour", + this.proofTimerMinutes = 0, + this.rrule = "", + }); + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['type'] = type; + data['commitmentClass'] = commitmentClass; + data['title'] = title; + data['category'] = category; + data['dueStart'] = dueStart; + data['dueEnd'] = dueEnd; + data['estMinutes'] = estMinutes; + data['energy'] = energy; + data['proofType'] = proofType; + data['proofTimerMinutes'] = proofTimerMinutes; + data['rrule'] = rrule; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/CompletionRequest.dart b/frontend/lib/Grounded/about/external/initial/CompletionRequest.dart new file mode 100644 index 0000000..3ab630f --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/CompletionRequest.dart @@ -0,0 +1,38 @@ +/// Completion always carries its proof. The server decides Complete vs Late +/// Complete from the window, never the client. +class CompletionRequest { + String commitmentId; + + String proofType; + + /// Reference to the uploaded artefact — photo id, timer session id, geofence + /// dwell id or witness confirmation id. + String proofRef; + + /// Foreground seconds actually run, for Timer proof. + int timerSeconds; + + double latitude; + + double longitude; + + CompletionRequest({ + this.commitmentId = "", + this.proofType = "Honour", + this.proofRef = "", + this.timerSeconds = 0, + this.latitude = 0, + this.longitude = 0, + }); + + Map toJson() { + final Map data = {}; + data['commitmentId'] = commitmentId; + data['proofType'] = proofType; + data['proofRef'] = proofRef; + data['timerSeconds'] = timerSeconds; + data['latitude'] = latitude; + data['longitude'] = longitude; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/DeferralRequest.dart b/frontend/lib/Grounded/about/external/initial/DeferralRequest.dart new file mode 100644 index 0000000..e354d14 --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/DeferralRequest.dart @@ -0,0 +1,27 @@ +/// Deferral always carries an excuse. Free text, minimum length enforced, no +/// template buttons — the friction is the point. +class DeferralRequest { + String commitmentId; + + String excuseText; + + String newDueStart; + + String newDueEnd; + + DeferralRequest({ + this.commitmentId = "", + this.excuseText = "", + this.newDueStart = "", + this.newDueEnd = "", + }); + + Map toJson() { + final Map data = {}; + data['commitmentId'] = commitmentId; + data['excuseText'] = excuseText; + data['newDueStart'] = newDueStart; + data['newDueEnd'] = newDueEnd; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/DeviceRequest.dart b/frontend/lib/Grounded/about/external/initial/DeviceRequest.dart new file mode 100644 index 0000000..2e68a7e --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/DeviceRequest.dart @@ -0,0 +1,25 @@ +class DeviceRequest { + String identifier; + + String model; + + String platform; + + String version; + + DeviceRequest({ + this.identifier = "", + this.model = "", + this.platform = "", + this.version = "", + }); + + Map toJson() { + final Map data = {}; + data['identifier'] = identifier; + data['model'] = model; + data['platform'] = platform; + data['version'] = version; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/GoalRequest.dart b/frontend/lib/Grounded/about/external/initial/GoalRequest.dart new file mode 100644 index 0000000..686029c --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/GoalRequest.dart @@ -0,0 +1,41 @@ +class GoalRequest { + String? id; + + String title; + + String description; + + String category; + + String defaultClass; + + String startDate; + + String targetDate; + + String colourHex; + + GoalRequest({ + this.id, + this.title = "", + this.description = "", + this.category = "", + this.defaultClass = "Standard", + this.startDate = "", + this.targetDate = "", + this.colourHex = "", + }); + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['title'] = title; + data['description'] = description; + data['category'] = category; + data['defaultClass'] = defaultClass; + data['startDate'] = startDate; + data['targetDate'] = targetDate; + data['colourHex'] = colourHex; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/IdRequest.dart b/frontend/lib/Grounded/about/external/initial/IdRequest.dart new file mode 100644 index 0000000..66d9beb --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/IdRequest.dart @@ -0,0 +1,11 @@ +class IdRequest { + String id; + + IdRequest({this.id = ""}); + + Map toJson() { + final Map data = {}; + data['id'] = id; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/LoginData.dart b/frontend/lib/Grounded/about/external/initial/LoginData.dart new file mode 100644 index 0000000..3014d3d --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/LoginData.dart @@ -0,0 +1,14 @@ +class LoginData { + String username; + + String password; + + LoginData({this.username = "", this.password = ""}); + + Map toJson() { + final Map data = {}; + data['username'] = username; + data['password'] = password; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/ReportCardRequest.dart b/frontend/lib/Grounded/about/external/initial/ReportCardRequest.dart new file mode 100644 index 0000000..c793b14 --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/ReportCardRequest.dart @@ -0,0 +1,14 @@ +class ReportCardRequest { + String periodStart; + + String periodEnd; + + ReportCardRequest({this.periodStart = "", this.periodEnd = ""}); + + Map toJson() { + final Map data = {}; + data['periodStart'] = periodStart; + data['periodEnd'] = periodEnd; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/SessionLogRequest.dart b/frontend/lib/Grounded/about/external/initial/SessionLogRequest.dart new file mode 100644 index 0000000..00c19d0 --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/SessionLogRequest.dart @@ -0,0 +1,45 @@ +class SessionLogRequest { + String? id; + + String templateId; + + String startedAt; + + String endedAt; + + double sessionRpe; + + int sleepScore; + + int sorenessScore; + + int motivationScore; + + List> sets; + + SessionLogRequest({ + this.id, + this.templateId = "", + this.startedAt = "", + this.endedAt = "", + this.sessionRpe = 0, + this.sleepScore = 0, + this.sorenessScore = 0, + this.motivationScore = 0, + List>? sets, + }) : sets = sets ?? >[]; + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['templateId'] = templateId; + data['startedAt'] = startedAt; + data['endedAt'] = endedAt; + data['sessionRpe'] = sessionRpe; + data['sleepScore'] = sleepScore; + data['sorenessScore'] = sorenessScore; + data['motivationScore'] = motivationScore; + data['sets'] = sets; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/SickModeRequest.dart b/frontend/lib/Grounded/about/external/initial/SickModeRequest.dart new file mode 100644 index 0000000..ad54e8c --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/SickModeRequest.dart @@ -0,0 +1,18 @@ +/// Pauses debt accrual entirely. Requires a reason and is logged in history. +class SickModeRequest { + bool enabled; + + String reason; + + String until; + + SickModeRequest({this.enabled = false, this.reason = "", this.until = ""}); + + Map toJson() { + final Map data = {}; + data['enabled'] = enabled; + data['reason'] = reason; + data['until'] = until; + return data; + } +} diff --git a/frontend/lib/Grounded/about/external/initial/ToneRequest.dart b/frontend/lib/Grounded/about/external/initial/ToneRequest.dart new file mode 100644 index 0000000..edb6c95 --- /dev/null +++ b/frontend/lib/Grounded/about/external/initial/ToneRequest.dart @@ -0,0 +1,11 @@ +class ToneRequest { + String tone; + + ToneRequest({this.tone = "Strict"}); + + Map toJson() { + final Map data = {}; + data['tone'] = tone; + return data; + } +} diff --git a/frontend/lib/Grounded/about/internal/application/CapacityProfile.dart b/frontend/lib/Grounded/about/internal/application/CapacityProfile.dart new file mode 100644 index 0000000..732b59a --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/CapacityProfile.dart @@ -0,0 +1,57 @@ +/// What the user can realistically absorb, learned from history rather than +/// asserted. Drives the capacity block at plan time. +class CapacityProfile { + /// p50 of historically completed minutes, keyed by weekday (1 = Monday). + Map completedMinutesByWeekday; + + /// Per-category estimation multiplier — you say 30min, you take 70min -> 2.3. + Map estimationMultipliers; + + CapacityProfile({ + Map? completedMinutesByWeekday, + Map? estimationMultipliers, + }) : completedMinutesByWeekday = completedMinutesByWeekday ?? {}, + estimationMultipliers = estimationMultipliers ?? {}; + + factory CapacityProfile.fromJson(Map json) { + final Map minutes = {}; + if (json['completedMinutesByWeekday'] != null) { + (json['completedMinutesByWeekday'] as Map) + .forEach((key, value) { + minutes[int.tryParse(key) ?? 1] = (value ?? 0).toDouble(); + }); + } + + final Map multipliers = {}; + if (json['estimationMultipliers'] != null) { + (json['estimationMultipliers'] as Map) + .forEach((key, value) { + multipliers[key] = (value ?? 1).toDouble(); + }); + } + + return CapacityProfile( + completedMinutesByWeekday: minutes, + estimationMultipliers: multipliers, + ); + } + + Map toJson() { + final Map data = {}; + data['completedMinutesByWeekday'] = completedMinutesByWeekday + .map((key, value) => MapEntry(key.toString(), value)); + data['estimationMultipliers'] = estimationMultipliers; + return data; + } + + /// The learned multiplier for a category, defaulting to honest 1.0 until + /// there is enough history to say otherwise. + double multiplierFor(String category) { + return estimationMultipliers[category] ?? 1.0; + } + + /// The p50 of what actually gets done on this weekday. + double capacityFor(int weekday) { + return completedMinutesByWeekday[weekday] ?? 0; + } +} diff --git a/frontend/lib/Grounded/about/internal/application/CommitmentClass.dart b/frontend/lib/Grounded/about/internal/application/CommitmentClass.dart new file mode 100644 index 0000000..d62b3cf --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/CommitmentClass.dart @@ -0,0 +1,44 @@ +/// The disciplinary weight class of a commitment. Central, not cosmetic — the +/// class decides debt weight, deferability and escalation harshness. +enum CommitmentClass { + /// Never auto-deferrable, heaviest debt weight, hardest escalation. + NonNegotiable, + + /// Normal weight, limited deferrals. + Standard, + + /// Nice-to-have, no debt on miss, auto-archives. + Elective, +} + +/// Debt weight w(class) from the debt formula. +double classWeight(CommitmentClass value) { + switch (value) { + case CommitmentClass.NonNegotiable: + return 5.0; + case CommitmentClass.Standard: + return 2.0; + case CommitmentClass.Elective: + return 0.0; + } +} + +String classLabel(CommitmentClass value) { + switch (value) { + case CommitmentClass.NonNegotiable: + return "Non-negotiable"; + case CommitmentClass.Standard: + return "Standard"; + case CommitmentClass.Elective: + return "Elective"; + } +} + +CommitmentClass getCommitmentClass(String? name) { + for (CommitmentClass value in CommitmentClass.values) { + if (value.name == name) { + return value; + } + } + return CommitmentClass.Standard; +} diff --git a/frontend/lib/Grounded/about/internal/application/CommitmentStatus.dart b/frontend/lib/Grounded/about/internal/application/CommitmentStatus.dart new file mode 100644 index 0000000..d1aa216 --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/CommitmentStatus.dart @@ -0,0 +1,18 @@ +enum CommitmentStatus { + Open, + Completed, + LateCompleted, + Overdue, + Deferred, + Abandoned, + Archived, +} + +CommitmentStatus getCommitmentStatus(String? name) { + for (CommitmentStatus value in CommitmentStatus.values) { + if (value.name == name) { + return value; + } + } + return CommitmentStatus.Open; +} diff --git a/frontend/lib/Grounded/about/internal/application/CommitmentType.dart b/frontend/lib/Grounded/about/internal/application/CommitmentType.dart new file mode 100644 index 0000000..5531f8d --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/CommitmentType.dart @@ -0,0 +1,10 @@ +enum CommitmentType { TASK, SESSION, HABIT } + +CommitmentType getCommitmentType(String? name) { + for (CommitmentType value in CommitmentType.values) { + if (value.name == name) { + return value; + } + } + return CommitmentType.TASK; +} diff --git a/frontend/lib/Grounded/about/internal/application/DialogData.dart b/frontend/lib/Grounded/about/internal/application/DialogData.dart new file mode 100644 index 0000000..84cf0ec --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/DialogData.dart @@ -0,0 +1,7 @@ +class DialogData { + String title; + + String description; + + DialogData(this.title, this.description); +} diff --git a/frontend/lib/Grounded/about/internal/application/EnergyCost.dart b/frontend/lib/Grounded/about/internal/application/EnergyCost.dart new file mode 100644 index 0000000..095f087 --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/EnergyCost.dart @@ -0,0 +1,10 @@ +enum EnergyCost { Low, Medium, High } + +EnergyCost getEnergyCost(String? name) { + for (EnergyCost value in EnergyCost.values) { + if (value.name == name) { + return value; + } + } + return EnergyCost.Medium; +} diff --git a/frontend/lib/Grounded/about/internal/application/EscalationTier.dart b/frontend/lib/Grounded/about/internal/application/EscalationTier.dart new file mode 100644 index 0000000..00e452f --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/EscalationTier.dart @@ -0,0 +1,11 @@ +/// Notification escalation ladder. Disappointment outperforms anger. +enum EscalationTier { Reminder, Nudge, Nag, Disappointed, Cold } + +EscalationTier getEscalationTier(String? name) { + for (EscalationTier value in EscalationTier.values) { + if (value.name == name) { + return value; + } + } + return EscalationTier.Reminder; +} diff --git a/frontend/lib/Grounded/about/internal/application/EventType.dart b/frontend/lib/Grounded/about/internal/application/EventType.dart new file mode 100644 index 0000000..bf78648 --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/EventType.dart @@ -0,0 +1,20 @@ +/// Append-only commitment event log. The status field is never the source of +/// truth — this log is. +enum EventType { + CREATED, + COMPLETED, + LATE, + DEFERRED, + MISSED, + ABANDONED, + AMNESTY, +} + +EventType getEventType(String? name) { + for (EventType value in EventType.values) { + if (value.name == name) { + return value; + } + } + return EventType.CREATED; +} diff --git a/frontend/lib/Grounded/about/internal/application/MeDescription.dart b/frontend/lib/Grounded/about/internal/application/MeDescription.dart new file mode 100644 index 0000000..38c8ebd --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/MeDescription.dart @@ -0,0 +1,27 @@ +/// The device + session identity carried on every request. `name` and `id` are +/// stored pre-encrypted and become the `what` / `whom` headers. +class MeDescription { + String id; + + String name; + + String token; + + MeDescription({required this.id, required this.name, required this.token}); + + factory MeDescription.fromJson(Map json) { + return MeDescription( + id: json['id'] ?? "", + name: json['name'] ?? "", + token: json['token'] ?? "", + ); + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['name'] = name; + data['token'] = token; + return data; + } +} diff --git a/frontend/lib/Grounded/about/internal/application/NavigatorType.dart b/frontend/lib/Grounded/about/internal/application/NavigatorType.dart new file mode 100644 index 0000000..97556ab --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/NavigatorType.dart @@ -0,0 +1 @@ +enum NavigatorType { justOpen, openFully, replaceCurrent, makeNewMain } diff --git a/frontend/lib/Grounded/about/internal/application/NotificationType.dart b/frontend/lib/Grounded/about/internal/application/NotificationType.dart new file mode 100644 index 0000000..a7daf1a --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/NotificationType.dart @@ -0,0 +1 @@ +enum NotificationType { info, success, error, warning } diff --git a/frontend/lib/Grounded/about/internal/application/Pair.dart b/frontend/lib/Grounded/about/internal/application/Pair.dart new file mode 100644 index 0000000..71d6d40 --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/Pair.dart @@ -0,0 +1,7 @@ +class Pair { + String key; + + dynamic value; + + Pair(this.key, this.value); +} diff --git a/frontend/lib/Grounded/about/internal/application/ProgressionRule.dart b/frontend/lib/Grounded/about/internal/application/ProgressionRule.dart new file mode 100644 index 0000000..f755874 --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/ProgressionRule.dart @@ -0,0 +1,42 @@ +/// Progression rules that actually apply to bodyweight training, where adding +/// plates is not the lever. +enum ProgressionRule { + /// 3x8 -> 3x12, then a harder variation. + Reps, + + /// Incline push-up -> flat -> decline -> archer -> one-arm. + Leverage, + + /// Slower tempo, longer holds, same reps. + TimeUnderTension, + + /// Same work, less rest. + Density, + + /// Added external load. + Load, +} + +String progressionLabel(ProgressionRule value) { + switch (value) { + case ProgressionRule.Reps: + return "Rep progression"; + case ProgressionRule.Leverage: + return "Leverage progression"; + case ProgressionRule.TimeUnderTension: + return "Time under tension"; + case ProgressionRule.Density: + return "Density"; + case ProgressionRule.Load: + return "Load"; + } +} + +ProgressionRule getProgressionRule(String? name) { + for (ProgressionRule value in ProgressionRule.values) { + if (value.name == name) { + return value; + } + } + return ProgressionRule.Reps; +} diff --git a/frontend/lib/Grounded/about/internal/application/ProofType.dart b/frontend/lib/Grounded/about/internal/application/ProofType.dart new file mode 100644 index 0000000..a4f943a --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/ProofType.dart @@ -0,0 +1,46 @@ +/// The anti-cheat layer. Chosen per commitment at creation time. +enum ProofType { + /// Plain checkbox — for trivia only. + Honour, + + /// Camera-only, no gallery import, timestamp + optional GPS embedded. + Photo, + + /// Foreground session of >= X minutes; backgrounding pauses it. + Timer, + + /// Geofence dwell via passive location. + Location, + + /// Health platform confirms a workout occurred in the window. + Health, + + /// An accountability partner taps to confirm. + Witness, +} + +String proofLabel(ProofType value) { + switch (value) { + case ProofType.Honour: + return "Honour"; + case ProofType.Photo: + return "Photo"; + case ProofType.Timer: + return "Timer"; + case ProofType.Location: + return "Location"; + case ProofType.Health: + return "Health"; + case ProofType.Witness: + return "Witness"; + } +} + +ProofType getProofType(String? name) { + for (ProofType value in ProofType.values) { + if (value.name == name) { + return value; + } + } + return ProofType.Honour; +} diff --git a/frontend/lib/Grounded/about/internal/application/Standing.dart b/frontend/lib/Grounded/about/internal/application/Standing.dart new file mode 100644 index 0000000..79c284a --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/Standing.dart @@ -0,0 +1,35 @@ +/// The current disciplinary state. Derived from debt, drives what the app +/// permits: Good -> Warned -> Grounded -> Lockdown. +enum Standing { Good, Warned, Grounded, Lockdown } + +String standingLabel(Standing value) { + switch (value) { + case Standing.Good: + return "Good standing"; + case Standing.Warned: + return "Warned"; + case Standing.Grounded: + return "Grounded"; + case Standing.Lockdown: + return "Lockdown"; + } +} + +/// Whether the user is allowed to create new commitments in this standing. +bool canAddCommitments(Standing value) { + return value == Standing.Good || value == Standing.Warned; +} + +/// Whether elective commitments are permitted in this standing. +bool canAddElectives(Standing value) { + return value == Standing.Good; +} + +Standing getStanding(String? name) { + for (Standing value in Standing.values) { + if (value.name == name) { + return value; + } + } + return Standing.Good; +} diff --git a/frontend/lib/Grounded/about/internal/application/TextType.dart b/frontend/lib/Grounded/about/internal/application/TextType.dart new file mode 100644 index 0000000..3067b56 --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/TextType.dart @@ -0,0 +1,6 @@ +enum TextType { + Bold, + Light, + Regular, + Medium, +} diff --git a/frontend/lib/Grounded/about/internal/application/Token.dart b/frontend/lib/Grounded/about/internal/application/Token.dart new file mode 100644 index 0000000..c845ac6 --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/Token.dart @@ -0,0 +1,34 @@ +class Token { + String accessToken; + + String refreshToken; + + String tokenType; + + int expiresIn; + + String scope; + + Token(this.accessToken, this.refreshToken, this.tokenType, this.expiresIn, + this.scope); + + factory Token.fromJsonMap(Map json) { + return Token( + json['access_token'] ?? "", + json['refresh_token'] ?? "", + json['token_type'] ?? "", + json['expires_in'] ?? 0, + json['scope'] ?? "", + ); + } + + Map toJson() { + final Map data = {}; + data['access_token'] = accessToken; + data['refresh_token'] = refreshToken; + data['token_type'] = tokenType; + data['expires_in'] = expiresIn; + data['scope'] = scope; + return data; + } +} diff --git a/frontend/lib/Grounded/about/internal/application/ToneLevel.dart b/frontend/lib/Grounded/about/internal/application/ToneLevel.dart new file mode 100644 index 0000000..b3507a5 --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/ToneLevel.dart @@ -0,0 +1,23 @@ +/// The tone slider. Hard-capped: no copy ever attacks the user's worth, only +/// their behaviour. +enum ToneLevel { Firm, Strict, DrillSergeant } + +String toneLabel(ToneLevel value) { + switch (value) { + case ToneLevel.Firm: + return "Firm"; + case ToneLevel.Strict: + return "Strict"; + case ToneLevel.DrillSergeant: + return "Drill Sergeant"; + } +} + +ToneLevel getToneLevel(String? name) { + for (ToneLevel value in ToneLevel.values) { + if (value.name == name) { + return value; + } + } + return ToneLevel.Strict; +} diff --git a/frontend/lib/Grounded/about/internal/application/UserDetails.dart b/frontend/lib/Grounded/about/internal/application/UserDetails.dart new file mode 100644 index 0000000..1eaec69 --- /dev/null +++ b/frontend/lib/Grounded/about/internal/application/UserDetails.dart @@ -0,0 +1,57 @@ +import 'Standing.dart'; +import 'ToneLevel.dart'; + +class UserDetails { + String pic; + + String name; + + String timezone; + + ToneLevel tone; + + Standing standing; + + double debtScore; + + int amnestyTokens; + + bool sickMode; + + UserDetails({ + required this.pic, + required this.name, + this.timezone = "Africa/Nairobi", + this.tone = ToneLevel.Strict, + this.standing = Standing.Good, + this.debtScore = 0, + this.amnestyTokens = 0, + this.sickMode = false, + }); + + factory UserDetails.fromJson(Map json) { + return UserDetails( + pic: json['pic'] ?? "", + name: json['name'] ?? "", + timezone: json['timezone'] ?? "Africa/Nairobi", + tone: getToneLevel(json['tone']), + standing: getStanding(json['standing']), + debtScore: (json['debtScore'] ?? 0).toDouble(), + amnestyTokens: json['amnestyTokens'] ?? 0, + sickMode: json['sickMode'] ?? false, + ); + } + + Map toJson() { + final Map data = {}; + data['pic'] = pic; + data['name'] = name; + data['timezone'] = timezone; + data['tone'] = tone.name; + data['standing'] = standing.name; + data['debtScore'] = debtScore; + data['amnestyTokens'] = amnestyTokens; + data['sickMode'] = sickMode; + return data; + } +} diff --git a/frontend/lib/Grounded/about/internal/file/ConnectFileStorage.dart b/frontend/lib/Grounded/about/internal/file/ConnectFileStorage.dart new file mode 100644 index 0000000..dedea01 --- /dev/null +++ b/frontend/lib/Grounded/about/internal/file/ConnectFileStorage.dart @@ -0,0 +1,13 @@ +import 'dart:typed_data'; + +abstract class ConnectFileStorage { + /// Persists proof bytes locally and returns the reference the completion + /// request carries. + Future saveProof(String name, Uint8List bytes); + + Future readProof(String reference); + + Future deleteProof(String reference); + + Future proofDirectory(); +} diff --git a/frontend/lib/Grounded/about/internal/file/FileStorage.dart b/frontend/lib/Grounded/about/internal/file/FileStorage.dart new file mode 100644 index 0000000..6c0882d --- /dev/null +++ b/frontend/lib/Grounded/about/internal/file/FileStorage.dart @@ -0,0 +1,53 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:path_provider/path_provider.dart'; + +import 'ConnectFileStorage.dart'; + +class FileStorage implements ConnectFileStorage { + static const String proofFolder = "proof"; + + @override + Future proofDirectory() async { + final Directory base = await getApplicationDocumentsDirectory(); + final Directory folder = Directory("${base.path}/$proofFolder"); + + if (!await folder.exists()) { + await folder.create(recursive: true); + } + + return folder.path; + } + + @override + Future saveProof(String name, Uint8List bytes) async { + final String folder = await proofDirectory(); + final File file = File("$folder/$name"); + await file.writeAsBytes(bytes); + return file.path; + } + + @override + Future readProof(String reference) async { + final File file = File(reference); + + if (!await file.exists()) { + return null; + } + + return await file.readAsBytes(); + } + + @override + Future deleteProof(String reference) async { + final File file = File(reference); + + if (!await file.exists()) { + return false; + } + + await file.delete(); + return true; + } +} diff --git a/frontend/lib/Grounded/comms/Comms.dart b/frontend/lib/Grounded/comms/Comms.dart new file mode 100644 index 0000000..17d3775 --- /dev/null +++ b/frontend/lib/Grounded/comms/Comms.dart @@ -0,0 +1,508 @@ +import 'package:dio/dio.dart'; + +import '../about/external/data/pages/request/CommitmentsRequest.dart'; +import '../about/external/data/pages/request/HistoryRequest.dart'; +import '../about/external/initial/AbandonRequest.dart'; +import '../about/external/initial/AmnestyRequest.dart'; +import '../about/external/initial/CommitmentRequest.dart'; +import '../about/external/initial/CompletionRequest.dart'; +import '../about/external/initial/DeferralRequest.dart'; +import '../about/external/initial/DeviceRequest.dart'; +import '../about/external/initial/GoalRequest.dart'; +import '../about/external/initial/IdRequest.dart'; +import '../about/external/initial/LoginData.dart'; +import '../about/external/initial/ReportCardRequest.dart'; +import '../about/external/initial/SessionLogRequest.dart'; +import '../about/external/initial/SickModeRequest.dart'; +import '../about/external/initial/ToneRequest.dart'; +import '../about/internal/application/MeDescription.dart'; +import '../about/internal/application/Pair.dart'; +import '../configs/Env.dart'; +import '../memory/ConnectInternalMemory.dart'; +import 'CommsDirections.dart'; +import 'ConnectComms.dart'; + +class Comms implements ConnectComms { + Dio dio = Dio(); + + ConnectInternalMemory helper; + + Comms(this.helper); + + /// Builds the identity headers and resolves the URL. Headers are reset on + /// every call so a stale Authorization can never leak into an auth-flow + /// request. + Future getRequestHeaders(String url, String urlData) async { + dio.options.headers = {}; + + MeDescription data = await helper.getMyDescription(); + + if (data.token.isNotEmpty && url != deviceToken) { + dio.options.headers["Authorization"] = "Bearer ${data.token}"; + } + + if (url == logoutRequest) { + dio.options.headers.remove("Authorization"); + } + + dio.options.contentType = Headers.jsonContentType; + + dio.options.responseType = ResponseType.json; + + // The backend identity contract: what = encrypted access code, + // whom = encrypted device id, version = the app build. + dio.options.headers["what"] = data.name; + + dio.options.headers["whom"] = data.id; + + dio.options.headers["version"] = localisedAppVersion; + + return Pair("${_routeFor(url)}$url$urlData", dio.options.headers); + } + + /// Auth-flow paths go to Prospect, training paths to Training, everything + /// else to the Discipline service. + String _routeFor(String url) { + if (url == deviceReg || + url == deviceToken || + url == loginUser || + url == logoutRequest || + url == aboutMe || + url == accountRecovery || + url.startsWith('InAugurate/')) { + return prospectRoute; + } + + if (url.startsWith('Program/') || + url.startsWith('Session/') || + url.startsWith('Metrics/')) { + return trainingRoute; + } + + return disciplineRoute; + } + + // ── Auth ────────────────────────────────────────────────────────────────── + + @override + Future registerDevice(DeviceRequest request) async { + Pair navigation = await getRequestHeaders(deviceReg, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future registerDeviceToken(String token) async { + Pair navigation = await getRequestHeaders(deviceToken, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: {"token": token}); + } + + @override + Future login(LoginData request) async { + Pair navigation = await getRequestHeaders(loginUser, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future logout() async { + Pair navigation = await getRequestHeaders(logoutRequest, ""); + dio.options.headers = navigation.value; + return await dio.get(navigation.key); + } + + @override + Future me() async { + Pair navigation = await getRequestHeaders(aboutMe, ""); + dio.options.headers = navigation.value; + return await dio.get(navigation.key); + } + + // ── Goals ───────────────────────────────────────────────────────────────── + + @override + Future getMyGoals(HistoryRequest request) async { + Pair navigation = await getRequestHeaders(myGoals, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future saveGoalEntry(GoalRequest request) async { + Pair navigation = await getRequestHeaders(saveGoal, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getGoalTasks(IdRequest request) async { + Pair navigation = await getRequestHeaders(goalTasks, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future archiveGoalEntry(IdRequest request) async { + Pair navigation = await getRequestHeaders(archiveGoal, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + // ── Commitments ─────────────────────────────────────────────────────────── + + @override + Future getTodayPlan(CommitmentsRequest request) async { + Pair navigation = await getRequestHeaders(todayPlan, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getMyCommitments(CommitmentsRequest request) async { + Pair navigation = await getRequestHeaders(myCommitments, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getOverdueQueue(HistoryRequest request) async { + Pair navigation = await getRequestHeaders(overdueQueue, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future saveCommitmentEntry(CommitmentRequest request) async { + Pair navigation = await getRequestHeaders(saveCommitment, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future updateCommitmentEntry(CommitmentRequest request) async { + Pair navigation = await getRequestHeaders(updateCommitment, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future deleteCommitmentEntry(IdRequest request) async { + Pair navigation = await getRequestHeaders(deleteCommitment, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future completeCommitmentEntry(CompletionRequest request) async { + Pair navigation = await getRequestHeaders(completeCommitment, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future deferCommitmentEntry(DeferralRequest request) async { + Pair navigation = await getRequestHeaders(deferCommitment, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future abandonCommitmentEntry(AbandonRequest request) async { + Pair navigation = await getRequestHeaders(abandonCommitment, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getCommitmentHistory(HistoryRequest request) async { + Pair navigation = await getRequestHeaders(commitmentHistory, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getCommitmentEvents(IdRequest request) async { + Pair navigation = await getRequestHeaders(commitmentEvents, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + // ── Capacity ────────────────────────────────────────────────────────────── + + @override + Future checkCapacity(CommitmentsRequest request) async { + Pair navigation = await getRequestHeaders(capacityCheck, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getCapacityProfileEntry() async { + Pair navigation = await getRequestHeaders(capacityProfilePath, ""); + dio.options.headers = navigation.value; + return await dio.get(navigation.key); + } + + // ── Debt & standing ─────────────────────────────────────────────────────── + + @override + Future getDebtSummary() async { + Pair navigation = await getRequestHeaders(debtSummary, ""); + dio.options.headers = navigation.value; + return await dio.get(navigation.key); + } + + @override + Future getDebtLedger(HistoryRequest request) async { + Pair navigation = await getRequestHeaders(debtLedger, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getDebtTrend(ReportCardRequest request) async { + Pair navigation = await getRequestHeaders(debtTrendPath, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getStanding() async { + Pair navigation = await getRequestHeaders(standingPath, ""); + dio.options.headers = navigation.value; + return await dio.get(navigation.key); + } + + @override + Future getStandingHistory(HistoryRequest request) async { + Pair navigation = await getRequestHeaders(standingHistory, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + // ── Excuses ─────────────────────────────────────────────────────────────── + + @override + Future getExcuseClusters(ReportCardRequest request) async { + Pair navigation = await getRequestHeaders(excuseClusters, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + // ── Proof ───────────────────────────────────────────────────────────────── + + @override + Future uploadPhotoProof(FormData request) async { + Pair navigation = await getRequestHeaders(uploadProofPhoto, ""); + dio.options.headers = navigation.value; + dio.options.contentType = Headers.multipartFormDataContentType; + return await dio.post(navigation.key, data: request); + } + + @override + Future submitTimerProofEntry(CompletionRequest request) async { + Pair navigation = await getRequestHeaders(submitTimerProof, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future submitLocationProofEntry(CompletionRequest request) async { + Pair navigation = await getRequestHeaders(submitLocationProof, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + // ── Guardrails ──────────────────────────────────────────────────────────── + + @override + Future spendAmnestyToken(AmnestyRequest request) async { + Pair navigation = await getRequestHeaders(spendAmnesty, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getAmnestyBalance() async { + Pair navigation = await getRequestHeaders(amnestyBalance, ""); + dio.options.headers = navigation.value; + return await dio.get(navigation.key); + } + + @override + Future updateSickMode(SickModeRequest request) async { + Pair navigation = await getRequestHeaders(setSickMode, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future checkDistress() async { + Pair navigation = await getRequestHeaders(distressCheck, ""); + dio.options.headers = navigation.value; + return await dio.get(navigation.key); + } + + @override + Future updateTone(ToneRequest request) async { + Pair navigation = await getRequestHeaders(setTone, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + // ── Habits & routines ───────────────────────────────────────────────────── + + @override + Future getMyHabits(HistoryRequest request) async { + Pair navigation = await getRequestHeaders(myHabits, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future saveHabitEntry(Map request) async { + Pair navigation = await getRequestHeaders(saveHabit, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future logHabitEntry(IdRequest request) async { + Pair navigation = await getRequestHeaders(logHabit, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getKeystoneHabits() async { + Pair navigation = await getRequestHeaders(keystoneHabits, ""); + dio.options.headers = navigation.value; + return await dio.get(navigation.key); + } + + @override + Future getMyRoutines(HistoryRequest request) async { + Pair navigation = await getRequestHeaders(myRoutines, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future logRoutineChainEntry(Map request) async { + Pair navigation = await getRequestHeaders(logRoutineChain, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + // ── Training ────────────────────────────────────────────────────────────── + + @override + Future getMyPrograms(HistoryRequest request) async { + Pair navigation = await getRequestHeaders(myPrograms, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future saveProgramEntry(Map request) async { + Pair navigation = await getRequestHeaders(saveProgram, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future activateProgramEntry(IdRequest request) async { + Pair navigation = await getRequestHeaders(activateProgram, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getProgramSessions(IdRequest request) async { + Pair navigation = await getRequestHeaders(programSessions, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future startSessionEntry(IdRequest request) async { + Pair navigation = await getRequestHeaders(startSession, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future saveSessionLogEntry(SessionLogRequest request) async { + Pair navigation = await getRequestHeaders(saveSessionLog, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getSessionHistory(HistoryRequest request) async { + Pair navigation = await getRequestHeaders(sessionHistory, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getLastSessionFor(IdRequest request) async { + Pair navigation = await getRequestHeaders(lastSessionFor, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getWeeklyVolume(ReportCardRequest request) async { + Pair navigation = await getRequestHeaders(weeklyVolume, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getContactVolume(ReportCardRequest request) async { + Pair navigation = await getRequestHeaders(contactVolume, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getPersonalRecords(HistoryRequest request) async { + Pair navigation = await getRequestHeaders(personalRecords, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + // ── Report card ─────────────────────────────────────────────────────────── + + @override + Future getWeeklyReportCard(ReportCardRequest request) async { + Pair navigation = await getRequestHeaders(weeklyReportCard, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future getMonthlyReportCard(ReportCardRequest request) async { + Pair navigation = await getRequestHeaders(monthlyReportCard, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + // ── Notifications ───────────────────────────────────────────────────────── + + @override + Future getMyNotifications(HistoryRequest request) async { + Pair navigation = await getRequestHeaders(myNotifications, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } + + @override + Future acknowledgeNudgeEntry(IdRequest request) async { + Pair navigation = await getRequestHeaders(acknowledgeNudge, ""); + dio.options.headers = navigation.value; + return await dio.post(navigation.key, data: request); + } +} diff --git a/frontend/lib/Grounded/comms/CommsDirections.dart b/frontend/lib/Grounded/comms/CommsDirections.dart new file mode 100644 index 0000000..fd618c3 --- /dev/null +++ b/frontend/lib/Grounded/comms/CommsDirections.dart @@ -0,0 +1,174 @@ +import '../configs/Env.dart'; + +/// The single switch between local and production. +bool isProd = false; + +// ── Service routes ──────────────────────────────────────────────────────────── +String prospectRoute = isProd + ? "$groundedRouteProd/Prospect/" + : "$groundedRouteLocal:40003/Prospect/"; + +String disciplineRoute = isProd + ? "$groundedRouteProd/Discipline/" + : "$groundedRouteLocal:40005/Discipline/"; + +String trainingRoute = isProd + ? "$groundedRouteProd/Training/" + : "$groundedRouteLocal:40004/Training/"; + +// ── Auth flow (Prospect) ────────────────────────────────────────────────────── +String deviceReg = "Device/NewDevice"; + +String deviceToken = 'Device/Note'; + +String loginUser = 'User/Login'; + +String logoutRequest = 'User/Logout'; + +String aboutMe = 'User/Me'; + +String accountRecovery = "User/RecoverRequest"; + +String inaugurateProspect = 'InAugurate/Prospect'; + +String inaugurateTerms = 'InAugurate/Terms'; + +String registerCredentialsLocation = 'InAugurate/UserAndPass'; + +// ── Goals ───────────────────────────────────────────────────────────────────── +String myGoals = 'Goal/Mine'; + +String saveGoal = 'Goal/Save'; + +String goalTasks = 'Goal/Tasks'; + +String archiveGoal = 'Goal/Archive'; + +// ── Commitments ─────────────────────────────────────────────────────────────── +String myCommitments = 'Commitment/Mine'; + +String todayPlan = 'Commitment/Today'; + +String overdueQueue = 'Commitment/Overdue'; + +String saveCommitment = 'Commitment/Save'; + +String updateCommitment = 'Commitment/Update'; + +String deleteCommitment = 'Commitment/Delete'; + +String completeCommitment = 'Commitment/Complete'; + +String deferCommitment = 'Commitment/Defer'; + +String abandonCommitment = 'Commitment/Abandon'; + +String commitmentHistory = 'Commitment/History'; + +String commitmentEvents = 'Commitment/Events'; + +// ── Capacity ────────────────────────────────────────────────────────────────── +String capacityCheck = 'Capacity/Check'; + +String capacityProfilePath = 'Capacity/Profile'; + +// ── Debt & standing ─────────────────────────────────────────────────────────── +String debtSummary = 'Debt/Summary'; + +String debtLedger = 'Debt/Ledger'; + +String debtTrendPath = 'Debt/Trend'; + +String standingPath = 'Standing/Current'; + +String standingHistory = 'Standing/History'; + +// ── Excuses ─────────────────────────────────────────────────────────────────── +String excuseClusters = 'Excuse/Clusters'; + +String excuseInsights = 'Excuse/Insights'; + +// ── Proof ───────────────────────────────────────────────────────────────────── +String uploadProofPhoto = 'Proof/PhotoUpload'; + +String submitTimerProof = 'Proof/Timer'; + +String submitLocationProof = 'Proof/Location'; + +String requestWitnessProof = 'Proof/Witness'; + +// ── Guardrails ──────────────────────────────────────────────────────────────── +String spendAmnesty = 'Guardrail/Amnesty'; + +String amnestyBalance = 'Guardrail/AmnestyBalance'; + +String setSickMode = 'Guardrail/SickMode'; + +String distressCheck = 'Guardrail/Distress'; + +String setTone = 'Guardrail/Tone'; + +// ── Habits & routines ───────────────────────────────────────────────────────── +String myHabits = 'Habit/Mine'; + +String saveHabit = 'Habit/Save'; + +String logHabit = 'Habit/Log'; + +String keystoneHabits = 'Habit/Keystone'; + +String myRoutines = 'Routine/Mine'; + +String saveRoutine = 'Routine/Save'; + +String logRoutineChain = 'Routine/Log'; + +// ── Training ────────────────────────────────────────────────────────────────── +String myPrograms = 'Program/Mine'; + +String saveProgram = 'Program/Save'; + +String activateProgram = 'Program/Activate'; + +String programSessions = 'Program/Sessions'; + +String saveSessionTemplate = 'Program/SaveTemplate'; + +String startSession = 'Session/Start'; + +String saveSessionLog = 'Session/Save'; + +String sessionHistory = 'Session/History'; + +String lastSessionFor = 'Session/Last'; + +String weeklyVolume = 'Session/WeeklyVolume'; + +String contactVolume = 'Session/Contacts'; + +String personalRecords = 'Session/Records'; + +String bodyMetrics = 'Metrics/Body'; + +String saveBodyMetric = 'Metrics/SaveBody'; + +// ── Report card ─────────────────────────────────────────────────────────────── +String weeklyReportCard = 'Report/Weekly'; + +String monthlyReportCard = 'Report/Monthly'; + +String reportHistory = 'Report/History'; + +// ── Notifications ───────────────────────────────────────────────────────────── +String myNotifications = 'Notifications/Mine'; + +String readNotification = 'Notifications/Read'; + +String acknowledgeNudge = 'Notifications/Acknowledge'; + +// ── Partners ────────────────────────────────────────────────────────────────── +String myPartners = 'Partner/Mine'; + +String invitePartner = 'Partner/Invite'; + +String partnerPolicy = 'Partner/Policy'; diff --git a/frontend/lib/Grounded/comms/ConnectComms.dart b/frontend/lib/Grounded/comms/ConnectComms.dart new file mode 100644 index 0000000..950d4ad --- /dev/null +++ b/frontend/lib/Grounded/comms/ConnectComms.dart @@ -0,0 +1,145 @@ +import 'package:dio/dio.dart'; + +import '../about/external/data/pages/request/CommitmentsRequest.dart'; +import '../about/external/data/pages/request/HistoryRequest.dart'; +import '../about/external/initial/AbandonRequest.dart'; +import '../about/external/initial/AmnestyRequest.dart'; +import '../about/external/initial/CommitmentRequest.dart'; +import '../about/external/initial/CompletionRequest.dart'; +import '../about/external/initial/DeferralRequest.dart'; +import '../about/external/initial/DeviceRequest.dart'; +import '../about/external/initial/GoalRequest.dart'; +import '../about/external/initial/IdRequest.dart'; +import '../about/external/initial/LoginData.dart'; +import '../about/external/initial/ReportCardRequest.dart'; +import '../about/external/initial/SessionLogRequest.dart'; +import '../about/external/initial/SickModeRequest.dart'; +import '../about/external/initial/ToneRequest.dart'; + +abstract class ConnectComms { + // ── Auth ──────────────────────────────────────────────────────────────── + Future registerDevice(DeviceRequest request); + + Future registerDeviceToken(String token); + + Future login(LoginData request); + + Future logout(); + + Future me(); + + // ── Goals ─────────────────────────────────────────────────────────────── + Future getMyGoals(HistoryRequest request); + + Future saveGoalEntry(GoalRequest request); + + Future getGoalTasks(IdRequest request); + + Future archiveGoalEntry(IdRequest request); + + // ── Commitments ───────────────────────────────────────────────────────── + Future getTodayPlan(CommitmentsRequest request); + + Future getMyCommitments(CommitmentsRequest request); + + Future getOverdueQueue(HistoryRequest request); + + Future saveCommitmentEntry(CommitmentRequest request); + + Future updateCommitmentEntry(CommitmentRequest request); + + Future deleteCommitmentEntry(IdRequest request); + + Future completeCommitmentEntry(CompletionRequest request); + + Future deferCommitmentEntry(DeferralRequest request); + + Future abandonCommitmentEntry(AbandonRequest request); + + Future getCommitmentHistory(HistoryRequest request); + + Future getCommitmentEvents(IdRequest request); + + // ── Capacity ──────────────────────────────────────────────────────────── + Future checkCapacity(CommitmentsRequest request); + + Future getCapacityProfileEntry(); + + // ── Debt & standing ───────────────────────────────────────────────────── + Future getDebtSummary(); + + Future getDebtLedger(HistoryRequest request); + + Future getDebtTrend(ReportCardRequest request); + + Future getStanding(); + + Future getStandingHistory(HistoryRequest request); + + // ── Excuses ───────────────────────────────────────────────────────────── + Future getExcuseClusters(ReportCardRequest request); + + // ── Proof ─────────────────────────────────────────────────────────────── + Future uploadPhotoProof(FormData request); + + Future submitTimerProofEntry(CompletionRequest request); + + Future submitLocationProofEntry(CompletionRequest request); + + // ── Guardrails ────────────────────────────────────────────────────────── + Future spendAmnestyToken(AmnestyRequest request); + + Future getAmnestyBalance(); + + Future updateSickMode(SickModeRequest request); + + Future checkDistress(); + + Future updateTone(ToneRequest request); + + // ── Habits & routines ─────────────────────────────────────────────────── + Future getMyHabits(HistoryRequest request); + + Future saveHabitEntry(Map request); + + Future logHabitEntry(IdRequest request); + + Future getKeystoneHabits(); + + Future getMyRoutines(HistoryRequest request); + + Future logRoutineChainEntry(Map request); + + // ── Training ──────────────────────────────────────────────────────────── + Future getMyPrograms(HistoryRequest request); + + Future saveProgramEntry(Map request); + + Future activateProgramEntry(IdRequest request); + + Future getProgramSessions(IdRequest request); + + Future startSessionEntry(IdRequest request); + + Future saveSessionLogEntry(SessionLogRequest request); + + Future getSessionHistory(HistoryRequest request); + + Future getLastSessionFor(IdRequest request); + + Future getWeeklyVolume(ReportCardRequest request); + + Future getContactVolume(ReportCardRequest request); + + Future getPersonalRecords(HistoryRequest request); + + // ── Report card ───────────────────────────────────────────────────────── + Future getWeeklyReportCard(ReportCardRequest request); + + Future getMonthlyReportCard(ReportCardRequest request); + + // ── Notifications ─────────────────────────────────────────────────────── + Future getMyNotifications(HistoryRequest request); + + Future acknowledgeNudgeEntry(IdRequest request); +} diff --git a/frontend/lib/Grounded/configs/Env.dart b/frontend/lib/Grounded/configs/Env.dart new file mode 100644 index 0000000..1e685ef --- /dev/null +++ b/frontend/lib/Grounded/configs/Env.dart @@ -0,0 +1,7 @@ +import 'package:flutter_dotenv/flutter_dotenv.dart'; + +String groundedRouteProd = dotenv.get('GROUNDED_PRODUCTION_PATH', fallback: ''); + +String groundedRouteLocal = dotenv.get('GROUNDED_LOCAL_PATH', fallback: ''); + +String localisedAppVersion = dotenv.get('LOCALISED_APP_VERSION', fallback: ''); diff --git a/frontend/lib/Grounded/configs/Navigator.dart b/frontend/lib/Grounded/configs/Navigator.dart new file mode 100644 index 0000000..abb9082 --- /dev/null +++ b/frontend/lib/Grounded/configs/Navigator.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:page_transition/page_transition.dart'; + +import '../about/internal/application/NavigatorType.dart'; + +/// Every transition in the app funnels through here. Destinations are widget +/// instances, not named routes, and the single choke point is what lets a +/// device-integrity check gate all routing. +class GroundedNavigation { + void navigateToPage(NavigatorType type, dynamic path, BuildContext context) { + _runSec(context, type, path); + } + + Future _runSec(BuildContext context, NavigatorType type, dynamic path) async { + switch (type) { + case NavigatorType.openFully: + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (BuildContext context) => path)); + break; + case NavigatorType.justOpen: + Navigator.push( + context, + PageTransition( + type: PageTransitionType.size, + alignment: Alignment.center, + child: path)); + break; + case NavigatorType.replaceCurrent: + Navigator.pushReplacement( + context, + PageTransition( + type: PageTransitionType.scale, + alignment: Alignment.center, + curve: Curves.ease, + duration: const Duration(microseconds: 9000), + child: path)); + break; + case NavigatorType.makeNewMain: + Navigator.pushAndRemoveUntil( + context, + PageTransition( + type: PageTransitionType.fade, + alignment: Alignment.center, + child: path), + ModalRoute.withName('/')); + break; + } + } + + /// The reload-on-return channel: the child pops with a result and the parent + /// refreshes on it. + Future navigateToPageWithData( + dynamic path, BuildContext context) async { + return await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => path, + ), + ); + } +} diff --git a/frontend/lib/Grounded/configs/NotificationServiceConfig.dart b/frontend/lib/Grounded/configs/NotificationServiceConfig.dart new file mode 100644 index 0000000..6ee72c4 --- /dev/null +++ b/frontend/lib/Grounded/configs/NotificationServiceConfig.dart @@ -0,0 +1,204 @@ +import 'dart:io'; + +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; + +import '../about/external/data/Commitment.dart'; +import '../about/external/data/LiveSession.dart'; +import '../about/internal/application/CommitmentClass.dart'; +import '../about/internal/application/EscalationTier.dart'; +import '../about/internal/application/ToneLevel.dart'; +import '../utils/CommonUtils.dart'; +import '../utils/ToneEngine.dart'; + +/// Notification channels, the ongoing "task running" notification, and the +/// full-screen alarm used for non-negotiables. +class LocalNotificationEngine { + static final FlutterLocalNotificationsPlugin plugin = + FlutterLocalNotificationsPlugin(); + + /// Ordinary reminders — respects quiet hours. + static const String reminderChannel = "grounded_reminders"; + + /// The ongoing notification attached to a running task. Not dismissable, so + /// a task in progress is always one tap away from the lock screen. + static const String sessionChannel = "grounded_session"; + + /// Alarm-class, full-screen. Reserved for non-negotiables, and the only + /// channel that overrides quiet hours. + static const String alarmChannel = "grounded_alarm"; + + static const int sessionNotificationId = 9000; + + static bool _ready = false; + + static Future init() async { + if (_ready) { + return; + } + + const AndroidInitializationSettings android = + AndroidInitializationSettings('@mipmap/ic_launcher'); + + const DarwinInitializationSettings apple = DarwinInitializationSettings( + requestAlertPermission: true, + requestBadgePermission: true, + requestSoundPermission: true, + // Critical alerts need Apple entitlement approval; requesting without it + // is simply ignored rather than failing. + requestCriticalPermission: true, + ); + + await plugin.initialize( + settings: const InitializationSettings( + android: android, iOS: apple, macOS: apple), + ); + + if (Platform.isAndroid) { + await _createAndroidChannels(); + } + + _ready = true; + } + + static Future _createAndroidChannels() async { + final AndroidFlutterLocalNotificationsPlugin? android = + plugin.resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>(); + + if (android == null) { + return; + } + + await android.createNotificationChannel(const AndroidNotificationChannel( + reminderChannel, + 'Reminders', + description: 'Nudges about commitments that are due.', + importance: Importance.defaultImportance, + )); + + await android.createNotificationChannel(const AndroidNotificationChannel( + sessionChannel, + 'Task in progress', + description: 'The ongoing notification for a task you are running.', + importance: Importance.low, + playSound: false, + enableVibration: false, + )); + + await android.createNotificationChannel(const AndroidNotificationChannel( + alarmChannel, + 'Non-negotiables', + description: + 'Full-screen alarms for non-negotiable commitments. These ignore quiet hours.', + importance: Importance.max, + playSound: true, + enableVibration: true, + )); + + await android.requestNotificationsPermission(); + + // Exact alarms are what let a window actually close on time rather than + // whenever the OS feels like it. + await android.requestExactAlarmsPermission(); + } + + /// The ongoing notification for a running task. Shows the live remaining + /// time so it is useful from the lock screen without unlocking. + static Future showSessionNotification(LiveSession session) async { + await init(); + + final String remaining = session.requiredSeconds > 0 + ? "${formatClock(session.remainingSeconds())} remaining" + : "${formatClock(session.elapsedSeconds())} elapsed"; + + final AndroidNotificationDetails android = AndroidNotificationDetails( + sessionChannel, + 'Task in progress', + channelDescription: 'The ongoing notification for a running task.', + importance: Importance.low, + priority: Priority.low, + ongoing: true, + autoCancel: false, + onlyAlertOnce: true, + showWhen: true, + usesChronometer: session.requiredSeconds <= 0, + category: AndroidNotificationCategory.workout, + actions: const [ + AndroidNotificationAction('pause', 'Pause'), + AndroidNotificationAction('finish', 'Finish'), + ], + ); + + await plugin.show( + id: sessionNotificationId, + title: session.title, + body: session.goalTitle.isEmpty + ? remaining + : "${session.goalTitle} · $remaining", + notificationDetails: NotificationDetails( + android: android, + iOS: const DarwinNotificationDetails(presentBanner: false), + ), + payload: session.commitmentId, + ); + } + + static Future cancelSessionNotification() async { + await init(); + await plugin.cancel(id: sessionNotificationId); + } + + /// A due-window nudge. Non-negotiables go out full-screen and alarm-class so + /// they surface over the lock screen; everything else is an ordinary + /// notification. + static Future showCommitmentNudge( + Commitment commitment, + EscalationTier tier, + ToneLevel tone, + ) async { + await init(); + + final bool nonNegotiable = + commitment.commitmentClass == CommitmentClass.NonNegotiable; + + final AndroidNotificationDetails android = AndroidNotificationDetails( + nonNegotiable ? alarmChannel : reminderChannel, + nonNegotiable ? 'Non-negotiables' : 'Reminders', + importance: nonNegotiable ? Importance.max : Importance.defaultImportance, + priority: nonNegotiable ? Priority.max : Priority.defaultPriority, + // The full-screen intent is what turns this into a lock-screen takeover + // rather than a banner that can be swiped past. + fullScreenIntent: nonNegotiable, + category: nonNegotiable + ? AndroidNotificationCategory.alarm + : AndroidNotificationCategory.reminder, + actions: [ + const AndroidNotificationAction('start', 'Start now'), + if (!nonNegotiable) + const AndroidNotificationAction('snooze', 'Snooze'), + ], + ); + + await plugin.show( + id: commitment.id.hashCode, + title: commitment.title, + body: ToneEngine.nudge(tier, tone, commitment.title), + notificationDetails: NotificationDetails( + android: android, + iOS: DarwinNotificationDetails( + // Critical alerts bypass Do Not Disturb, and are the iOS equivalent + // of the Android full-screen intent. Requires Apple approval. + interruptionLevel: nonNegotiable + ? InterruptionLevel.critical + : InterruptionLevel.active, + ), + ), + payload: commitment.id, + ); + } + + static Future cancelAll() async { + await init(); + await plugin.cancelAll(); + } +} diff --git a/frontend/lib/Grounded/designs/Component.dart b/frontend/lib/Grounded/designs/Component.dart new file mode 100644 index 0000000..c553c99 --- /dev/null +++ b/frontend/lib/Grounded/designs/Component.dart @@ -0,0 +1,202 @@ +import 'package:flutter/material.dart'; + +import '../about/internal/application/TextType.dart'; +import '../utils/Colors.dart'; +import 'text/Text.dart'; + +/// The font families, wrapped so no screen ever names a family directly. +/// General Sans — see fonts/LICENSE-GeneralSans.txt. +String getTextType(TextType type) { + switch (type) { + case TextType.Bold: + return "GroundedBold"; + case TextType.Light: + return "GroundedLight"; + case TextType.Regular: + return "GroundedRegular"; + case TextType.Medium: + return "GroundedMedium"; + } +} + +/// A labelled pill — the standing chip, the class chip, the proof chip. One +/// implementation so they stay visually identical everywhere. +Widget pill( + String label, + Color foreground, + Color background, { + IconData? icon, + double textSize = 10, +}) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: background, + borderRadius: BorderRadius.circular(999), + border: Border.all(color: foreground.withValues(alpha: 0.20), width: 1), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (icon != null) ...[ + Icon(icon, size: textSize + 2, color: foreground), + const SizedBox(width: 5), + ], + text( + label.toUpperCase(), + textSize, + TextType.Bold, + color: foreground, + letterSpacing: 0.7, + ), + ], + ), + ); +} + +/// The standard card surface. +Widget card({ + required Widget child, + EdgeInsets padding = const EdgeInsets.all(16), + EdgeInsets margin = EdgeInsets.zero, + Color? background, + Color? borderColor, + double radius = 16, + VoidCallback? onTap, +}) { + final Widget body = Container( + width: double.infinity, + padding: padding, + margin: margin, + decoration: BoxDecoration( + color: background ?? colorCard, + borderRadius: BorderRadius.circular(radius), + border: Border.all(color: borderColor ?? colorBorder, width: 1), + boxShadow: const [ + BoxShadow( + color: Color(0x08000000), + blurRadius: 18, + offset: Offset(0, 4), + ), + ], + ), + child: child, + ); + + if (onTap == null) { + return body; + } + + return GestureDetector(onTap: onTap, child: body); +} + +/// Section heading — small all-caps label over a large light title, the +/// house style used on every screen header. +Widget sectionHeader(String label, String title, {Color? titleColor}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text(label.toUpperCase(), 11, TextType.Bold, + color: colorGrey2, letterSpacing: 1.2), + const SizedBox(height: 8), + text(title, 32, TextType.Light, color: titleColor ?? colorPrimaryDark), + ], + ); +} + +/// A labelled statistic, used across the report card and the debt header. +Widget statTile( + String label, + String value, { + Color? valueColor, + String? caption, +}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text(label.toUpperCase(), 10, TextType.Bold, + color: colorGrey2, letterSpacing: 0.8), + const SizedBox(height: 6), + text(value, 26, TextType.Bold, color: valueColor ?? colorPrimaryDark), + if (caption != null) ...[ + const SizedBox(height: 2), + text(caption, 11, TextType.Regular, color: colorGrey2), + ], + ], + ); +} + +/// A hairline divider at the house opacity. +Widget hairline({EdgeInsets margin = EdgeInsets.zero}) { + return Container( + height: 1, + margin: margin, + color: colorDivider, + ); +} + +/// Empty state. Deliberately plain — an empty queue is good news and should +/// not be celebrated with confetti. +Widget emptyState( + IconData icon, + String title, + String description, { + Color? accent, +}) { + final Color tone = accent ?? colorGrey2; + + return Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 48), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 76, + height: 76, + decoration: BoxDecoration( + color: tone.withValues(alpha: 0.08), + shape: BoxShape.circle, + ), + child: Icon(icon, size: 32, color: tone), + ), + const SizedBox(height: 20), + text(title, 18, TextType.Bold, + color: colorPrimaryDark, align: TextAlign.center), + const SizedBox(height: 8), + text(description, 13, TextType.Regular, + color: colorGrey2, align: TextAlign.center, height: 1.5), + ], + ), + ), + ); +} + +/// A progress bar with the house geometry. +Widget meter( + double fraction, { + Color? fill, + Color? track, + double height = 8, +}) { + final double clamped = fraction.isNaN + ? 0 + : fraction < 0 + ? 0 + : fraction > 1 + ? 1 + : fraction; + + return ClipRRect( + borderRadius: BorderRadius.circular(999), + child: LinearProgressIndicator( + value: clamped, + minHeight: height, + backgroundColor: track ?? colorMuted, + valueColor: AlwaysStoppedAnimation(fill ?? colorPrimary), + ), + ); +} diff --git a/frontend/lib/Grounded/designs/Responsive.dart b/frontend/lib/Grounded/designs/Responsive.dart new file mode 100644 index 0000000..0a99f4d --- /dev/null +++ b/frontend/lib/Grounded/designs/Responsive.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; + +import '../about/internal/application/TextType.dart'; +import 'text/Text.dart'; + +class Responsive extends StatelessWidget { + final Widget mobile; + final Widget tablet; + final Widget desktop; + + const Responsive({ + super.key, + required this.desktop, + required this.mobile, + required this.tablet, + }); + + /// mobile < 650 + static bool isMobile(BuildContext context) => + MediaQuery.sizeOf(context).width < 650; + + /// tablet >= 650 + static bool isTablet(BuildContext context) => + MediaQuery.sizeOf(context).width >= 650; + + /// desktop >= 1100 + static bool isDesktop(BuildContext context) => + MediaQuery.sizeOf(context).width >= 1100; + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + if (isDesktop(context)) { + return desktop; + } else if (isTablet(context)) { + return tablet; + } else if (isMobile(context)) { + return mobile; + } else { + return SizedBox( + width: double.infinity, + height: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [text("NOT SUPPORTED", 12, TextType.Bold)], + ), + ); + } + }); + } +} diff --git a/frontend/lib/Grounded/designs/Shell.dart b/frontend/lib/Grounded/designs/Shell.dart new file mode 100644 index 0000000..510538a --- /dev/null +++ b/frontend/lib/Grounded/designs/Shell.dart @@ -0,0 +1,287 @@ +import 'package:flutter/material.dart'; + +import '../about/internal/application/TextType.dart'; +import '../utils/Colors.dart'; +import 'Component.dart'; +import 'text/Text.dart'; + +/// The house layout: black chrome at the top, a white sheet rising into it with +/// a large corner radius. Every screen is built from this so the app reads as +/// one object rather than a stack of pages. +class Sheet extends StatelessWidget { + /// Small all-caps label rendered in the black chrome, above the title. + final String eyebrow; + + /// The chrome title — small and centred, not the display title. + final String title; + + final Widget child; + + final VoidCallback? onBack; + + /// Optional trailing control in the chrome. + final Widget? action; + + /// Chrome colour. Defaults to near-black; standing screens tint it. + final Color? chrome; + + /// Rendered inside the chrome beneath the title — the standing strip. + final Widget? banner; + + final bool scrollable; + + const Sheet({ + super.key, + required this.title, + required this.child, + this.eyebrow = "", + this.onBack, + this.action, + this.chrome, + this.banner, + this.scrollable = true, + }); + + @override + Widget build(BuildContext context) { + final Color chromeColor = chrome ?? colorPrimaryDark; + + return Scaffold( + backgroundColor: chromeColor, + body: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SafeArea( + bottom: false, + child: Padding( + padding: const EdgeInsets.fromLTRB(12, 4, 12, 16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + SizedBox( + width: 44, + child: onBack == null + ? null + : _chromeButton( + Icons.arrow_back_ios_new_rounded, onBack!), + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (eyebrow.isNotEmpty) ...[ + text( + eyebrow.toUpperCase(), + 9, + TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), + letterSpacing: 1.2, + align: TextAlign.center, + ), + const SizedBox(height: 3), + ], + text( + title, + 15, + TextType.Medium, + color: colorWhite, + align: TextAlign.center, + ), + ], + ), + ), + SizedBox( + width: 44, + child: action == null + ? null + : Align( + alignment: Alignment.centerRight, + child: action, + ), + ), + ], + ), + if (banner != null) ...[ + const SizedBox(height: 16), + banner!, + ], + ], + ), + ), + ), + Expanded( + child: Container( + decoration: BoxDecoration( + color: colorPrimaryLight, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28), + ), + ), + clipBehavior: Clip.antiAlias, + child: scrollable + ? SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(20, 28, 20, 40), + child: child, + ) + : Padding( + padding: const EdgeInsets.fromLTRB(20, 28, 20, 0), + child: child, + ), + ), + ), + ], + ), + ); + } + + Widget _chromeButton(IconData icon, VoidCallback onTap) { + return GestureDetector( + onTap: onTap, + child: Container( + width: 38, + height: 38, + alignment: Alignment.center, + decoration: BoxDecoration( + color: colorWhite.withValues(alpha: 0.10), + shape: BoxShape.circle, + ), + child: Icon(icon, size: 15, color: colorWhite), + ), + ); + } +} + +/// A circular control for the black chrome, with an optional unread dot. +Widget chromeAction( + IconData icon, + VoidCallback onTap, { + bool dotted = false, + Color? dotColor, +}) { + return GestureDetector( + onTap: onTap, + child: Stack( + clipBehavior: Clip.none, + children: [ + Container( + width: 38, + height: 38, + alignment: Alignment.center, + decoration: BoxDecoration( + color: colorWhite.withValues(alpha: 0.10), + shape: BoxShape.circle, + ), + child: Icon(icon, size: 17, color: colorWhite), + ), + if (dotted) + Positioned( + top: 1, + right: 1, + child: Container( + width: 9, + height: 9, + decoration: BoxDecoration( + color: dotColor ?? colorAccent, + shape: BoxShape.circle, + border: Border.all(color: colorPrimaryDark, width: 1.5), + ), + ), + ), + ], + ), + ); +} + +/// The label/value pair — a tiny grey all-caps label sitting directly above a +/// value. The core unit of the whole interface. +Widget labelled( + String label, + String value, { + double valueSize = 15, + TextType valueType = TextType.Medium, + Color? valueColor, + Color? labelColor, + CrossAxisAlignment align = CrossAxisAlignment.start, +}) { + return Column( + crossAxisAlignment: align, + mainAxisSize: MainAxisSize.min, + children: [ + text( + label.toUpperCase(), + 9, + TextType.Bold, + color: labelColor ?? colorGrey2, + letterSpacing: 1.0, + ), + const SizedBox(height: 5), + text(value, valueSize, valueType, + color: valueColor ?? colorPrimaryDark), + ], + ); +} + +/// A row of metadata above a display title, as on the reference: small grey +/// pairs separated by generous space. +Widget metaRow(List items) { + final List spaced = []; + + for (int index = 0; index < items.length; index++) { + spaced.add(items[index]); + if (index != items.length - 1) { + spaced.add(const SizedBox(width: 28)); + } + } + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: spaced, + ); +} + +/// The oversized light display title that opens a section. +Widget displayTitle(String value, {Color? color, double size = 34}) { + return text(value, size, TextType.Light, + color: color ?? colorPrimaryDark, height: 1.15); +} + +/// A section break: hairline, then a small bold heading with an optional +/// trailing chip. +Widget sectionBreak(String heading, {Widget? trailing, String caption = ""}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + hairline(margin: const EdgeInsets.only(bottom: 20)), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + text(heading, 17, TextType.Bold, color: colorPrimaryDark), + if (caption.isNotEmpty) ...[ + const SizedBox(width: 10), + Padding( + padding: const EdgeInsets.only(bottom: 2), + child: text(caption, 11, TextType.Regular, + color: colorGrey2), + ), + ], + ], + ), + ), + if (trailing != null) trailing, + ], + ), + const SizedBox(height: 16), + ], + ); +} diff --git a/frontend/lib/Grounded/designs/buttons/Buttons.dart b/frontend/lib/Grounded/designs/buttons/Buttons.dart new file mode 100644 index 0000000..e5360c9 --- /dev/null +++ b/frontend/lib/Grounded/designs/buttons/Buttons.dart @@ -0,0 +1,178 @@ +import 'package:flutter/material.dart'; + +import '../../about/internal/application/TextType.dart'; +import '../../utils/Colors.dart'; +import '../Component.dart'; +import '../text/Text.dart'; + +/// The primary action. One per screen — if a screen appears to need two, one +/// of them is secondary. +Widget roundedCornerButton( + String label, + VoidCallback onPressed, { + Color? background, + Color? foreground, + IconData? icon, + bool enabled = true, + double radius = 14, + double verticalPadding = 16, +}) { + final Color bg = enabled ? (background ?? colorPrimaryDark) : colorGrey; + final Color fg = foreground ?? colorWhite; + + return ElevatedButton( + onPressed: enabled ? onPressed : null, + style: ElevatedButton.styleFrom( + elevation: 0, + backgroundColor: bg, + disabledBackgroundColor: colorGrey.withValues(alpha: 0.4), + padding: EdgeInsets.symmetric(vertical: verticalPadding), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(radius), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (icon != null) ...[ + Icon(icon, size: 16, color: fg), + const SizedBox(width: 8), + ], + text(label, 14, TextType.Bold, color: fg), + ], + ), + ); +} + +/// The secondary action — outlined, never filled, so the hierarchy is never +/// ambiguous. +Widget outlinedActionButton( + String label, + VoidCallback onPressed, { + Color? foreground, + IconData? icon, + bool enabled = true, + double radius = 14, +}) { + final Color fg = enabled ? (foreground ?? colorPrimaryDark) : colorGrey; + + return OutlinedButton( + onPressed: enabled ? onPressed : null, + style: OutlinedButton.styleFrom( + side: BorderSide(color: fg.withValues(alpha: 0.35), width: 1), + padding: const EdgeInsets.symmetric(vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(radius), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (icon != null) ...[ + Icon(icon, size: 16, color: fg), + const SizedBox(width: 8), + ], + text(label, 14, TextType.Bold, color: fg), + ], + ), + ); +} + +Widget textButton( + String label, + VoidCallback onPressed, { + Color? color, + double textSize = 13, + TextType type = TextType.Regular, +}) { + return TextButton( + onPressed: onPressed, + child: text(label, textSize, type, color: color ?? colorGrey2), + ); +} + +Widget iconButton( + Widget icon, + VoidCallback onPressed, { + bool bordered = false, + Color? borderColor, + Color? background, + double radius = 10, + double size = 38, +}) { + return GestureDetector( + onTap: onPressed, + child: Container( + width: size, + height: size, + alignment: Alignment.center, + decoration: BoxDecoration( + color: background ?? Colors.transparent, + borderRadius: BorderRadius.circular(radius), + border: bordered + ? Border.all(color: borderColor ?? colorBorder, width: 1) + : null, + ), + child: icon, + ), + ); +} + +/// The destructive action — abandoning, which costs the most debt of all and +/// so is always visually distinct from completing. +Widget destructiveButton( + String label, + VoidCallback onPressed, { + IconData? icon, + bool enabled = true, +}) { + return roundedCornerButton( + label, + onPressed, + background: colorDestructive, + foreground: colorWhite, + icon: icon, + enabled: enabled, + ); +} + +/// A segmented selector, used for class, energy, proof type and tone. +Widget segmentedSelector({ + required List options, + required T selected, + required String Function(T) label, + required void Function(T) onSelected, + Color? activeColor, +}) { + final Color active = activeColor ?? colorPrimaryDark; + + return Wrap( + spacing: 8, + runSpacing: 8, + children: options.map((option) { + final bool isSelected = option == selected; + return GestureDetector( + onTap: () => onSelected(option), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: isSelected ? active : colorWhite, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: isSelected ? active : colorBorder, + width: 1, + ), + ), + child: text( + label(option), + 12, + isSelected ? TextType.Bold : TextType.Regular, + color: isSelected ? colorWhite : colorGrey2, + ), + ), + ); + }).toList(), + ); +} diff --git a/frontend/lib/Grounded/designs/input/InputFields.dart b/frontend/lib/Grounded/designs/input/InputFields.dart new file mode 100644 index 0000000..3602f7e --- /dev/null +++ b/frontend/lib/Grounded/designs/input/InputFields.dart @@ -0,0 +1,206 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../about/internal/application/TextType.dart'; +import '../../utils/Colors.dart'; +import '../Component.dart'; +import '../text/Text.dart'; + +/// The standard text field. +Widget inputField( + String label, + TextEditingController controller, { + String hint = "", + String? Function(String?)? validator, + TextInputType keyboard = TextInputType.text, + bool obscure = false, + int maxLines = 1, + List? formatters, + IconData? icon, + ValueChanged? onChanged, +}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text(label.toUpperCase(), 10, TextType.Bold, + color: colorGrey2, letterSpacing: 0.8), + const SizedBox(height: 8), + TextFormField( + controller: controller, + validator: validator, + keyboardType: keyboard, + obscureText: obscure, + maxLines: obscure ? 1 : maxLines, + inputFormatters: formatters, + onChanged: onChanged, + style: TextStyle( + fontFamily: getTextType(TextType.Regular), + fontSize: 14, + color: colorPrimaryDark, + ), + decoration: InputDecoration( + hintText: hint, + hintStyle: TextStyle( + fontFamily: getTextType(TextType.Regular), + fontSize: 13, + color: colorGrey, + ), + prefixIcon: icon == null + ? null + : Icon(icon, size: 18, color: colorGrey2), + filled: true, + fillColor: colorWhite, + contentPadding: + const EdgeInsets.symmetric(horizontal: 14, vertical: 14), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: colorBorder, width: 1), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: colorPrimaryDark, width: 1.4), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: colorNegative, width: 1), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: colorNegative, width: 1.4), + ), + errorStyle: TextStyle( + fontFamily: getTextType(TextType.Regular), + fontSize: 11, + color: colorNegative, + ), + ), + ), + ], + ); +} + +/// The excuse field. Deliberately unadorned — no templates, no quick-picks, a +/// live character count that shows the minimum, because the friction is the +/// feature rather than an obstacle to route around. +Widget excuseField( + TextEditingController controller, + int minimumLength, { + String? Function(String?)? validator, + ValueChanged? onChanged, +}) { + final int length = controller.text.trim().length; + final bool satisfied = length >= minimumLength; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + text("WHY", 10, TextType.Bold, color: colorGrey2, letterSpacing: 0.8), + text( + satisfied ? "$length characters" : "$length / $minimumLength", + 10, + TextType.Bold, + color: satisfied ? colorPositive : colorGrey, + ), + ], + ), + const SizedBox(height: 8), + TextFormField( + controller: controller, + validator: validator, + onChanged: onChanged, + maxLines: 4, + style: TextStyle( + fontFamily: getTextType(TextType.Regular), + fontSize: 14, + color: colorPrimaryDark, + height: 1.5, + ), + decoration: InputDecoration( + hintText: "In your own words. No shortcuts here.", + hintStyle: TextStyle( + fontFamily: getTextType(TextType.Regular), + fontSize: 13, + color: colorGrey, + ), + filled: true, + fillColor: colorWhite, + contentPadding: const EdgeInsets.all(14), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: colorBorder, width: 1), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: colorPrimaryDark, width: 1.4), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: colorNegative, width: 1), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: colorNegative, width: 1.4), + ), + errorStyle: TextStyle( + fontFamily: getTextType(TextType.Regular), + fontSize: 11, + color: colorNegative, + ), + ), + ), + ], + ); +} + +/// A read-only field that loads its options on tap rather than pre-loading +/// them — the ViewModel fetches, then calls back to open the picker. +Widget selectField( + String label, + String value, + VoidCallback onTap, { + String hint = "Select", + IconData icon = Icons.expand_more_rounded, +}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text(label.toUpperCase(), 10, TextType.Bold, + color: colorGrey2, letterSpacing: 0.8), + const SizedBox(height: 8), + GestureDetector( + onTap: onTap, + child: Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 16), + decoration: BoxDecoration( + color: colorWhite, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: colorBorder, width: 1), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: text( + value.isEmpty ? hint : value, + 14, + value.isEmpty ? TextType.Regular : TextType.Bold, + color: value.isEmpty ? colorGrey : colorPrimaryDark, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + Icon(icon, size: 20, color: colorGrey2), + ], + ), + ), + ), + ], + ); +} diff --git a/frontend/lib/Grounded/designs/text/Text.dart b/frontend/lib/Grounded/designs/text/Text.dart new file mode 100644 index 0000000..017fd0c --- /dev/null +++ b/frontend/lib/Grounded/designs/text/Text.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +import '../../about/internal/application/TextType.dart'; +import '../../utils/Colors.dart'; +import '../Component.dart'; + +/// The only way display copy is rendered. Raw Text/TextStyle is never used for +/// user-facing copy. +Widget text( + String text, + double textSize, + TextType type, { + Color? color, + TextAlign? align, + int? maxLines, + TextOverflow? overflow, + double? letterSpacing, + double? height, + double? wordSpacing, + TextDecoration? decoration, + FontWeight? weight, +}) { + return Text( + text, + textAlign: align ?? TextAlign.left, + maxLines: maxLines, + overflow: overflow, + style: TextStyle( + decoration: decoration ?? TextDecoration.none, + color: color ?? colorPrimaryDark, + fontSize: textSize, + fontFamily: getTextType(type), + letterSpacing: letterSpacing, + height: height, + wordSpacing: wordSpacing, + fontWeight: weight, + ), + ); +} diff --git a/frontend/lib/Grounded/informatics/AppDataManager.dart b/frontend/lib/Grounded/informatics/AppDataManager.dart new file mode 100644 index 0000000..fdc9064 --- /dev/null +++ b/frontend/lib/Grounded/informatics/AppDataManager.dart @@ -0,0 +1,362 @@ +import 'dart:typed_data'; + +import 'package:dio/dio.dart'; + +import '../about/external/data/Commitment.dart'; +import '../about/external/data/Program.dart'; +import '../about/external/data/SystemResponse.dart'; +import '../about/external/data/pages/request/CommitmentsRequest.dart'; +import '../about/external/data/pages/request/HistoryRequest.dart'; +import '../about/external/initial/AbandonRequest.dart'; +import '../about/external/initial/AmnestyRequest.dart'; +import '../about/external/initial/CommitmentRequest.dart'; +import '../about/external/initial/CompletionRequest.dart'; +import '../about/external/initial/DeferralRequest.dart'; +import '../about/external/initial/DeviceRequest.dart'; +import '../about/external/initial/GoalRequest.dart'; +import '../about/external/initial/IdRequest.dart'; +import '../about/external/initial/LoginData.dart'; +import '../about/external/initial/ReportCardRequest.dart'; +import '../about/external/initial/SessionLogRequest.dart'; +import '../about/external/initial/SickModeRequest.dart'; +import '../about/external/initial/ToneRequest.dart'; +import '../about/internal/application/CapacityProfile.dart'; +import '../about/internal/application/MeDescription.dart'; +import '../about/internal/application/Token.dart'; +import '../about/internal/application/UserDetails.dart'; +import '../about/internal/file/ConnectFileStorage.dart'; +import '../comms/ConnectComms.dart'; +import '../memory/ConnectInternalMemory.dart'; +import 'DataManager.dart'; + +/// The single data gateway. Constructed once in [ParentViewModel] and shared by +/// every screen; it only delegates to its three collaborators. +class AppDataManager implements DataManager { + ConnectInternalMemory memory; + + ConnectComms comms; + + ConnectFileStorage files; + + AppDataManager(this.memory, this.comms, this.files); + + // ── Memory ──────────────────────────────────────────────────────────────── + + @override + Future getMyDescription() => memory.getMyDescription(); + + @override + Future setMyDescription(MeDescription description) => + memory.setMyDescription(description); + + @override + Future getNotToken() => memory.getNotToken(); + + @override + Future setNotToken(String token) => memory.setNotToken(token); + + @override + Future getUserCreationDetails() => + memory.getUserCreationDetails(); + + @override + Future setUserCreationDetails(SystemResponse response) => + memory.setUserCreationDetails(response); + + @override + Future getUserDetails() => memory.getUserDetails(); + + @override + Future setUserDetails(UserDetails details) => memory.setUserDetails(details); + + @override + Future getTokenEntry() => memory.getTokenEntry(); + + @override + Future setTokenEntry(Token token) => memory.setTokenEntry(token); + + @override + Future getRefreshAt() => memory.getRefreshAt(); + + @override + Future setRefreshAt(String refresher) => memory.setRefreshAt(refresher); + + @override + Future getActiveCommitment() => memory.getActiveCommitment(); + + @override + Future setActiveCommitment(Commitment commitment) => + memory.setActiveCommitment(commitment); + + @override + Future getActiveProgram() => memory.getActiveProgram(); + + @override + Future setActiveProgram(Program program) => memory.setActiveProgram(program); + + @override + Future getCapacityProfile() => memory.getCapacityProfile(); + + @override + Future setCapacityProfile(CapacityProfile profile) => + memory.setCapacityProfile(profile); + + @override + Future getCachedDebtScore() => memory.getCachedDebtScore(); + + @override + Future setCachedDebtScore(double score) => memory.setCachedDebtScore(score); + + @override + Future getEngagementCount() => memory.getEngagementCount(); + + @override + Future setEngagementCount(int count) => memory.setEngagementCount(count); + + @override + Future getAmnestySpent() => memory.getAmnestySpent(); + + @override + Future setAmnestySpent(int spent) => memory.setAmnestySpent(spent); + + @override + Future showOnboarding() => memory.showOnboarding(); + + @override + Future setOnboardingOption(bool value) => memory.setOnboardingOption(value); + + // ── Files ───────────────────────────────────────────────────────────────── + + @override + Future saveProof(String name, Uint8List bytes) => + files.saveProof(name, bytes); + + @override + Future readProof(String reference) => files.readProof(reference); + + @override + Future deleteProof(String reference) => files.deleteProof(reference); + + @override + Future proofDirectory() => files.proofDirectory(); + + // ── Comms ───────────────────────────────────────────────────────────────── + + @override + Future registerDevice(DeviceRequest request) => + comms.registerDevice(request); + + @override + Future registerDeviceToken(String token) => + comms.registerDeviceToken(token); + + @override + Future login(LoginData request) => comms.login(request); + + @override + Future logout() => comms.logout(); + + @override + Future me() => comms.me(); + + @override + Future getMyGoals(HistoryRequest request) => + comms.getMyGoals(request); + + @override + Future saveGoalEntry(GoalRequest request) => + comms.saveGoalEntry(request); + + @override + Future getGoalTasks(IdRequest request) => + comms.getGoalTasks(request); + + @override + Future archiveGoalEntry(IdRequest request) => + comms.archiveGoalEntry(request); + + @override + Future getTodayPlan(CommitmentsRequest request) => + comms.getTodayPlan(request); + + @override + Future getMyCommitments(CommitmentsRequest request) => + comms.getMyCommitments(request); + + @override + Future getOverdueQueue(HistoryRequest request) => + comms.getOverdueQueue(request); + + @override + Future saveCommitmentEntry(CommitmentRequest request) => + comms.saveCommitmentEntry(request); + + @override + Future updateCommitmentEntry(CommitmentRequest request) => + comms.updateCommitmentEntry(request); + + @override + Future deleteCommitmentEntry(IdRequest request) => + comms.deleteCommitmentEntry(request); + + @override + Future completeCommitmentEntry(CompletionRequest request) => + comms.completeCommitmentEntry(request); + + @override + Future deferCommitmentEntry(DeferralRequest request) => + comms.deferCommitmentEntry(request); + + @override + Future abandonCommitmentEntry(AbandonRequest request) => + comms.abandonCommitmentEntry(request); + + @override + Future getCommitmentHistory(HistoryRequest request) => + comms.getCommitmentHistory(request); + + @override + Future getCommitmentEvents(IdRequest request) => + comms.getCommitmentEvents(request); + + @override + Future checkCapacity(CommitmentsRequest request) => + comms.checkCapacity(request); + + @override + Future getCapacityProfileEntry() => comms.getCapacityProfileEntry(); + + @override + Future getDebtSummary() => comms.getDebtSummary(); + + @override + Future getDebtLedger(HistoryRequest request) => + comms.getDebtLedger(request); + + @override + Future getDebtTrend(ReportCardRequest request) => + comms.getDebtTrend(request); + + @override + Future getStanding() => comms.getStanding(); + + @override + Future getStandingHistory(HistoryRequest request) => + comms.getStandingHistory(request); + + @override + Future getExcuseClusters(ReportCardRequest request) => + comms.getExcuseClusters(request); + + @override + Future uploadPhotoProof(FormData request) => + comms.uploadPhotoProof(request); + + @override + Future submitTimerProofEntry(CompletionRequest request) => + comms.submitTimerProofEntry(request); + + @override + Future submitLocationProofEntry(CompletionRequest request) => + comms.submitLocationProofEntry(request); + + @override + Future spendAmnestyToken(AmnestyRequest request) => + comms.spendAmnestyToken(request); + + @override + Future getAmnestyBalance() => comms.getAmnestyBalance(); + + @override + Future updateSickMode(SickModeRequest request) => + comms.updateSickMode(request); + + @override + Future checkDistress() => comms.checkDistress(); + + @override + Future updateTone(ToneRequest request) => comms.updateTone(request); + + @override + Future getMyHabits(HistoryRequest request) => + comms.getMyHabits(request); + + @override + Future saveHabitEntry(Map request) => + comms.saveHabitEntry(request); + + @override + Future logHabitEntry(IdRequest request) => + comms.logHabitEntry(request); + + @override + Future getKeystoneHabits() => comms.getKeystoneHabits(); + + @override + Future getMyRoutines(HistoryRequest request) => + comms.getMyRoutines(request); + + @override + Future logRoutineChainEntry(Map request) => + comms.logRoutineChainEntry(request); + + @override + Future getMyPrograms(HistoryRequest request) => + comms.getMyPrograms(request); + + @override + Future saveProgramEntry(Map request) => + comms.saveProgramEntry(request); + + @override + Future activateProgramEntry(IdRequest request) => + comms.activateProgramEntry(request); + + @override + Future getProgramSessions(IdRequest request) => + comms.getProgramSessions(request); + + @override + Future startSessionEntry(IdRequest request) => + comms.startSessionEntry(request); + + @override + Future saveSessionLogEntry(SessionLogRequest request) => + comms.saveSessionLogEntry(request); + + @override + Future getSessionHistory(HistoryRequest request) => + comms.getSessionHistory(request); + + @override + Future getLastSessionFor(IdRequest request) => + comms.getLastSessionFor(request); + + @override + Future getWeeklyVolume(ReportCardRequest request) => + comms.getWeeklyVolume(request); + + @override + Future getContactVolume(ReportCardRequest request) => + comms.getContactVolume(request); + + @override + Future getPersonalRecords(HistoryRequest request) => + comms.getPersonalRecords(request); + + @override + Future getWeeklyReportCard(ReportCardRequest request) => + comms.getWeeklyReportCard(request); + + @override + Future getMonthlyReportCard(ReportCardRequest request) => + comms.getMonthlyReportCard(request); + + @override + Future getMyNotifications(HistoryRequest request) => + comms.getMyNotifications(request); + + @override + Future acknowledgeNudgeEntry(IdRequest request) => + comms.acknowledgeNudgeEntry(request); +} diff --git a/frontend/lib/Grounded/informatics/DataManager.dart b/frontend/lib/Grounded/informatics/DataManager.dart new file mode 100644 index 0000000..4216097 --- /dev/null +++ b/frontend/lib/Grounded/informatics/DataManager.dart @@ -0,0 +1,6 @@ +import '../about/internal/file/ConnectFileStorage.dart'; +import '../comms/ConnectComms.dart'; +import '../memory/ConnectInternalMemory.dart'; + +abstract class DataManager + implements ConnectInternalMemory, ConnectComms, ConnectFileStorage {} diff --git a/frontend/lib/Grounded/memory/ConnectInternalMemory.dart b/frontend/lib/Grounded/memory/ConnectInternalMemory.dart new file mode 100644 index 0000000..6428e4d --- /dev/null +++ b/frontend/lib/Grounded/memory/ConnectInternalMemory.dart @@ -0,0 +1,65 @@ +import '../about/external/data/Commitment.dart'; +import '../about/external/data/Program.dart'; +import '../about/external/data/SystemResponse.dart'; +import '../about/internal/application/CapacityProfile.dart'; +import '../about/internal/application/MeDescription.dart'; +import '../about/internal/application/Token.dart'; +import '../about/internal/application/UserDetails.dart'; + +abstract class ConnectInternalMemory { + Future getMyDescription(); + + Future setMyDescription(MeDescription description); + + Future getNotToken(); + + Future setNotToken(String token); + + Future getUserCreationDetails(); + + Future setUserCreationDetails(SystemResponse response); + + Future getUserDetails(); + + Future setUserDetails(UserDetails details); + + Future getTokenEntry(); + + Future setTokenEntry(Token token); + + Future getRefreshAt(); + + Future setRefreshAt(String refresher); + + Future getActiveCommitment(); + + Future setActiveCommitment(Commitment commitment); + + Future getActiveProgram(); + + Future setActiveProgram(Program program); + + Future getCapacityProfile(); + + Future setCapacityProfile(CapacityProfile profile); + + /// The last computed debt score, so the app can open with a number rather + /// than a spinner. + Future getCachedDebtScore(); + + Future setCachedDebtScore(double score); + + /// App opens this week, for the distress conjunction. + Future getEngagementCount(); + + Future setEngagementCount(int count); + + /// Amnesty tokens spent this month. + Future getAmnestySpent(); + + Future setAmnestySpent(int spent); + + Future showOnboarding(); + + Future setOnboardingOption(bool value); +} diff --git a/frontend/lib/Grounded/memory/InternalMemory.dart b/frontend/lib/Grounded/memory/InternalMemory.dart new file mode 100644 index 0000000..67d3d39 --- /dev/null +++ b/frontend/lib/Grounded/memory/InternalMemory.dart @@ -0,0 +1,270 @@ +import 'dart:convert'; + +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +import '../about/external/data/Commitment.dart'; +import '../about/external/data/Program.dart'; +import '../about/external/data/ResponseState.dart'; +import '../about/external/data/SystemResponse.dart'; +import '../about/internal/application/CapacityProfile.dart'; +import '../about/internal/application/MeDescription.dart'; +import '../about/internal/application/Token.dart'; +import '../about/internal/application/UserDetails.dart'; +import 'ConnectInternalMemory.dart'; + +/// Secure local storage. Keys carry a random suffix so they are not guessable +/// from the field name alone. +class InternalMemory implements ConnectInternalMemory { + static const String DESCRIBE_ME = "DESCRIBE_ME_Kq7fRp2XvN4mLd8T"; + + static const String NOT_TOKEN = "NOT_TOKEN_Zw3hYb9CsK6nQe1V"; + + static const String USER_DETAILS_NEW = "USER_DETAILS_NEW_Rt5jXm8PfA2wDc7L"; + + static const String ABOUT_ME = "ABOUT_ME_Hn4vTq7BdS9xGk3M"; + + static const String TOKEN_DETAILS = "TOKEN_DETAILS_Ly6pWc3NrE8zFj5Q"; + + static const String REFRESHER_ID = "REFRESHER_ID_Vb2sJd7MtX4qHu9K"; + + static const String ACTIVE_COMMITMENT = + "ACTIVE_COMMITMENT_Pf9kRn3WgY6tBz2S"; + + static const String ACTIVE_PROGRAM = "ACTIVE_PROGRAM_Dm5cQx8LvH1rTk4N"; + + static const String CAPACITY_PROFILE = "CAPACITY_PROFILE_Gs7bZp4JnW9dFy6X"; + + static const String DEBT_SCORE = "DEBT_SCORE_Ux3mKt6QcR8vNa5H"; + + static const String ENGAGEMENT_COUNT = "ENGAGEMENT_COUNT_Ct8nDw2FbP5jSq7Z"; + + static const String AMNESTY_SPENT = "AMNESTY_SPENT_Jr4xVh9KmT3gLc6B"; + + static const String SHOW_ONBOARDING = "SHOW_ONBOARDING_Nz6qGf2SdX7wPb4M"; + + final groundedStorage = const FlutterSecureStorage(); + + @override + Future getMyDescription() async { + String? value = await groundedStorage.read(key: DESCRIBE_ME); + + if (value != null) { + if (value != "") { + Map json = jsonDecode(value); + return MeDescription.fromJson(json); + } + } + + return MeDescription(id: "", name: "", token: ""); + } + + @override + Future setMyDescription(MeDescription description) async { + String value = jsonEncode(description.toJson()); + await groundedStorage.write(key: DESCRIBE_ME, value: value); + } + + @override + Future getNotToken() async { + return await groundedStorage.read(key: NOT_TOKEN) ?? ""; + } + + @override + Future setNotToken(String token) async { + await groundedStorage.write(key: NOT_TOKEN, value: token); + } + + @override + Future getUserCreationDetails() async { + String? value = await groundedStorage.read(key: USER_DETAILS_NEW); + + if (value != null) { + if (value != "") { + Map json = jsonDecode(value); + return SystemResponse.fromJsonMap(json); + } + } + + return SystemResponse("", "", "", ResponseState.Success); + } + + @override + Future setUserCreationDetails(SystemResponse response) async { + String value = jsonEncode(response.toJson()); + await groundedStorage.write(key: USER_DETAILS_NEW, value: value); + } + + @override + Future getUserDetails() async { + String? value = await groundedStorage.read(key: ABOUT_ME); + + if (value != null) { + if (value != "") { + Map json = jsonDecode(value); + return UserDetails.fromJson(json); + } + } + + return UserDetails(pic: '', name: ''); + } + + @override + Future setUserDetails(UserDetails details) async { + String value = jsonEncode(details.toJson()); + await groundedStorage.write(key: ABOUT_ME, value: value); + } + + @override + Future getTokenEntry() async { + String? value = await groundedStorage.read(key: TOKEN_DETAILS); + + if (value != null) { + if (value != "") { + Map json = jsonDecode(value); + return Token.fromJsonMap(json); + } + } + + return Token("", "", "", 0, ""); + } + + @override + Future setTokenEntry(Token token) async { + String value = jsonEncode(token.toJson()); + await groundedStorage.write(key: TOKEN_DETAILS, value: value); + } + + @override + Future getRefreshAt() async { + return await groundedStorage.read(key: REFRESHER_ID) ?? ""; + } + + @override + Future setRefreshAt(String refresher) async { + await groundedStorage.write(key: REFRESHER_ID, value: refresher); + } + + @override + Future getActiveCommitment() async { + String? value = await groundedStorage.read(key: ACTIVE_COMMITMENT); + + if (value != null) { + if (value.isNotEmpty) { + Map json = jsonDecode(value); + return Commitment.fromJson(json); + } + } + + return Commitment(); + } + + @override + Future setActiveCommitment(Commitment commitment) async { + String value = jsonEncode(commitment.toJson()); + await groundedStorage.write(key: ACTIVE_COMMITMENT, value: value); + } + + @override + Future getActiveProgram() async { + String? value = await groundedStorage.read(key: ACTIVE_PROGRAM); + + if (value != null) { + if (value.isNotEmpty) { + Map json = jsonDecode(value); + return Program.fromJson(json); + } + } + + return Program(); + } + + @override + Future setActiveProgram(Program program) async { + String value = jsonEncode(program.toJson()); + await groundedStorage.write(key: ACTIVE_PROGRAM, value: value); + } + + @override + Future getCapacityProfile() async { + String? value = await groundedStorage.read(key: CAPACITY_PROFILE); + + if (value != null) { + if (value.isNotEmpty) { + Map json = jsonDecode(value); + return CapacityProfile.fromJson(json); + } + } + + return CapacityProfile(); + } + + @override + Future setCapacityProfile(CapacityProfile profile) async { + String value = jsonEncode(profile.toJson()); + await groundedStorage.write(key: CAPACITY_PROFILE, value: value); + } + + @override + Future getCachedDebtScore() async { + String? value = await groundedStorage.read(key: DEBT_SCORE); + + if (value != null) { + return double.tryParse(value) ?? 0; + } + + return 0; + } + + @override + Future setCachedDebtScore(double score) async { + await groundedStorage.write(key: DEBT_SCORE, value: score.toString()); + } + + @override + Future getEngagementCount() async { + String? value = await groundedStorage.read(key: ENGAGEMENT_COUNT); + + if (value != null) { + return int.tryParse(value) ?? 0; + } + + return 0; + } + + @override + Future setEngagementCount(int count) async { + await groundedStorage.write(key: ENGAGEMENT_COUNT, value: count.toString()); + } + + @override + Future getAmnestySpent() async { + String? value = await groundedStorage.read(key: AMNESTY_SPENT); + + if (value != null) { + return int.tryParse(value) ?? 0; + } + + return 0; + } + + @override + Future setAmnestySpent(int spent) async { + await groundedStorage.write(key: AMNESTY_SPENT, value: spent.toString()); + } + + @override + Future showOnboarding() async { + String? value = await groundedStorage.read(key: SHOW_ONBOARDING); + + if (value != null) { + return value.toLowerCase() == 'true'; + } + + return true; + } + + @override + Future setOnboardingOption(bool value) async { + await groundedStorage.write(key: SHOW_ONBOARDING, value: value.toString()); + } +} diff --git a/frontend/lib/Grounded/see/commitment/ConnectNewCommitment.dart b/frontend/lib/Grounded/see/commitment/ConnectNewCommitment.dart new file mode 100644 index 0000000..5051cf6 --- /dev/null +++ b/frontend/lib/Grounded/see/commitment/ConnectNewCommitment.dart @@ -0,0 +1,12 @@ +import '../../utils/CapacityEngine.dart'; + +abstract class ConnectNewCommitment { + void onSaved(); + + /// The plan is over capacity — the save is refused until something is cut. + void onCapacityBlocked(CapacityVerdict verdict); + + /// The learned multiplier for this category, so the estimate field can show + /// what the app actually expects the task to take. + void onMultiplierResolved(double multiplier); +} diff --git a/frontend/lib/Grounded/see/commitment/NewCommitment.dart b/frontend/lib/Grounded/see/commitment/NewCommitment.dart new file mode 100644 index 0000000..69da0c3 --- /dev/null +++ b/frontend/lib/Grounded/see/commitment/NewCommitment.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'NewCommitmentState.dart'; + +class NewCommitment extends StatefulWidget { + const NewCommitment({super.key}); + + @override + State createState() => NewCommitmentState(); +} diff --git a/frontend/lib/Grounded/see/commitment/NewCommitmentState.dart b/frontend/lib/Grounded/see/commitment/NewCommitmentState.dart new file mode 100644 index 0000000..c3cecdc --- /dev/null +++ b/frontend/lib/Grounded/see/commitment/NewCommitmentState.dart @@ -0,0 +1,552 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/external/data/Commitment.dart'; +import '../../about/external/initial/CommitmentRequest.dart'; +import '../../about/internal/application/CommitmentClass.dart'; +import '../../about/internal/application/EnergyCost.dart'; +import '../../about/internal/application/NotificationType.dart'; +import '../../about/internal/application/ProofType.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../designs/Component.dart'; +import '../../designs/Responsive.dart'; +import '../../designs/Shell.dart'; +import '../../designs/buttons/Buttons.dart'; +import '../../designs/input/InputFields.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/CapacityEngine.dart'; +import '../../utils/Colors.dart'; +import '../../utils/CommonUtils.dart'; +import '../../utils/Validators.dart'; +import 'ConnectNewCommitment.dart'; +import 'NewCommitment.dart'; +import 'ViewNewCommitment.dart'; + +class NewCommitmentState extends State + implements ConnectNewCommitment { + ViewNewCommitment? _model; + + final GlobalKey _formKey = GlobalKey(); + + final TextEditingController _title = TextEditingController(); + + final TextEditingController _category = TextEditingController(); + + final TextEditingController _estimate = TextEditingController(); + + CommitmentClass _class = CommitmentClass.Standard; + + EnergyCost _energy = EnergyCost.Medium; + + ProofType _proof = ProofType.Honour; + + DateTime? _windowStart; + + DateTime? _windowEnd; + + double _multiplier = 1.0; + + String _windowError = ""; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewNewCommitment(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ); + } + + void _initiate() { + // A window, not a date: the default opens now and closes in two hours, so + // the field is never left as a bare day. + final DateTime now = DateTime.now(); + setState(() { + _windowStart = now; + _windowEnd = now.add(const Duration(hours: 2)); + }); + } + + // ── Handlers ────────────────────────────────────────────────────────────── + + void _onBack() { + Navigator.pop(context, false); + } + + void _onCategoryChanged(String value) { + _model?.resolveMultiplier(value.trim()); + } + + void _onClassSelected(CommitmentClass value) { + setState(() { + _class = value; + // Non-negotiables carry real consequences, so they default to real proof + // rather than the honour checkbox. + if (value == CommitmentClass.NonNegotiable && + _proof == ProofType.Honour) { + _proof = ProofType.Photo; + } + }); + } + + void _onEnergySelected(EnergyCost value) { + setState(() { + _energy = value; + }); + } + + void _onProofSelected(ProofType value) { + setState(() { + _proof = value; + }); + } + + void _onPickWindowStart() async { + final DateTime? picked = await _pickMoment(_windowStart); + if (picked == null) { + return; + } + + setState(() { + _windowStart = picked; + if (_windowEnd == null || !_windowEnd!.isAfter(picked)) { + _windowEnd = picked.add(const Duration(hours: 2)); + } + _windowError = ""; + }); + } + + void _onPickWindowEnd() async { + final DateTime? picked = await _pickMoment(_windowEnd); + if (picked == null) { + return; + } + + setState(() { + _windowEnd = picked; + _windowError = Validators.window(_windowStart, _windowEnd) ?? ""; + }); + } + + Future _pickMoment(DateTime? initial) async { + final DateTime base = initial ?? DateTime.now(); + + final DateTime? day = await showDatePicker( + context: context, + initialDate: base, + firstDate: DateTime.now().subtract(const Duration(days: 1)), + lastDate: DateTime.now().add(const Duration(days: 365)), + ); + + if (day == null || !mounted) { + return null; + } + + final TimeOfDay? time = await showTimePicker( + context: context, + initialTime: TimeOfDay.fromDateTime(base), + ); + + if (time == null) { + return null; + } + + return DateTime(day.year, day.month, day.day, time.hour, time.minute); + } + + void _onSave() { + final String? windowIssue = Validators.window(_windowStart, _windowEnd); + + if (windowIssue != null) { + setState(() { + _windowError = windowIssue; + }); + return; + } + + if (_formKey.currentState?.validate() != true) { + return; + } + + _model?.save(_buildRequest(), _buildCandidate()); + } + + CommitmentRequest _buildRequest() { + return CommitmentRequest( + commitmentClass: _class.name, + title: _title.text.trim(), + category: _category.text.trim(), + dueStart: _windowStart?.toIso8601String() ?? "", + dueEnd: _windowEnd?.toIso8601String() ?? "", + estMinutes: int.tryParse(_estimate.text.trim()) ?? 0, + energy: _energy.name, + proofType: _proof.name, + proofTimerMinutes: + _proof == ProofType.Timer ? int.tryParse(_estimate.text.trim()) ?? 0 : 0, + ); + } + + Commitment _buildCandidate() { + return Commitment( + commitmentClass: _class, + title: _title.text.trim(), + category: _category.text.trim(), + dueStart: _windowStart, + dueEnd: _windowEnd, + estMinutes: int.tryParse(_estimate.text.trim()) ?? 0, + energy: _energy, + proofType: _proof, + ); + } + + // ── Views ───────────────────────────────────────────────────────────────── + + Widget _mobileView(BoxConstraints constraints) { + final int estimate = int.tryParse(_estimate.text.trim()) ?? 0; + + final bool multiplierWorthShowing = _multiplier > 1.15 && estimate > 0; + + return Sheet( + eyebrow: "New", + title: "Commitment", + onBack: _onBack, + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + displayTitle("What are you\ncommitting to?"), + const SizedBox(height: 28), + inputField( + "Title", + _title, + hint: "Say it the way you would say it out loud", + validator: Validators.title, + ), + const SizedBox(height: 20), + inputField( + "Category", + _category, + hint: "Admin, gym, deep work…", + onChanged: _onCategoryChanged, + ), + const SizedBox(height: 28), + sectionBreak("Class", caption: "decides the weight"), + segmentedSelector( + options: CommitmentClass.values, + selected: _class, + label: classLabel, + onSelected: _onClassSelected, + activeColor: classColor(_class), + ), + const SizedBox(height: 12), + text( + _classDescription(_class), + 12, + TextType.Regular, + color: colorGrey2, + height: 1.5, + ), + const SizedBox(height: 28), + sectionBreak("Window", caption: "not just a day"), + if (_windowError.isNotEmpty) ...[ + text(_windowError, 11, TextType.Regular, color: colorNegative), + const SizedBox(height: 10), + ], + Row( + children: [ + Expanded( + child: selectField( + "Opens", + _windowStart == null ? "" : formatDateTime(_windowStart), + _onPickWindowStart, + icon: CupertinoIcons.calendar, + ), + ), + const SizedBox(width: 10), + Expanded( + child: selectField( + "Closes", + _windowEnd == null ? "" : formatDateTime(_windowEnd), + _onPickWindowEnd, + icon: CupertinoIcons.calendar, + ), + ), + ], + ), + const SizedBox(height: 10), + text( + "When this window closes, the item goes overdue. It does not roll over to tomorrow.", + 12, + TextType.Regular, + color: colorGrey2, + height: 1.5, + ), + const SizedBox(height: 28), + sectionBreak("Effort"), + inputField( + "Estimated minutes", + _estimate, + hint: "How long you think it takes", + validator: Validators.estimateMinutes, + keyboard: TextInputType.number, + onChanged: (value) => setState(() {}), + ), + if (multiplierWorthShowing) ...[ + const SizedBox(height: 12), + card( + background: colorStandingWarnedBg, + borderColor: colorStandingWarned.withValues(alpha: 0.20), + padding: const EdgeInsets.all(14), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(CupertinoIcons.info_circle_fill, + size: 15, color: colorStandingWarned), + const SizedBox(width: 10), + Expanded( + child: text( + "On ${_category.text.trim().isEmpty ? "this category" : _category.text.trim()} you historically take ${_multiplier.toStringAsFixed(1)}× your estimate. Your day is planned against ${formatMinutes(estimate * _multiplier)}.", + 12, + TextType.Regular, + color: colorPrimaryDark, + height: 1.5, + ), + ), + ], + ), + ), + ], + const SizedBox(height: 20), + text("ENERGY COST", 9, TextType.Bold, + color: colorGrey2, letterSpacing: 1.0), + const SizedBox(height: 10), + segmentedSelector( + options: EnergyCost.values, + selected: _energy, + label: (value) => value.name, + onSelected: _onEnergySelected, + ), + const SizedBox(height: 28), + sectionBreak("Proof", caption: "the checkbox is the enemy"), + segmentedSelector( + options: ProofType.values, + selected: _proof, + label: proofLabel, + onSelected: _onProofSelected, + ), + const SizedBox(height: 12), + text( + _proofDescription(_proof), + 12, + TextType.Regular, + color: colorGrey2, + height: 1.5, + ), + const SizedBox(height: 32), + roundedCornerButton( + "Commit", + _onSave, + icon: CupertinoIcons.checkmark, + ), + const SizedBox(height: 10), + Center( + child: text( + "You can change the details later. You cannot change the history.", + 11, + TextType.Regular, + color: colorGrey2, + align: TextAlign.center, + ), + ), + ], + ), + ), + ); + } + + String _classDescription(CommitmentClass value) { + switch (value) { + case CommitmentClass.NonNegotiable: + return "Never deferrable, heaviest debt, hardest escalation. Meds, deadlines, rent."; + case CommitmentClass.Standard: + return "Normal weight, two deferrals, then it is complete or abandon."; + case CommitmentClass.Elective: + return "No debt if you miss it. Auto-archives if it sits untouched."; + } + } + + String _proofDescription(ProofType value) { + switch (value) { + case ProofType.Honour: + return "A plain checkbox. Fine for trivia, worthless for anything you actually lie to yourself about."; + case ProofType.Photo: + return "Camera only, no gallery. Timestamped, and near-duplicate photos get flagged."; + case ProofType.Timer: + return "A foreground session. Backgrounding the app pauses the clock."; + case ProofType.Location: + return "Geofence dwell. Being near it does not count as being there."; + case ProofType.Health: + return "Your health platform confirms the workout happened inside the window."; + case ProofType.Witness: + return "Someone else confirms it. The hardest one to talk your way around."; + } + } + + /// The capacity refusal. It shows the arithmetic rather than just saying no, + /// because the point is to make overcommitment visible. + void _openCapacitySheet(CapacityVerdict verdict) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + barrierColor: colorPrimaryDark.withValues(alpha: 0.6), + builder: (BuildContext sheetContext) { + return Container( + decoration: BoxDecoration( + color: colorSheetBackground, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28), + ), + ), + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 16, 24, 32), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + width: 36, + height: 4, + margin: const EdgeInsets.only(bottom: 20), + decoration: BoxDecoration( + color: colorGrey.withValues(alpha: 0.35), + borderRadius: BorderRadius.circular(999), + ), + ), + ), + text("OVER CAPACITY", 9, TextType.Bold, + color: colorStandingWarned, letterSpacing: 1.2), + const SizedBox(height: 10), + text("This day is already full.", 26, TextType.Light, + color: colorPrimaryDark, height: 1.2), + const SizedBox(height: 14), + text(verdict.message, 14, TextType.Regular, + color: colorGrey2, height: 1.55), + const SizedBox(height: 24), + card( + child: Row( + children: [ + Expanded( + child: labelled( + "Planned", + formatMinutes(verdict.projectedMinutes), + valueSize: 20, + valueType: TextType.Light, + ), + ), + Expanded( + child: labelled( + "You do", + formatMinutes(verdict.historicalMinutes), + valueSize: 20, + valueType: TextType.Light, + ), + ), + Expanded( + child: labelled( + "Cut", + formatMinutes(verdict.excessMinutes), + valueSize: 20, + valueType: TextType.Light, + valueColor: colorStandingGrounded, + ), + ), + ], + ), + ), + const SizedBox(height: 20), + text( + "Chronic overdue is usually an overcommitment problem wearing a laziness costume. Cutting something now is the cheapest fix available.", + 12, + TextType.Regular, + color: colorGrey2, + height: 1.5, + ), + const SizedBox(height: 24), + roundedCornerButton( + "Let me cut something", + () => Navigator.pop(sheetContext), + icon: CupertinoIcons.scissors, + ), + const SizedBox(height: 8), + Center( + child: textButton( + "Add it anyway", + () { + Navigator.pop(sheetContext); + _model?.saveAnyway(_buildRequest()); + }, + textSize: 12, + color: colorGrey2, + ), + ), + ], + ), + ), + ); + }, + ); + } + + // ── ConnectNewCommitment ────────────────────────────────────────────────── + + @override + void onSaved() { + _model?.showApplicationNotification( + NotificationType.success, + "Committed", + "It is on the record now. The window closes ${formatDateTime(_windowEnd)}.", + true, + true, + () { + Navigator.pop(context, true); + }, + ); + } + + @override + void onCapacityBlocked(CapacityVerdict verdict) { + _openCapacitySheet(verdict); + } + + @override + void onMultiplierResolved(double multiplier) { + setState(() { + _multiplier = multiplier; + }); + } + + @override + void dispose() { + _title.dispose(); + _category.dispose(); + _estimate.dispose(); + super.dispose(); + } +} diff --git a/frontend/lib/Grounded/see/commitment/ViewNewCommitment.dart b/frontend/lib/Grounded/see/commitment/ViewNewCommitment.dart new file mode 100644 index 0000000..4e97324 --- /dev/null +++ b/frontend/lib/Grounded/see/commitment/ViewNewCommitment.dart @@ -0,0 +1,93 @@ +import '../../about/external/data/Commitment.dart'; +import '../../about/external/data/pages/request/CommitmentsRequest.dart'; +import '../../about/external/data/pages/request/PageAndSort.dart'; +import '../../about/external/data/pages/request/Pageable.dart'; +import '../../about/external/data/pages/request/Sort.dart'; +import '../../about/external/data/pages/response/CommitmentPage.dart'; +import '../../about/external/initial/CommitmentRequest.dart'; +import '../../about/internal/application/CapacityProfile.dart'; +import '../../utils/CapacityEngine.dart'; +import '../parent/ParentViewModel.dart'; +import 'ConnectNewCommitment.dart'; + +class ViewNewCommitment extends ParentViewModel { + ConnectNewCommitment connection; + + ViewNewCommitment(super.context, this.connection); + + /// Surfaces the learned multiplier for the category so the estimate field + /// can show what the app actually expects, rather than silently overriding. + void resolveMultiplier(String category) async { + final CapacityProfile profile = + await getDataManager().getCapacityProfile(); + + connection.onMultiplierResolved(profile.multiplierFor(category)); + } + + /// The capacity gate. Chronic overdue is usually overcommitment misdiagnosed + /// as laziness, so the plan is checked against what history says actually + /// gets done before anything is accepted. + void save(CommitmentRequest request, Commitment candidate) async { + if (!await hasNetwork(() => save(request, candidate))) return; + + showLoading("Checking your day"); + + try { + final DateTime day = candidate.dueStart ?? DateTime.now(); + + final response = await getDataManager().getTodayPlan(CommitmentsRequest( + day: day.toIso8601String(), + query: PageAndSort( + sort: Sort('asc', 'dueStart'), + page: Pageable(0, 0, 100, 0), + ), + )); + + final CommitmentPage page = CommitmentPage.fromJson(response.data); + + final CapacityProfile profile = + await getDataManager().getCapacityProfile(); + + final List proposed = [ + ...page.content, + candidate, + ]; + + final CapacityVerdict verdict = + CapacityEngine.check(proposed, profile, day.weekday); + + if (verdict.blocked) { + closeLoading(); + connection.onCapacityBlocked(verdict); + return; + } + + await getDataManager().saveCommitmentEntry(request); + + closeLoading(); + + connection.onSaved(); + } catch (e) { + handleError( + e, () => save(request, candidate), () => dismissError(), "Retry"); + } + } + + /// Saving past a capacity block, which is only reachable after the user has + /// seen exactly how much they are over by. + void saveAnyway(CommitmentRequest request) async { + if (!await hasNetwork(() => saveAnyway(request))) return; + + showLoading("Saving"); + + try { + await getDataManager().saveCommitmentEntry(request); + + closeLoading(); + + connection.onSaved(); + } catch (e) { + handleError(e, () => saveAnyway(request), () => dismissError(), "Retry"); + } + } +} diff --git a/frontend/lib/Grounded/see/excuse/ConnectExcuseReport.dart b/frontend/lib/Grounded/see/excuse/ConnectExcuseReport.dart new file mode 100644 index 0000000..782e22d --- /dev/null +++ b/frontend/lib/Grounded/see/excuse/ConnectExcuseReport.dart @@ -0,0 +1,5 @@ +import '../../about/external/data/ExcuseCluster.dart'; + +abstract class ConnectExcuseReport { + void onClustersLoaded(List clusters); +} diff --git a/frontend/lib/Grounded/see/excuse/ExcuseReport.dart b/frontend/lib/Grounded/see/excuse/ExcuseReport.dart new file mode 100644 index 0000000..50ca55e --- /dev/null +++ b/frontend/lib/Grounded/see/excuse/ExcuseReport.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'ExcuseReportState.dart'; + +class ExcuseReport extends StatefulWidget { + const ExcuseReport({super.key}); + + @override + State createState() => ExcuseReportState(); +} diff --git a/frontend/lib/Grounded/see/excuse/ExcuseReportState.dart b/frontend/lib/Grounded/see/excuse/ExcuseReportState.dart new file mode 100644 index 0000000..5e54cd1 --- /dev/null +++ b/frontend/lib/Grounded/see/excuse/ExcuseReportState.dart @@ -0,0 +1,216 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/external/data/ExcuseCluster.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../designs/Component.dart'; +import '../../designs/Responsive.dart'; +import '../../designs/Shell.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/Colors.dart'; +import '../../utils/CommonUtils.dart'; +import 'ConnectExcuseReport.dart'; +import 'ExcuseReport.dart'; +import 'ViewExcuseReport.dart'; + +class ExcuseReportState extends State + implements ConnectExcuseReport { + ViewExcuseReport? _model; + + List _clusters = []; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewExcuseReport(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ); + } + + void _initiate() { + _model?.loadClusters(); + } + + void _onBack() { + Navigator.pop(context); + } + + Widget _mobileView(BoxConstraints constraints) { + final int total = _clusters.fold(0, (sum, item) => sum + item.occurrences); + + return Sheet( + eyebrow: "Last 30 days", + title: "Excuses", + onBack: _onBack, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + displayTitle("What you tell\nyourself."), + const SizedBox(height: 14), + text( + "Every deferral you wrote, grouped. Read the concentrations rather than the totals — that is where the pattern is.", + 14, + TextType.Regular, + color: colorGrey2, + height: 1.55, + ), + const SizedBox(height: 28), + if (_clusters.isEmpty) + emptyState( + CupertinoIcons.text_quote, + "Nothing to confront yet", + "Excuses appear here once you have deferred a few things. There is no shame in an empty page.", + accent: colorPositive, + ) + else ...[ + card( + background: colorPrimaryDark, + borderColor: colorPrimaryDark, + child: Row( + children: [ + Expanded( + child: labelled( + "Total excuses", + "$total", + valueSize: 30, + valueType: TextType.Light, + valueColor: colorWhite, + labelColor: colorWhite.withValues(alpha: 0.45), + ), + ), + Expanded( + child: labelled( + "Distinct kinds", + "${_clusters.length}", + valueSize: 30, + valueType: TextType.Light, + valueColor: colorWhite, + labelColor: colorWhite.withValues(alpha: 0.45), + ), + ), + ], + ), + ), + const SizedBox(height: 24), + sectionBreak("The taxonomy", caption: "most frequent first"), + ..._clusters.map(_clusterCard), + ], + ], + ), + ); + } + + Widget _clusterCard(ExcuseCluster cluster) { + final MapEntry? peakDay = _peak(cluster.byWeekday); + final MapEntry? peakHour = _peak(cluster.byHour); + + return Container( + margin: const EdgeInsets.only(bottom: 12), + child: card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: text(cluster.label, 19, TextType.Light, + color: colorPrimaryDark), + ), + pill("${cluster.occurrences}×", colorPrimaryDark, colorMuted, + textSize: 10), + ], + ), + if (cluster.insight.isNotEmpty) ...[ + const SizedBox(height: 14), + Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: colorInset, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: colorBorder, width: 1), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(CupertinoIcons.quote_bubble_fill, + size: 14, color: colorGrey), + const SizedBox(width: 10), + Expanded( + child: text(cluster.insight, 13, TextType.Regular, + color: colorPrimaryDark, height: 1.55), + ), + ], + ), + ), + ], + hairline(margin: const EdgeInsets.symmetric(vertical: 16)), + Row( + children: [ + Expanded( + child: labelled( + "Worst day", + peakDay == null ? "—" : weekdayName(peakDay.key), + valueSize: 13, + ), + ), + Expanded( + child: labelled( + "Worst hour", + peakHour == null ? "—" : hourLabel(peakHour.key), + valueSize: 13, + ), + ), + Expanded( + child: labelled( + "Category", + cluster.dominantCategory.isEmpty + ? "—" + : cluster.dominantCategory, + valueSize: 13, + ), + ), + ], + ), + ], + ), + ), + ); + } + + MapEntry? _peak(Map histogram) { + if (histogram.isEmpty) { + return null; + } + + MapEntry? peak; + for (MapEntry entry in histogram.entries) { + if (peak == null || entry.value > peak.value) { + peak = entry; + } + } + return peak; + } + + @override + void onClustersLoaded(List clusters) { + setState(() { + _clusters = clusters; + }); + } +} diff --git a/frontend/lib/Grounded/see/excuse/ViewExcuseReport.dart b/frontend/lib/Grounded/see/excuse/ViewExcuseReport.dart new file mode 100644 index 0000000..0cbc1bd --- /dev/null +++ b/frontend/lib/Grounded/see/excuse/ViewExcuseReport.dart @@ -0,0 +1,37 @@ +import '../../about/external/data/ExcuseCluster.dart'; +import '../../about/external/initial/ReportCardRequest.dart'; +import '../../utils/ObjectConvertors.dart'; +import '../parent/ParentViewModel.dart'; +import 'ConnectExcuseReport.dart'; + +class ViewExcuseReport extends ParentViewModel { + ConnectExcuseReport connection; + + ViewExcuseReport(super.context, this.connection); + + void loadClusters() async { + if (!await hasNetwork(() => loadClusters())) return; + + showLoading("Reading your excuses"); + + try { + final DateTime now = DateTime.now(); + final DateTime start = now.subtract(const Duration(days: 30)); + + final response = + await getDataManager().getExcuseClusters(ReportCardRequest( + periodStart: start.toIso8601String(), + periodEnd: now.toIso8601String(), + )); + + final List clusters = + getExcuseClusterList(response.data); + + closeLoading(); + + connection.onClustersLoaded(clusters); + } catch (e) { + handleError(e, () => loadClusters(), () => dismissError(), "Retry"); + } + } +} diff --git a/frontend/lib/Grounded/see/goal/ConnectGoalDetail.dart b/frontend/lib/Grounded/see/goal/ConnectGoalDetail.dart new file mode 100644 index 0000000..5b96e85 --- /dev/null +++ b/frontend/lib/Grounded/see/goal/ConnectGoalDetail.dart @@ -0,0 +1,9 @@ +import '../../about/external/data/Commitment.dart'; +import '../../about/external/data/Goal.dart'; + +abstract class ConnectGoalDetail { + void onGoalLoaded(Goal goal, List tasks); + + /// The task is ready to run — hand off to the full-screen runner. + void onTaskReady(Commitment task); +} diff --git a/frontend/lib/Grounded/see/goal/ConnectGoals.dart b/frontend/lib/Grounded/see/goal/ConnectGoals.dart new file mode 100644 index 0000000..a896a4f --- /dev/null +++ b/frontend/lib/Grounded/see/goal/ConnectGoals.dart @@ -0,0 +1,7 @@ +import '../../about/external/data/Goal.dart'; + +abstract class ConnectGoals { + void onGoalsLoaded(List goals); + + void onGoalSaved(); +} diff --git a/frontend/lib/Grounded/see/goal/GoalDetail.dart b/frontend/lib/Grounded/see/goal/GoalDetail.dart new file mode 100644 index 0000000..a574b9c --- /dev/null +++ b/frontend/lib/Grounded/see/goal/GoalDetail.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +import '../../about/external/data/Goal.dart'; +import 'GoalDetailState.dart'; + +class GoalDetail extends StatefulWidget { + final Goal goal; + + const GoalDetail({super.key, required this.goal}); + + @override + State createState() => GoalDetailState(); +} diff --git a/frontend/lib/Grounded/see/goal/GoalDetailState.dart b/frontend/lib/Grounded/see/goal/GoalDetailState.dart new file mode 100644 index 0000000..adb35a8 --- /dev/null +++ b/frontend/lib/Grounded/see/goal/GoalDetailState.dart @@ -0,0 +1,310 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/external/data/Commitment.dart'; +import '../../about/external/data/Goal.dart'; +import '../../about/internal/application/CommitmentClass.dart'; +import '../../about/internal/application/CommitmentStatus.dart'; +import '../../about/internal/application/ProofType.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../configs/Navigator.dart'; +import '../../designs/Component.dart'; +import '../../designs/Responsive.dart'; +import '../../designs/Shell.dart'; +import '../../designs/buttons/Buttons.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/Colors.dart'; +import '../../utils/CommonUtils.dart'; +import '../../utils/DebtEngine.dart'; +import '../commitment/NewCommitment.dart'; +import '../live/LiveTask.dart'; +import 'ConnectGoalDetail.dart'; +import 'GoalDetail.dart'; +import 'ViewGoalDetail.dart'; + +class GoalDetailState extends State + implements ConnectGoalDetail { + ViewGoalDetail? _model; + + Goal _goal = Goal(); + + List _tasks = []; + + bool _changed = false; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewGoalDetail(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ); + } + + void _initiate() { + setState(() { + _goal = widget.goal; + }); + _model?.loadTasks(widget.goal); + } + + // ── Handlers ────────────────────────────────────────────────────────────── + + void _onBack() { + Navigator.pop(context, _changed); + } + + void _onStartTask(Commitment task) { + _model?.startTask(task); + } + + void _onAddTask() async { + final result = await GroundedNavigation() + .navigateToPageWithData(const NewCommitment(), context); + + if (result == true) { + _changed = true; + _model?.loadTasks(_goal); + } + } + + // ── Views ───────────────────────────────────────────────────────────────── + + Widget _mobileView(BoxConstraints constraints) { + final List open = _tasks + .where((task) => + task.status != CommitmentStatus.Completed && + task.status != CommitmentStatus.LateCompleted && + task.status != CommitmentStatus.Abandoned) + .toList(); + + final List done = _tasks + .where((task) => + task.status == CommitmentStatus.Completed || + task.status == CommitmentStatus.LateCompleted) + .toList(); + + return Sheet( + eyebrow: "Goal", + title: _goal.title, + onBack: _onBack, + action: chromeAction(CupertinoIcons.add, _onAddTask), + banner: _progressBanner(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + if (_goal.description.isNotEmpty) ...[ + text(_goal.description, 15, TextType.Regular, + color: colorGrey2, height: 1.6), + const SizedBox(height: 28), + ], + sectionBreak("To do", caption: "${open.length} open"), + if (open.isEmpty) + emptyState( + CupertinoIcons.square_list, + "Nothing scheduled", + "Add the actual sessions — Monday shoulders, Wednesday legs — and they start counting.", + ) + else + ...open.map(_taskRow), + if (done.isNotEmpty) ...[ + const SizedBox(height: 28), + sectionBreak("Done", caption: "${done.length}"), + ...done.map(_doneRow), + ], + const SizedBox(height: 24), + roundedCornerButton("Add a task", _onAddTask, + icon: CupertinoIcons.add), + ], + ), + ); + } + + Widget _progressBanner() { + return Container( + padding: const EdgeInsets.fromLTRB(16, 14, 16, 14), + decoration: BoxDecoration( + color: colorWhite.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(16), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + text("PROGRESS", 9, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), + letterSpacing: 1.2), + text("${(_goal.progress * 100).round()}%", 13, TextType.Bold, + color: colorWhite), + ], + ), + const SizedBox(height: 12), + meter( + _goal.progress, + fill: colorWhite, + track: colorWhite.withValues(alpha: 0.14), + height: 5, + ), + ], + ), + ); + } + + /// A task row leads with the action: the point of opening a goal is to start + /// something, not to admire the list. + Widget _taskRow(Commitment task) { + final Color accent = classColor(task.commitmentClass); + + final bool late = task.windowClosed; + + return Container( + margin: const EdgeInsets.only(bottom: 10), + child: card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 3, + height: 38, + margin: const EdgeInsets.only(right: 14, top: 2), + decoration: BoxDecoration( + color: accent, + borderRadius: BorderRadius.circular(4), + ), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text(task.title, 16, TextType.Medium, + color: colorPrimaryDark, + maxLines: 2, + overflow: TextOverflow.ellipsis), + const SizedBox(height: 7), + Row( + children: [ + text(formatWindow(task), 11, TextType.Regular, + color: colorGrey2), + const SizedBox(width: 9), + Container( + width: 3, + height: 3, + decoration: BoxDecoration( + color: colorGrey, shape: BoxShape.circle), + ), + const SizedBox(width: 9), + text(formatMinutes(task.estMinutes), 11, + TextType.Regular, color: colorGrey2), + ], + ), + ], + ), + ), + const SizedBox(width: 8), + pill(proofLabel(task.proofType), colorGrey2, colorMuted, + textSize: 9), + ], + ), + if (late) ...[ + const SizedBox(height: 12), + Row( + children: [ + pill(overdueLabel(task), colorStandingGrounded, + colorStandingGroundedBg, textSize: 9), + const SizedBox(width: 6), + pill("−${formatDebt(DebtEngine.commitmentDebt(task))}", + colorGrey2, colorMuted, textSize: 9), + ], + ), + ], + const SizedBox(height: 14), + roundedCornerButton( + "Start", + () => _onStartTask(task), + icon: CupertinoIcons.play_fill, + verticalPadding: 13, + ), + ], + ), + ), + ); + } + + Widget _doneRow(Commitment task) { + final bool late = task.status == CommitmentStatus.LateCompleted; + + return Container( + margin: const EdgeInsets.only(bottom: 8), + child: card( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 13), + child: Row( + children: [ + Icon( + late + ? CupertinoIcons.checkmark_circle + : CupertinoIcons.checkmark_circle_fill, + size: 17, + color: late ? colorStandingWarned : colorPositive, + ), + const SizedBox(width: 12), + Expanded( + child: text(task.title, 13, TextType.Regular, + color: colorGrey2, + maxLines: 1, + overflow: TextOverflow.ellipsis), + ), + if (late) + pill("Late", colorStandingWarned, colorStandingWarnedBg, + textSize: 9), + ], + ), + ), + ); + } + + // ── ConnectGoalDetail ───────────────────────────────────────────────────── + + @override + void onGoalLoaded(Goal goal, List tasks) { + setState(() { + _goal = goal; + _tasks = tasks; + }); + } + + @override + void onTaskReady(Commitment task) async { + // The runner takes over the whole screen — a task you are running is the + // thing you are doing, not a row in a list. + final result = await GroundedNavigation().navigateToPageWithData( + LiveTask(commitment: task, goalTitle: _goal.title), + context, + ); + + if (result == true) { + _changed = true; + _model?.loadTasks(_goal); + } + } +} diff --git a/frontend/lib/Grounded/see/goal/Goals.dart b/frontend/lib/Grounded/see/goal/Goals.dart new file mode 100644 index 0000000..554e408 --- /dev/null +++ b/frontend/lib/Grounded/see/goal/Goals.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'GoalsState.dart'; + +class Goals extends StatefulWidget { + const Goals({super.key}); + + @override + State createState() => GoalsState(); +} diff --git a/frontend/lib/Grounded/see/goal/GoalsState.dart b/frontend/lib/Grounded/see/goal/GoalsState.dart new file mode 100644 index 0000000..633b8b7 --- /dev/null +++ b/frontend/lib/Grounded/see/goal/GoalsState.dart @@ -0,0 +1,311 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/external/data/Goal.dart'; +import '../../about/external/initial/GoalRequest.dart'; +import '../../about/internal/application/CommitmentClass.dart'; +import '../../about/internal/application/NavigatorType.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../configs/Navigator.dart'; +import '../../designs/Component.dart'; +import '../../designs/Responsive.dart'; +import '../../designs/Shell.dart'; +import '../../designs/buttons/Buttons.dart'; +import '../../designs/input/InputFields.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/Colors.dart'; +import '../../utils/CommonUtils.dart'; +import '../../utils/Validators.dart'; +import 'ConnectGoals.dart'; +import 'GoalDetail.dart'; +import 'Goals.dart'; +import 'ViewGoals.dart'; + +class GoalsState extends State implements ConnectGoals { + ViewGoals? _model; + + List _goals = []; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewGoals(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ); + } + + void _initiate() { + _model?.loadGoals(); + } + + void _onBack() { + Navigator.pop(context); + } + + void _onOpenGoal(Goal goal) async { + final result = await GroundedNavigation() + .navigateToPageWithData(GoalDetail(goal: goal), context); + + if (result == true) { + _model?.loadGoals(); + } + } + + void _onNewGoal() { + _openGoalSheet(); + } + + /// Goals are lightweight on purpose — a name and a default class. The + /// weight lives on the tasks inside them. + void _openGoalSheet() { + final TextEditingController title = TextEditingController(); + final TextEditingController description = TextEditingController(); + final GlobalKey formKey = GlobalKey(); + + CommitmentClass defaultClass = CommitmentClass.Standard; + + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + barrierColor: colorPrimaryDark.withValues(alpha: 0.6), + builder: (BuildContext sheetContext) { + return StatefulBuilder( + builder: (BuildContext sheetContext, StateSetter setSheetState) { + return Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(sheetContext).viewInsets.bottom, + ), + child: Container( + decoration: BoxDecoration( + color: colorSheetBackground, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28), + ), + ), + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 16, 24, 32), + child: Form( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + width: 36, + height: 4, + margin: const EdgeInsets.only(bottom: 20), + decoration: BoxDecoration( + color: colorGrey.withValues(alpha: 0.35), + borderRadius: BorderRadius.circular(999), + ), + ), + ), + text("NEW GOAL", 9, TextType.Bold, + color: colorGrey2, letterSpacing: 1.2), + const SizedBox(height: 10), + text("What are you\nworking toward?", 26, + TextType.Light, + color: colorPrimaryDark, height: 1.2), + const SizedBox(height: 20), + inputField( + "Goal", + title, + hint: "Workout, thesis, get the flat sorted…", + validator: Validators.title, + ), + const SizedBox(height: 18), + inputField( + "Why it matters", + description, + hint: "Optional, but it helps on the bad days", + maxLines: 3, + ), + const SizedBox(height: 20), + text("TASKS DEFAULT TO", 9, TextType.Bold, + color: colorGrey2, letterSpacing: 1.0), + const SizedBox(height: 10), + segmentedSelector( + options: CommitmentClass.values, + selected: defaultClass, + label: classLabel, + onSelected: (value) => setSheetState(() { + defaultClass = value; + }), + activeColor: classColor(defaultClass), + ), + const SizedBox(height: 24), + roundedCornerButton( + "Create goal", + () { + if (formKey.currentState?.validate() != true) { + return; + } + Navigator.pop(sheetContext); + _model?.save(GoalRequest( + title: title.text.trim(), + description: description.text.trim(), + defaultClass: defaultClass.name, + startDate: DateTime.now().toIso8601String(), + )); + }, + icon: CupertinoIcons.add, + ), + const SizedBox(height: 8), + Center( + child: textButton("Cancel", + () => Navigator.pop(sheetContext), + textSize: 13), + ), + ], + ), + ), + ), + ), + ); + }, + ); + }, + ); + } + + Widget _mobileView(BoxConstraints constraints) { + return Sheet( + eyebrow: "Grounded", + title: "Goals", + onBack: _onBack, + action: chromeAction(CupertinoIcons.add, _onNewGoal), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + displayTitle("What you are\nworking toward."), + const SizedBox(height: 14), + text( + "A goal holds the tasks that get you there. The goal never carries debt — the tasks inside it do.", + 14, + TextType.Regular, + color: colorGrey2, + height: 1.55, + ), + const SizedBox(height: 28), + if (_goals.isEmpty) + emptyState( + CupertinoIcons.flag, + "No goals yet", + "Create one — Workout, say — then put the actual sessions inside it.", + ) + else + ..._goals.map(_goalCard), + const SizedBox(height: 24), + roundedCornerButton("New goal", _onNewGoal, + icon: CupertinoIcons.add), + ], + ), + ); + } + + Widget _goalCard(Goal goal) { + final Color accent = + goal.slipping ? colorStandingGrounded : colorPrimaryDark; + + return Container( + margin: const EdgeInsets.only(bottom: 12), + child: card( + onTap: () => _onOpenGoal(goal), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text(goal.title, 19, TextType.Light, + color: colorPrimaryDark, + maxLines: 2, + overflow: TextOverflow.ellipsis), + if (goal.description.isNotEmpty) ...[ + const SizedBox(height: 6), + text(goal.description, 12, TextType.Regular, + color: colorGrey2, + maxLines: 2, + overflow: TextOverflow.ellipsis), + ], + ], + ), + ), + const SizedBox(width: 10), + if (goal.overdueTasks > 0) + pill("${goal.overdueTasks} late", colorStandingGrounded, + colorStandingGroundedBg, textSize: 9), + ], + ), + const SizedBox(height: 18), + meter(goal.progress, fill: accent), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: labelled( + "Done", + "${goal.completedTasks} of ${goal.totalTasks}", + valueSize: 13, + ), + ), + Expanded( + child: labelled( + "Remaining", + "${goal.remainingTasks}", + valueSize: 13, + ), + ), + Expanded( + child: labelled( + "Debt", + formatDebt(goal.debtContribution), + valueSize: 13, + valueColor: goal.debtContribution > 0 + ? colorStandingGrounded + : colorPrimaryDark, + ), + ), + ], + ), + ], + ), + ), + ); + } + + @override + void onGoalsLoaded(List goals) { + setState(() { + _goals = goals; + }); + } + + @override + void onGoalSaved() { + _model?.loadGoals(); + } +} diff --git a/frontend/lib/Grounded/see/goal/ViewGoalDetail.dart b/frontend/lib/Grounded/see/goal/ViewGoalDetail.dart new file mode 100644 index 0000000..5369829 --- /dev/null +++ b/frontend/lib/Grounded/see/goal/ViewGoalDetail.dart @@ -0,0 +1,37 @@ +import '../../about/external/data/Commitment.dart'; +import '../../about/external/data/Goal.dart'; +import '../../about/external/initial/IdRequest.dart'; +import '../../utils/ObjectConvertors.dart'; +import '../parent/ParentViewModel.dart'; +import 'ConnectGoalDetail.dart'; + +class ViewGoalDetail extends ParentViewModel { + ConnectGoalDetail connection; + + ViewGoalDetail(super.context, this.connection); + + void loadTasks(Goal goal) async { + if (!await hasNetwork(() => loadTasks(goal))) return; + + showLoading("Loading ${goal.title}"); + + try { + final response = + await getDataManager().getGoalTasks(IdRequest(id: goal.id ?? "")); + + closeLoading(); + + connection.onGoalLoaded(goal, getCommitmentList(response.data)); + } catch (e) { + handleError(e, () => loadTasks(goal), () => dismissError(), "Retry"); + } + } + + /// Stashes the task as the active one before the runner opens, so the + /// ongoing notification and any relaunch land back on the right thing. + void startTask(Commitment task) async { + await getDataManager().setActiveCommitment(task); + + connection.onTaskReady(task); + } +} diff --git a/frontend/lib/Grounded/see/goal/ViewGoals.dart b/frontend/lib/Grounded/see/goal/ViewGoals.dart new file mode 100644 index 0000000..3418bab --- /dev/null +++ b/frontend/lib/Grounded/see/goal/ViewGoals.dart @@ -0,0 +1,51 @@ +import '../../about/external/data/pages/request/HistoryRequest.dart'; +import '../../about/external/data/pages/request/PageAndSort.dart'; +import '../../about/external/data/pages/request/Pageable.dart'; +import '../../about/external/data/pages/request/Sort.dart'; +import '../../about/external/initial/GoalRequest.dart'; +import '../../utils/ObjectConvertors.dart'; +import '../parent/ParentViewModel.dart'; +import 'ConnectGoals.dart'; + +class ViewGoals extends ParentViewModel { + ConnectGoals connection; + + ViewGoals(super.context, this.connection); + + void loadGoals() async { + if (!await hasNetwork(() => loadGoals())) return; + + showLoading("Loading your goals"); + + try { + final response = await getDataManager().getMyGoals(HistoryRequest( + query: PageAndSort( + sort: Sort('desc', 'startDate'), + page: Pageable(0, 0, 50, 0), + ), + )); + + closeLoading(); + + connection.onGoalsLoaded(getGoalList(response.data)); + } catch (e) { + handleError(e, () => loadGoals(), () => dismissError(), "Retry"); + } + } + + void save(GoalRequest request) async { + if (!await hasNetwork(() => save(request))) return; + + showLoading("Saving"); + + try { + await getDataManager().saveGoalEntry(request); + + closeLoading(); + + connection.onGoalSaved(); + } catch (e) { + handleError(e, () => save(request), () => dismissError(), "Retry"); + } + } +} diff --git a/frontend/lib/Grounded/see/home/ConnectHome.dart b/frontend/lib/Grounded/see/home/ConnectHome.dart new file mode 100644 index 0000000..dad1be4 --- /dev/null +++ b/frontend/lib/Grounded/see/home/ConnectHome.dart @@ -0,0 +1,24 @@ +import '../../about/external/data/Commitment.dart'; +import '../../about/external/data/ExcuseCluster.dart'; +import '../../about/internal/application/Standing.dart'; +import '../../about/internal/application/UserDetails.dart'; + +abstract class ConnectHome { + void onUserLoaded(UserDetails details); + + void onPlanLoaded(List plan); + + void onOverdueLoaded(List overdue); + + /// Standing arrives derived, with the debt it was derived from. + void onStandingResolved(Standing standing, double debtScore); + + /// The one excuse pattern worth confronting the user with today. + void onExcuseInsight(ExcuseCluster? cluster); + + /// Distress detected — the strict persona drops entirely. + void onDistressDetected(); + + /// Creating a commitment is refused at this standing. + void onCreationBlocked(String reason); +} diff --git a/frontend/lib/Grounded/see/home/Home.dart b/frontend/lib/Grounded/see/home/Home.dart new file mode 100644 index 0000000..157df0a --- /dev/null +++ b/frontend/lib/Grounded/see/home/Home.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'HomeState.dart'; + +class Home extends StatefulWidget { + const Home({super.key}); + + @override + State createState() => HomeState(); +} diff --git a/frontend/lib/Grounded/see/home/HomeState.dart b/frontend/lib/Grounded/see/home/HomeState.dart new file mode 100644 index 0000000..45949e1 --- /dev/null +++ b/frontend/lib/Grounded/see/home/HomeState.dart @@ -0,0 +1,696 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/external/data/Commitment.dart'; +import '../../about/external/data/ExcuseCluster.dart'; +import '../../about/internal/application/CommitmentClass.dart'; +import '../../about/internal/application/CommitmentStatus.dart'; +import '../../about/internal/application/NavigatorType.dart'; +import '../../about/internal/application/NotificationType.dart'; +import '../../about/internal/application/Standing.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../about/internal/application/ToneLevel.dart'; +import '../../about/internal/application/UserDetails.dart'; +import '../../configs/Navigator.dart'; +import '../../designs/Component.dart'; +import '../../designs/Responsive.dart'; +import '../../designs/Shell.dart'; +import '../../designs/buttons/Buttons.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/Colors.dart'; +import '../../utils/CommonUtils.dart'; +import '../../utils/DebtEngine.dart'; +import '../../utils/StandingEngine.dart'; +import '../../utils/Thresholds.dart'; +import '../../utils/ToneEngine.dart'; +import '../commitment/NewCommitment.dart'; +import '../excuse/ExcuseReport.dart'; +import '../goal/Goals.dart'; +import '../overdue/OverdueQueue.dart'; +import '../reportcard/ReportCardScreen.dart'; +import '../settings/Settings.dart'; +import '../training/Training.dart'; +import 'ConnectHome.dart'; +import 'Home.dart'; +import 'ViewHome.dart'; + +class HomeState extends State implements ConnectHome { + ViewHome? _model; + + UserDetails _user = UserDetails(pic: '', name: ''); + + List _plan = []; + + List _overdue = []; + + Standing _standing = Standing.Good; + + double _debt = 0; + + ExcuseCluster? _insight; + + bool _distressed = false; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewHome(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ); + } + + void _initiate() { + _model?.initialise(); + } + + // ── Handlers ────────────────────────────────────────────────────────────── + + void _onOpenOverdue() async { + final result = await GroundedNavigation() + .navigateToPageWithData(const OverdueQueue(), context); + + if (result == true) { + _model?.loadPlan(); + } + } + + void _onAddCommitment() async { + if (!StandingEngine.permitsNewCommitment(_standing)) { + _model?.requestNewCommitment(_standing, CommitmentClass.Standard); + return; + } + + final result = await GroundedNavigation() + .navigateToPageWithData(const NewCommitment(), context); + + if (result == true) { + _model?.loadPlan(); + } + } + + void _onOpenReportCard() { + GroundedNavigation().navigateToPage( + NavigatorType.justOpen, const ReportCardScreen(), context); + } + + void _onOpenGoals() async { + final result = await GroundedNavigation() + .navigateToPageWithData(const Goals(), context); + + if (result == true) { + _model?.loadPlan(); + } + } + + void _onOpenTraining() { + GroundedNavigation() + .navigateToPage(NavigatorType.justOpen, const Training(), context); + } + + void _onOpenSettings() { + GroundedNavigation() + .navigateToPage(NavigatorType.justOpen, const Settings(), context); + } + + void _onOpenExcuses() { + GroundedNavigation() + .navigateToPage(NavigatorType.justOpen, const ExcuseReport(), context); + } + + // ── Views ───────────────────────────────────────────────────────────────── + + Widget _mobileView(BoxConstraints constraints) { + // Grounded and Lockdown replace the home screen with the overdue queue — + // you do not get to look at your nice plans, only at your mess. + final bool queueIsHome = + StandingEngine.showsOverdueQueueAsHome(_standing) && !_distressed; + + return Sheet( + eyebrow: _user.name.isEmpty ? "Grounded" : _user.name, + title: queueIsHome ? "What you owe" : "Today", + chrome: _distressed ? colorPrimaryDark : _chromeFor(_standing), + banner: _standingBanner(), + action: chromeAction( + CupertinoIcons.person, + _onOpenSettings, + dotted: _user.sickMode, + dotColor: colorStandingWarned, + ), + child: _distressed + ? _distressBody() + : queueIsHome + ? _groundedBody() + : _planBody(), + ); + } + + /// The chrome carries the standing colour, so the tier is legible before a + /// single word is read. + Color _chromeFor(Standing standing) { + switch (standing) { + case Standing.Good: + return colorPrimaryDark; + case Standing.Warned: + return colorPrimaryDark; + case Standing.Grounded: + return colorStandingGrounded; + case Standing.Lockdown: + return colorStandingLockdown; + } + } + + /// The debt strip that sits in the black chrome under the title. + Widget _standingBanner() { + if (_distressed) { + return const SizedBox.shrink(); + } + + final Color tone = standingColor(_standing); + + return Container( + padding: const EdgeInsets.fromLTRB(16, 14, 16, 14), + decoration: BoxDecoration( + color: colorWhite.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(16), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Container( + width: 7, + height: 7, + decoration: BoxDecoration( + color: _standing == Standing.Good ? tone : colorWhite, + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 8), + text( + standingLabel(_standing).toUpperCase(), + 9, + TextType.Bold, + color: colorWhite.withValues(alpha: 0.75), + letterSpacing: 1.2, + ), + ], + ), + const SizedBox(height: 8), + text( + ToneEngine.standingHeadline(_standing, _user.tone), + 16, + TextType.Medium, + color: colorWhite, + ), + ], + ), + ), + const SizedBox(width: 16), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + text("DEBT", 9, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), + letterSpacing: 1.0), + const SizedBox(height: 4), + text(formatDebt(_debt), 30, TextType.Light, color: colorWhite), + ], + ), + ], + ), + ); + } + + /// The normal day: the plan, with the overdue count kept visible above it so + /// it is never out of sight. + Widget _planBody() { + final int overdueCount = _overdue.length; + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + if (overdueCount > 0) ...[ + _overdueCallout(overdueCount), + const SizedBox(height: 24), + ], + if (_insight != null) ...[ + _insightCard(_insight!), + const SizedBox(height: 24), + ], + sectionBreak( + "The plan", + caption: "${_plan.length} committed", + trailing: _plan.isEmpty + ? null + : text(formatMinutes(_plannedMinutes()), 12, TextType.Bold, + color: colorGrey2), + ), + if (_plan.isEmpty) + emptyState( + CupertinoIcons.square_list, + "Nothing committed today", + "An empty plan is a decision too. Add something you actually intend to do.", + ) + else + ..._plan.map(_commitmentRow), + const SizedBox(height: 28), + _quickLinks(), + const SizedBox(height: 24), + roundedCornerButton( + "Commit to something", + _onAddCommitment, + icon: CupertinoIcons.add, + enabled: StandingEngine.permitsNewCommitment(_standing), + ), + if (!StandingEngine.permitsNewCommitment(_standing)) ...[ + const SizedBox(height: 10), + text( + ToneEngine.standingBody(_standing, _user.tone), + 12, + TextType.Regular, + color: colorGrey2, + align: TextAlign.center, + ), + ], + ], + ); + } + + /// Grounded: the plan is hidden entirely and only the mess is shown. + Widget _groundedBody() { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + text("YOUR PLANS ARE HIDDEN", 10, TextType.Bold, + color: colorGrey2, letterSpacing: 1.2), + const SizedBox(height: 10), + displayTitle( + _standing == Standing.Lockdown + ? "One at a time." + : "Clear this first.", + ), + const SizedBox(height: 12), + text( + ToneEngine.standingBody(_standing, _user.tone), + 14, + TextType.Regular, + color: colorGrey2, + height: 1.55, + ), + const SizedBox(height: 28), + card( + background: standingBackground(_standing), + borderColor: standingColor(_standing).withValues(alpha: 0.20), + child: Row( + children: [ + Expanded( + child: labelled( + "Open overdue", + "${_overdue.length}", + valueSize: 26, + valueType: TextType.Light, + valueColor: standingColor(_standing), + ), + ), + Expanded( + child: labelled( + "Debt to clear", + formatDebt( + StandingEngine.debtToNextTierDown(_debt, _standing)), + valueSize: 26, + valueType: TextType.Light, + valueColor: standingColor(_standing), + ), + ), + ], + ), + ), + const SizedBox(height: 24), + sectionBreak("Outstanding", caption: "${_overdue.length} items"), + if (_overdue.isEmpty) + emptyState( + CupertinoIcons.checkmark_seal, + "The queue is empty", + "Your standing will recover as the debt decays.", + ) + else + ..._overdue.take(_standing == Standing.Lockdown ? 1 : _overdue.length) + .map(_commitmentRow), + const SizedBox(height: 24), + roundedCornerButton( + _standing == Standing.Lockdown ? "Deal with this one" : "Open the queue", + _onOpenOverdue, + background: standingColor(_standing), + icon: CupertinoIcons.arrow_right, + ), + ], + ); + } + + /// Distress: the strict persona drops entirely. This is the difference + /// between a product people keep and one they resent. + Widget _distressBody() { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + text("A NOTE", 10, TextType.Bold, color: colorGrey2, letterSpacing: 1.2), + const SizedBox(height: 10), + displayTitle(ToneEngine.distressHeadline()), + const SizedBox(height: 14), + text( + ToneEngine.distressBody(), + 15, + TextType.Regular, + color: colorGrey2, + height: 1.6, + ), + const SizedBox(height: 28), + card( + background: colorStandingGoodBg, + borderColor: colorPositive.withValues(alpha: 0.20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text("PAUSED", 9, TextType.Bold, + color: colorPositive, letterSpacing: 1.2), + const SizedBox(height: 8), + text("Debt is not accruing right now.", 16, TextType.Medium, + color: colorPrimaryDark), + const SizedBox(height: 6), + text( + "Nothing you miss this week is counting against you.", + 13, + TextType.Regular, + color: colorGrey2, + height: 1.5, + ), + ], + ), + ), + const SizedBox(height: 24), + sectionBreak("Three things", caption: "that actually matter"), + ..._plan + .where((item) => + item.commitmentClass == CommitmentClass.NonNegotiable) + .take(3) + .map(_commitmentRow), + const SizedBox(height: 24), + outlinedActionButton("Open settings", _onOpenSettings, + icon: CupertinoIcons.slider_horizontal_3), + ], + ); + } + + Widget _overdueCallout(int count) { + return card( + background: colorStandingGroundedBg, + borderColor: colorStandingGrounded.withValues(alpha: 0.20), + onTap: _onOpenOverdue, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 44, + height: 44, + alignment: Alignment.center, + decoration: BoxDecoration( + color: colorStandingGrounded, + borderRadius: BorderRadius.circular(13), + ), + child: text("$count", 17, TextType.Bold, color: colorWhite), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text("OVERDUE", 9, TextType.Bold, + color: colorStandingGrounded, letterSpacing: 1.2), + const SizedBox(height: 5), + text( + count >= Thresholds.maxOpenOverdue + ? "You are at the cap. Nothing new until this drops." + : "$count item${count == 1 ? "" : "s"} past the window.", + 14, + TextType.Medium, + color: colorPrimaryDark, + ), + ], + ), + ), + Icon(CupertinoIcons.chevron_right, + size: 15, color: colorStandingGrounded), + ], + ), + ); + } + + /// The excuse confrontation. One pattern, stated plainly, with the + /// suggestion attached. + Widget _insightCard(ExcuseCluster cluster) { + return card( + background: colorPrimaryDark, + borderColor: colorPrimaryDark, + onTap: _onOpenExcuses, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + text("PATTERN", 9, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), + letterSpacing: 1.2), + text("${cluster.occurrences}×", 11, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45)), + ], + ), + const SizedBox(height: 12), + text(cluster.insight, 15, TextType.Regular, + color: colorWhite, height: 1.55), + ], + ), + ); + } + + Widget _commitmentRow(Commitment item) { + final bool late = item.windowClosed && + item.status != CommitmentStatus.Completed && + item.status != CommitmentStatus.LateCompleted; + + final Color accent = classColor(item.commitmentClass); + + return Container( + margin: const EdgeInsets.only(bottom: 10), + child: card( + padding: const EdgeInsets.fromLTRB(14, 14, 14, 14), + onTap: _onOpenOverdue, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 3, + height: 42, + margin: const EdgeInsets.only(right: 14, top: 2), + decoration: BoxDecoration( + color: accent, + borderRadius: BorderRadius.circular(4), + ), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text(item.title, 15, TextType.Medium, + color: colorPrimaryDark, + maxLines: 2, + overflow: TextOverflow.ellipsis), + const SizedBox(height: 8), + Row( + children: [ + text(formatWindow(item), 11, TextType.Regular, + color: colorGrey2), + const SizedBox(width: 10), + Container(width: 3, height: 3, decoration: BoxDecoration( + color: colorGrey, shape: BoxShape.circle)), + const SizedBox(width: 10), + text(formatMinutes(item.estMinutes), 11, + TextType.Regular, color: colorGrey2), + ], + ), + if (late) ...[ + const SizedBox(height: 10), + Row( + children: [ + pill( + overdueLabel(item), + colorStandingGrounded, + colorStandingGroundedBg, + textSize: 9, + ), + const SizedBox(width: 6), + pill( + "−${formatDebt(DebtEngine.commitmentDebt(item))}", + colorGrey2, + colorMuted, + textSize: 9, + ), + ], + ), + ], + ], + ), + ), + const SizedBox(width: 10), + pill( + classLabel(item.commitmentClass), + accent, + classBackground(item.commitmentClass), + textSize: 9, + ), + ], + ), + ), + ); + } + + Widget _quickLinks() { + return Row( + children: [ + Expanded( + child: _quickLink( + CupertinoIcons.flag_fill, + "Goals", + _onOpenGoals, + ), + ), + const SizedBox(width: 10), + Expanded( + child: _quickLink( + CupertinoIcons.chart_bar_alt_fill, + "Report", + _onOpenReportCard, + ), + ), + const SizedBox(width: 10), + Expanded( + child: _quickLink( + CupertinoIcons.flame_fill, + "Training", + _onOpenTraining, + ), + ), + ], + ); + } + + Widget _quickLink(IconData icon, String label, VoidCallback onTap) { + return card( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 16), + onTap: onTap, + child: Row( + children: [ + Icon(icon, size: 17, color: colorPrimaryDark), + const SizedBox(width: 10), + Expanded( + child: text(label, 13, TextType.Medium, + color: colorPrimaryDark, maxLines: 1, + overflow: TextOverflow.ellipsis), + ), + ], + ), + ); + } + + double _plannedMinutes() { + double total = 0; + for (Commitment item in _plan) { + total = total + item.estMinutes; + } + return total; + } + + // ── ConnectHome ─────────────────────────────────────────────────────────── + + @override + void onUserLoaded(UserDetails details) { + setState(() { + _user = details; + }); + } + + @override + void onPlanLoaded(List plan) { + setState(() { + _plan = plan; + }); + } + + @override + void onOverdueLoaded(List overdue) { + setState(() { + _overdue = overdue; + }); + } + + @override + void onStandingResolved(Standing standing, double debtScore) { + setState(() { + _standing = standing; + _debt = debtScore; + }); + } + + @override + void onExcuseInsight(ExcuseCluster? cluster) { + setState(() { + _insight = cluster; + }); + } + + @override + void onDistressDetected() { + setState(() { + _distressed = true; + }); + } + + @override + void onCreationBlocked(String reason) { + _model?.showApplicationNotification( + NotificationType.warning, + "Not right now", + reason, + true, + true, + null, + ); + } +} diff --git a/frontend/lib/Grounded/see/home/ViewHome.dart b/frontend/lib/Grounded/see/home/ViewHome.dart new file mode 100644 index 0000000..6be05b6 --- /dev/null +++ b/frontend/lib/Grounded/see/home/ViewHome.dart @@ -0,0 +1,164 @@ +import '../../about/external/data/Commitment.dart'; +import '../../about/external/data/ExcuseCluster.dart'; +import '../../about/external/data/pages/request/CommitmentsRequest.dart'; +import '../../about/external/data/pages/request/HistoryRequest.dart'; +import '../../about/external/data/pages/request/PageAndSort.dart'; +import '../../about/external/data/pages/request/Pageable.dart'; +import '../../about/external/data/pages/request/Sort.dart'; +import '../../about/external/data/pages/response/CommitmentPage.dart'; +import '../../about/external/initial/ReportCardRequest.dart'; +import '../../about/internal/application/CommitmentClass.dart'; +import '../../about/internal/application/Standing.dart'; +import '../../about/internal/application/UserDetails.dart'; +import '../../utils/DebtEngine.dart'; +import '../../utils/GuardrailEngine.dart'; +import '../../utils/StandingEngine.dart'; +import '../../utils/ToneEngine.dart'; +import '../parent/ParentViewModel.dart'; +import 'ConnectHome.dart'; + +class ViewHome extends ParentViewModel { + ConnectHome connection; + + ViewHome(super.context, this.connection); + + /// Loads the cached user first so the screen never opens on a spinner, then + /// refreshes everything from the server. + void initialise() async { + final UserDetails cached = await getDataManager().getUserDetails(); + connection.onUserLoaded(cached); + + loadPlan(); + } + + void loadPlan() async { + if (!await hasNetwork(() => loadPlan())) return; + + showLoading("Loading your day"); + + try { + final response = await getDataManager().getTodayPlan(CommitmentsRequest( + query: PageAndSort( + sort: Sort('asc', 'dueStart'), + page: Pageable(0, 0, 50, 0), + ), + )); + + final CommitmentPage page = CommitmentPage.fromJson(response.data); + + closeLoading(); + + connection.onPlanLoaded(page.content); + + loadOverdue(); + } catch (e) { + handleError(e, () => loadPlan(), () => dismissError(), "Retry"); + } + } + + void loadOverdue() async { + try { + final response = await getDataManager().getOverdueQueue(HistoryRequest( + query: PageAndSort( + sort: Sort('desc', 'dueEnd'), + page: Pageable(0, 0, 50, 0), + ), + )); + + final CommitmentPage page = CommitmentPage.fromJson(response.data); + + connection.onOverdueLoaded(page.content); + + resolveStanding(page.content); + + loadExcuseInsight(); + } catch (e) { + handleError(e, () => loadOverdue(), () => dismissError(), "Retry"); + } + } + + /// Standing is derived on device from the same formula the server uses, so + /// the number on screen is never stale relative to the queue beneath it. + void resolveStanding(List overdue) async { + final UserDetails details = await getDataManager().getUserDetails(); + + final bool distressed = await _checkDistress(); + + final double debt = DebtEngine.totalDebt(overdue); + + final Standing standing = StandingEngine.evaluate( + debt, + missedNonNegotiables: DebtEngine.missedNonNegotiables(overdue), + sickMode: details.sickMode, + distressed: distressed, + ); + + await getDataManager().setCachedDebtScore(debt); + + connection.onStandingResolved(standing, debt); + + if (distressed) { + connection.onDistressDetected(); + } + } + + Future _checkDistress() async { + final double previous = await getDataManager().getCachedDebtScore(); + final int opens = await getDataManager().getEngagementCount(); + final double current = await getDataManager().getCachedDebtScore(); + + return GuardrailEngine.detectDistress( + debtDelta: current - previous, + appOpensThisWeek: opens, + meanReadiness: 0, + ); + } + + void loadExcuseInsight() async { + try { + final DateTime now = DateTime.now(); + final DateTime start = now.subtract(const Duration(days: 30)); + + final response = + await getDataManager().getExcuseClusters(ReportCardRequest( + periodStart: start.toIso8601String(), + periodEnd: now.toIso8601String(), + )); + + final List clusters = (response.data as List) + .map((item) => ExcuseCluster.fromJson(item)) + .toList(); + + // Only the strongest pattern is surfaced on the home screen — a wall of + // findings reads as noise and gets ignored. + final List worth = + clusters.where((cluster) => cluster.insight.isNotEmpty).toList(); + + connection.onExcuseInsight(worth.isEmpty ? null : worth.first); + } catch (e) { + // The insight is a bonus, never a blocker — a failure here stays silent. + connection.onExcuseInsight(null); + } + } + + /// The gate on creating anything new. Grounded blocks everything; Warned + /// blocks electives only. + void requestNewCommitment(Standing standing, CommitmentClass intended) async { + final bool elective = intended == CommitmentClass.Elective; + + if (StandingEngine.permitsNewCommitment(standing, elective: elective)) { + return; + } + + if (standing == Standing.Warned && elective) { + connection.onCreationBlocked( + "Electives are blocked while you are warned. Clear some debt first."); + return; + } + + final UserDetails details = await getDataManager().getUserDetails(); + + connection + .onCreationBlocked(ToneEngine.standingBody(standing, details.tone)); + } +} diff --git a/frontend/lib/Grounded/see/live/ConnectLiveTask.dart b/frontend/lib/Grounded/see/live/ConnectLiveTask.dart new file mode 100644 index 0000000..197fc1b --- /dev/null +++ b/frontend/lib/Grounded/see/live/ConnectLiveTask.dart @@ -0,0 +1,8 @@ +abstract class ConnectLiveTask { + void onCompleted(); + + /// Completion attempted before the required foreground time was reached. + void onTooEarly(int secondsRemaining); + + void onAbandoned(); +} diff --git a/frontend/lib/Grounded/see/live/LiveTask.dart b/frontend/lib/Grounded/see/live/LiveTask.dart new file mode 100644 index 0000000..126d871 --- /dev/null +++ b/frontend/lib/Grounded/see/live/LiveTask.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +import '../../about/external/data/Commitment.dart'; +import 'LiveTaskState.dart'; + +class LiveTask extends StatefulWidget { + /// The commitment being run. + final Commitment commitment; + + /// The goal it belongs to, shown as context in the runner and the ongoing + /// notification. + final String goalTitle; + + const LiveTask({super.key, required this.commitment, this.goalTitle = ""}); + + @override + State createState() => LiveTaskState(); +} diff --git a/frontend/lib/Grounded/see/live/LiveTaskState.dart b/frontend/lib/Grounded/see/live/LiveTaskState.dart new file mode 100644 index 0000000..199d970 --- /dev/null +++ b/frontend/lib/Grounded/see/live/LiveTaskState.dart @@ -0,0 +1,456 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/external/data/LiveSession.dart'; +import '../../about/internal/application/CommitmentClass.dart'; +import '../../about/internal/application/NotificationType.dart'; +import '../../about/internal/application/ProofType.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../designs/Component.dart'; +import '../../designs/buttons/Buttons.dart'; +import '../../designs/input/InputFields.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/Colors.dart'; +import '../../utils/CommonUtils.dart'; +import '../../utils/Validators.dart'; +import 'ConnectLiveTask.dart'; +import 'LiveTask.dart'; +import 'ViewLiveTask.dart'; + +/// The full-screen runner. Deliberately the only thing on screen: a task you +/// are running is not a row in a list, it is the thing you are doing. +class LiveTaskState extends State + with WidgetsBindingObserver + implements ConnectLiveTask { + ViewLiveTask? _model; + + late LiveSession _session; + + Timer? _ticker; + + /// Drives the display only. The elapsed value itself is derived from + /// wall-clock, so a throttled ticker during screen-off cannot lose time. + int _tick = 0; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + + _session = LiveSession( + commitmentId: widget.commitment.id ?? "", + title: widget.commitment.title, + goalTitle: widget.goalTitle, + startedAt: DateTime.now(), + requiredSeconds: widget.commitment.proofType == ProofType.Timer + ? widget.commitment.proofTimerMinutes * 60 + : 0, + ); + + _session.resume(); + } + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewLiveTask(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => PopScope( + // Leaving mid-run is a decision, not a back gesture. + canPop: false, + onPopInvokedWithResult: (didPop, result) { + if (!didPop) { + _onRequestExit(); + } + }, + child: AnnotatedRegion( + value: SystemUiOverlayStyle.light, + child: Scaffold( + backgroundColor: colorPrimaryDark, + body: SafeArea(child: _runnerView()), + ), + ), + ), + ); + } + + void _initiate() { + _startTicker(); + _model?.publishSession(_session); + } + + void _startTicker() { + _ticker?.cancel(); + _ticker = Timer.periodic(const Duration(seconds: 1), (timer) { + if (!mounted) { + return; + } + setState(() { + _tick = _tick + 1; + }); + + // Refresh the lock-screen notification every 5s rather than every tick, + // so the ongoing notification stays current without thrashing. + if (_tick % 5 == 0 && _session.running) { + _model?.publishSession(_session); + } + }); + } + + /// Backgrounding pauses the clock — that is what makes Timer proof mean + /// something. The count is kept and shown rather than hidden. + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + if (!_session.running) { + setState(() { + _session.resume(); + }); + _model?.publishSession(_session); + } + return; + } + + if (state == AppLifecycleState.paused || + state == AppLifecycleState.hidden) { + if (_session.running) { + setState(() { + _session.pause(); + _session.backgroundedCount = _session.backgroundedCount + 1; + }); + _model?.publishSession(_session); + } + } + } + + // ── Handlers ────────────────────────────────────────────────────────────── + + void _onTogglePause() { + setState(() { + if (_session.running) { + _session.pause(); + } else { + _session.resume(); + } + }); + _model?.publishSession(_session); + } + + void _onFinish() { + _model?.complete(widget.commitment, _session); + } + + void _onRequestExit() { + _model?.showApplicationNotification( + NotificationType.warning, + "Leave this running?", + _session.satisfied() + ? "You have met the requirement. You can finish it properly instead of walking away." + : "You are ${formatClock(_session.remainingSeconds())} short. Leaving now logs nothing.", + true, + true, + null, + action: "Leave anyway", + positiveAction: () { + Navigator.pop(context); + _model?.clearSession(); + Navigator.pop(context, false); + }, + ); + } + + void _onAbandon() { + final TextEditingController reason = TextEditingController(); + final GlobalKey formKey = GlobalKey(); + + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + barrierColor: colorBlack.withValues(alpha: 0.7), + builder: (BuildContext sheetContext) { + return Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(sheetContext).viewInsets.bottom, + ), + child: Container( + decoration: BoxDecoration( + color: colorSheetBackground, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28), + ), + ), + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 24, 24, 32), + child: Form( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + text("ABANDONING MID-RUN", 9, TextType.Bold, + color: colorStandingLockdown, letterSpacing: 1.2), + const SizedBox(height: 10), + text(widget.commitment.title, 24, TextType.Light, + color: colorPrimaryDark, height: 1.2), + const SizedBox(height: 16), + inputField( + "Reason", + reason, + hint: "Why is this stopping here?", + validator: Validators.excuse, + maxLines: 3, + ), + const SizedBox(height: 20), + destructiveButton("Abandon", () { + if (formKey.currentState?.validate() != true) { + return; + } + Navigator.pop(sheetContext); + _model?.abandon(widget.commitment, reason.text.trim()); + }), + const SizedBox(height: 8), + Center( + child: textButton("Keep going", + () => Navigator.pop(sheetContext), textSize: 13), + ), + ], + ), + ), + ), + ), + ); + }, + ); + } + + // ── Views ───────────────────────────────────────────────────────────────── + + Widget _runnerView() { + final bool timed = _session.requiredSeconds > 0; + + final bool satisfied = _session.satisfied(); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 20), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _runnerHeader(), + _runnerClock(timed, satisfied), + _runnerControls(timed, satisfied), + ], + ), + ); + } + + Widget _runnerHeader() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Container( + width: 7, + height: 7, + decoration: BoxDecoration( + color: _session.running ? colorPositive : colorWarning, + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 8), + text( + _session.running ? "IN PROGRESS" : "PAUSED", + 9, + TextType.Bold, + color: colorWhite.withValues(alpha: 0.55), + letterSpacing: 1.4, + ), + ], + ), + iconButton( + Icon(CupertinoIcons.xmark, + size: 15, color: colorWhite.withValues(alpha: 0.7)), + _onRequestExit, + ), + ], + ), + const SizedBox(height: 28), + if (widget.goalTitle.isNotEmpty) ...[ + text(widget.goalTitle.toUpperCase(), 10, TextType.Bold, + color: colorWhite.withValues(alpha: 0.40), letterSpacing: 1.4), + const SizedBox(height: 10), + ], + text(widget.commitment.title, 34, TextType.Light, + color: colorWhite, height: 1.15), + const SizedBox(height: 16), + Row( + children: [ + pill( + classLabel(widget.commitment.commitmentClass), + colorWhite, + colorWhite.withValues(alpha: 0.12), + textSize: 9, + ), + const SizedBox(width: 8), + pill( + proofLabel(widget.commitment.proofType), + colorWhite.withValues(alpha: 0.75), + colorWhite.withValues(alpha: 0.08), + textSize: 9, + ), + ], + ), + ], + ); + } + + Widget _runnerClock(bool timed, bool satisfied) { + final int elapsed = _session.elapsedSeconds(); + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + text( + timed ? (satisfied ? "REQUIREMENT MET" : "REMAINING") : "ELAPSED", + 9, + TextType.Bold, + color: satisfied + ? colorPositive + : colorWhite.withValues(alpha: 0.40), + letterSpacing: 1.6, + ), + const SizedBox(height: 18), + text( + timed && !satisfied + ? formatClock(_session.remainingSeconds()) + : formatClock(elapsed), + 78, + TextType.Light, + color: colorWhite, + height: 1.0, + ), + if (timed) ...[ + const SizedBox(height: 28), + meter( + _session.progress(), + fill: satisfied ? colorPositive : colorWhite, + track: colorWhite.withValues(alpha: 0.12), + height: 5, + ), + const SizedBox(height: 14), + text( + satisfied + ? "You can finish this now." + : "Leaving the app pauses the clock.", + 12, + TextType.Regular, + color: colorWhite.withValues(alpha: 0.45), + align: TextAlign.center, + ), + ], + if (_session.backgroundedCount > 0) ...[ + const SizedBox(height: 18), + pill( + "Left ${_session.backgroundedCount}×", + colorWarning, + colorWarning.withValues(alpha: 0.12), + textSize: 9, + ), + ], + ], + ); + } + + Widget _runnerControls(bool timed, bool satisfied) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + roundedCornerButton( + satisfied || !timed ? "Finish" : "Finish early", + _onFinish, + background: satisfied || !timed ? colorWhite : colorWhite.withValues(alpha: 0.14), + foreground: satisfied || !timed ? colorPrimaryDark : colorWhite, + icon: CupertinoIcons.checkmark, + ), + const SizedBox(height: 10), + outlinedActionButton( + _session.running ? "Pause" : "Resume", + _onTogglePause, + foreground: colorWhite, + icon: _session.running + ? CupertinoIcons.pause_fill + : CupertinoIcons.play_fill, + ), + const SizedBox(height: 6), + Center( + child: textButton( + "Abandon this", + _onAbandon, + textSize: 12, + color: colorWhite.withValues(alpha: 0.45), + ), + ), + ], + ); + } + + // ── ConnectLiveTask ─────────────────────────────────────────────────────── + + @override + void onCompleted() { + _model?.showApplicationNotification( + NotificationType.success, + widget.commitment.wouldBeLate ? "Late complete" : "Done", + widget.commitment.wouldBeLate + ? "Recorded as a late complete — the window had already closed." + : "${formatClock(_session.elapsedSeconds())} of focused work, recorded.", + true, + true, + () { + Navigator.pop(context, true); + }, + ); + } + + @override + void onTooEarly(int secondsRemaining) { + _model?.showApplicationNotification( + NotificationType.warning, + "Not yet", + "${formatClock(secondsRemaining)} still to go. The timer is the proof — finishing early would just be the checkbox again.", + true, + true, + null, + ); + } + + @override + void onAbandoned() { + Navigator.pop(context, true); + } + + @override + void dispose() { + _ticker?.cancel(); + WidgetsBinding.instance.removeObserver(this); + _model?.clearSession(); + super.dispose(); + } +} diff --git a/frontend/lib/Grounded/see/live/ViewLiveTask.dart b/frontend/lib/Grounded/see/live/ViewLiveTask.dart new file mode 100644 index 0000000..6c265f4 --- /dev/null +++ b/frontend/lib/Grounded/see/live/ViewLiveTask.dart @@ -0,0 +1,75 @@ +import '../../about/external/data/Commitment.dart'; +import '../../about/external/data/LiveSession.dart'; +import '../../about/external/initial/AbandonRequest.dart'; +import '../../about/external/initial/CompletionRequest.dart'; +import '../../configs/NotificationServiceConfig.dart'; +import '../parent/ParentViewModel.dart'; +import 'ConnectLiveTask.dart'; + +class ViewLiveTask extends ParentViewModel { + ConnectLiveTask connection; + + ViewLiveTask(super.context, this.connection); + + /// Mirrors the live state into the ongoing notification, so the run is + /// visible and controllable from the lock screen. + void publishSession(LiveSession session) { + LocalNotificationEngine.showSessionNotification(session); + } + + void clearSession() { + LocalNotificationEngine.cancelSessionNotification(); + } + + /// Completion is refused until the foreground requirement is actually met. + /// The timer is the proof, so it cannot be talked past. + void complete(Commitment commitment, LiveSession session) async { + if (!session.satisfied()) { + connection.onTooEarly(session.remainingSeconds()); + return; + } + + if (!await hasNetwork(() => complete(commitment, session))) return; + + showLoading("Recording"); + + try { + await getDataManager().completeCommitmentEntry(CompletionRequest( + commitmentId: commitment.id ?? "", + proofType: commitment.proofType.name, + timerSeconds: session.elapsedSeconds(), + )); + + clearSession(); + + closeLoading(); + + connection.onCompleted(); + } catch (e) { + handleError(e, () => complete(commitment, session), () => dismissError(), + "Retry"); + } + } + + void abandon(Commitment commitment, String reason) async { + if (!await hasNetwork(() => abandon(commitment, reason))) return; + + showLoading("Recording"); + + try { + await getDataManager().abandonCommitmentEntry(AbandonRequest( + commitmentId: commitment.id ?? "", + reason: reason, + )); + + clearSession(); + + closeLoading(); + + connection.onAbandoned(); + } catch (e) { + handleError( + e, () => abandon(commitment, reason), () => dismissError(), "Retry"); + } + } +} diff --git a/frontend/lib/Grounded/see/login/ConnectLogin.dart b/frontend/lib/Grounded/see/login/ConnectLogin.dart new file mode 100644 index 0000000..3682691 --- /dev/null +++ b/frontend/lib/Grounded/see/login/ConnectLogin.dart @@ -0,0 +1,5 @@ +import '../../about/internal/application/UserDetails.dart'; + +abstract class ConnectLogin { + void onLoggedIn(UserDetails details); +} diff --git a/frontend/lib/Grounded/see/login/Login.dart b/frontend/lib/Grounded/see/login/Login.dart new file mode 100644 index 0000000..0947628 --- /dev/null +++ b/frontend/lib/Grounded/see/login/Login.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'LoginState.dart'; + +class Login extends StatefulWidget { + const Login({super.key}); + + @override + State createState() => LoginState(); +} diff --git a/frontend/lib/Grounded/see/login/LoginState.dart b/frontend/lib/Grounded/see/login/LoginState.dart new file mode 100644 index 0000000..edb6fdf --- /dev/null +++ b/frontend/lib/Grounded/see/login/LoginState.dart @@ -0,0 +1,187 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/external/initial/LoginData.dart'; +import '../../about/internal/application/NavigatorType.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../about/internal/application/UserDetails.dart'; +import '../../configs/Navigator.dart'; +import '../../designs/Responsive.dart'; +import '../../designs/buttons/Buttons.dart'; +import '../../designs/input/InputFields.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/Colors.dart'; +import '../../utils/Validators.dart'; +import '../home/Home.dart'; +import 'ConnectLogin.dart'; +import 'Login.dart'; +import 'ViewLogin.dart'; + +class LoginState extends State implements ConnectLogin { + ViewLogin? _model; + + final GlobalKey _formKey = GlobalKey(); + + final TextEditingController _username = TextEditingController(); + + final TextEditingController _password = TextEditingController(); + + bool _obscured = true; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewLogin(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => Scaffold( + backgroundColor: colorPrimaryDark, + body: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ), + ); + } + + void _initiate() {} + + void _onToggleObscured() { + setState(() { + _obscured = !_obscured; + }); + } + + void _onSignIn() { + if (_formKey.currentState?.validate() != true) { + return; + } + + _model?.login(LoginData( + username: _username.text.trim(), + password: _password.text, + )); + } + + Widget _mobileView(BoxConstraints constraints) { + return Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SafeArea( + bottom: false, + child: Padding( + padding: const EdgeInsets.fromLTRB(28, 24, 28, 32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text("GROUNDED", 10, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), + letterSpacing: 2.0), + const SizedBox(height: 22), + text("Welcome back.", 36, TextType.Light, + color: colorWhite, height: 1.1), + const SizedBox(height: 10), + text( + "Your record has been waiting exactly where you left it.", + 14, + TextType.Regular, + color: colorWhite.withValues(alpha: 0.55), + height: 1.5, + ), + ], + ), + ), + ), + Expanded( + child: Container( + decoration: BoxDecoration( + color: colorPrimaryLight, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28), + ), + ), + clipBehavior: Clip.antiAlias, + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 32, 24, 32), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + inputField( + "Username", + _username, + hint: "The one you signed up with", + validator: Validators.username, + keyboard: TextInputType.emailAddress, + icon: CupertinoIcons.person, + ), + const SizedBox(height: 20), + inputField( + "Password", + _password, + hint: "Your password", + validator: Validators.password, + obscure: _obscured, + icon: CupertinoIcons.lock, + ), + const SizedBox(height: 8), + Align( + alignment: Alignment.centerRight, + child: textButton( + _obscured ? "Show password" : "Hide password", + _onToggleObscured, + textSize: 12, + ), + ), + const SizedBox(height: 20), + roundedCornerButton( + "Sign in", + _onSignIn, + icon: CupertinoIcons.arrow_right, + ), + const SizedBox(height: 24), + Center( + child: text( + "Nothing here judges you for being away.", + 12, + TextType.Regular, + color: colorGrey2, + align: TextAlign.center, + ), + ), + ], + ), + ), + ), + ), + ), + ], + ); + } + + @override + void onLoggedIn(UserDetails details) { + GroundedNavigation() + .navigateToPage(NavigatorType.makeNewMain, const Home(), context); + } + + @override + void dispose() { + _username.dispose(); + _password.dispose(); + super.dispose(); + } +} diff --git a/frontend/lib/Grounded/see/login/ViewLogin.dart b/frontend/lib/Grounded/see/login/ViewLogin.dart new file mode 100644 index 0000000..f163deb --- /dev/null +++ b/frontend/lib/Grounded/see/login/ViewLogin.dart @@ -0,0 +1,39 @@ +import '../../about/external/initial/LoginData.dart'; +import '../../about/internal/application/MeDescription.dart'; +import '../../about/internal/application/UserDetails.dart'; +import '../parent/ParentViewModel.dart'; +import 'ConnectLogin.dart'; + +class ViewLogin extends ParentViewModel { + ConnectLogin connection; + + ViewLogin(super.context, this.connection); + + void login(LoginData request) async { + if (!await hasNetwork(() => login(request))) return; + + showLoading("Signing in"); + + try { + final response = await getDataManager().login(request); + + final Map body = response.data; + + await getDataManager().setMyDescription(MeDescription( + id: body['id'] ?? "", + name: body['name'] ?? "", + token: body['token'] ?? "", + )); + + final UserDetails details = UserDetails.fromJson(body); + + await getDataManager().setUserDetails(details); + + closeLoading(); + + connection.onLoggedIn(details); + } catch (e) { + handleError(e, () => login(request), () => dismissError(), "Retry"); + } + } +} diff --git a/frontend/lib/Grounded/see/overdue/ConnectOverdueQueue.dart b/frontend/lib/Grounded/see/overdue/ConnectOverdueQueue.dart new file mode 100644 index 0000000..86e194f --- /dev/null +++ b/frontend/lib/Grounded/see/overdue/ConnectOverdueQueue.dart @@ -0,0 +1,16 @@ +import '../../about/external/data/Commitment.dart'; +import '../../about/internal/application/Standing.dart'; + +abstract class ConnectOverdueQueue { + void onQueueLoaded(List queue, Standing standing, double debt); + + /// The item settled, with the debt it removed from the score. + void onItemCleared(Commitment item, double reliefApplied, String verb); + + /// Deferral refused — cap spent, or the class does not permit it. + void onDeferralRefused(String reason); + + void onAmnestySpent(int remaining); + + void onAmnestyRefused(); +} diff --git a/frontend/lib/Grounded/see/overdue/OverdueQueue.dart b/frontend/lib/Grounded/see/overdue/OverdueQueue.dart new file mode 100644 index 0000000..51e2c99 --- /dev/null +++ b/frontend/lib/Grounded/see/overdue/OverdueQueue.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'OverdueQueueState.dart'; + +class OverdueQueue extends StatefulWidget { + const OverdueQueue({super.key}); + + @override + State createState() => OverdueQueueState(); +} diff --git a/frontend/lib/Grounded/see/overdue/OverdueQueueState.dart b/frontend/lib/Grounded/see/overdue/OverdueQueueState.dart new file mode 100644 index 0000000..b1a228b --- /dev/null +++ b/frontend/lib/Grounded/see/overdue/OverdueQueueState.dart @@ -0,0 +1,691 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/external/data/Commitment.dart'; +import '../../about/external/initial/CompletionRequest.dart'; +import '../../about/internal/application/CommitmentClass.dart'; +import '../../about/internal/application/NotificationType.dart'; +import '../../about/internal/application/ProofType.dart'; +import '../../about/internal/application/Standing.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../designs/Component.dart'; +import '../../designs/Responsive.dart'; +import '../../designs/Shell.dart'; +import '../../designs/buttons/Buttons.dart'; +import '../../designs/input/InputFields.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/Colors.dart'; +import '../../utils/CommonUtils.dart'; +import '../../utils/DebtEngine.dart'; +import '../../utils/Thresholds.dart'; +import '../../utils/Validators.dart'; +import 'ConnectOverdueQueue.dart'; +import 'OverdueQueue.dart'; +import 'ViewOverdueQueue.dart'; + +class OverdueQueueState extends State + implements ConnectOverdueQueue { + ViewOverdueQueue? _model; + + List _queue = []; + + Standing _standing = Standing.Good; + + double _debt = 0; + + bool _changed = false; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewOverdueQueue(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ); + } + + void _initiate() { + _model?.loadQueue(); + } + + // ── Handlers ────────────────────────────────────────────────────────────── + + void _onBack() { + Navigator.pop(context, _changed); + } + + void _onComplete(Commitment item) { + // Honour proof settles immediately; everything else has to produce its + // artefact before the completion is accepted. + if (item.proofType == ProofType.Honour) { + _model?.complete( + item, + CompletionRequest( + commitmentId: item.id ?? "", + proofType: item.proofType.name, + ), + ); + return; + } + + _openProofSheet(item); + } + + void _onDefer(Commitment item) { + _openDeferralSheet(item); + } + + void _onAbandon(Commitment item) { + _openAbandonSheet(item); + } + + void _onAmnesty(Commitment item) { + _model?.spendAmnesty(item); + } + + // ── Sheets ──────────────────────────────────────────────────────────────── + + /// The deferral sheet. The excuse field is the whole point of the screen — + /// free text, minimum length, no template buttons to route around it. + void _openDeferralSheet(Commitment item) { + final TextEditingController excuse = TextEditingController(); + final GlobalKey formKey = GlobalKey(); + + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + barrierColor: colorPrimaryDark.withValues(alpha: 0.6), + builder: (BuildContext sheetContext) { + return StatefulBuilder( + builder: (BuildContext sheetContext, StateSetter setSheetState) { + final int remaining = + Thresholds.maxDeferralsPerTask - item.deferralCount; + + return Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(sheetContext).viewInsets.bottom, + ), + child: Container( + decoration: BoxDecoration( + color: colorSheetBackground, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28), + ), + ), + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 16, 24, 32), + child: Form( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + width: 36, + height: 4, + margin: const EdgeInsets.only(bottom: 20), + decoration: BoxDecoration( + color: colorGrey.withValues(alpha: 0.35), + borderRadius: BorderRadius.circular(999), + ), + ), + ), + text("DEFERRING", 9, TextType.Bold, + color: colorGrey2, letterSpacing: 1.2), + const SizedBox(height: 10), + text(item.title, 26, TextType.Light, + color: colorPrimaryDark, height: 1.2), + const SizedBox(height: 14), + text( + remaining <= 1 + ? "This is the last deferral this task gets. After it, the only options are completing or abandoning." + : "$remaining deferrals left on this task.", + 13, + TextType.Regular, + color: colorGrey2, + height: 1.5, + ), + const SizedBox(height: 24), + excuseField( + excuse, + Thresholds.minExcuseLength, + validator: Validators.excuse, + onChanged: (value) => setSheetState(() {}), + ), + const SizedBox(height: 24), + roundedCornerButton( + "Defer with this reason", + () { + if (formKey.currentState?.validate() != true) { + return; + } + Navigator.pop(sheetContext); + _model?.defer(item, excuse.text); + }, + icon: CupertinoIcons.clock, + ), + const SizedBox(height: 8), + Center( + child: textButton( + "Cancel", + () => Navigator.pop(sheetContext), + textSize: 13, + ), + ), + ], + ), + ), + ), + ), + ); + }, + ); + }, + ); + } + + void _openAbandonSheet(Commitment item) { + final TextEditingController reason = TextEditingController(); + final GlobalKey formKey = GlobalKey(); + + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + barrierColor: colorPrimaryDark.withValues(alpha: 0.6), + builder: (BuildContext sheetContext) { + return Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(sheetContext).viewInsets.bottom, + ), + child: Container( + decoration: BoxDecoration( + color: colorSheetBackground, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28), + ), + ), + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 16, 24, 32), + child: Form( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + width: 36, + height: 4, + margin: const EdgeInsets.only(bottom: 20), + decoration: BoxDecoration( + color: colorGrey.withValues(alpha: 0.35), + borderRadius: BorderRadius.circular(999), + ), + ), + ), + text("ABANDONING", 9, TextType.Bold, + color: colorStandingLockdown, letterSpacing: 1.2), + const SizedBox(height: 10), + text(item.title, 26, TextType.Light, + color: colorPrimaryDark, height: 1.2), + const SizedBox(height: 14), + card( + background: colorStandingLockdownBg, + borderColor: + colorStandingLockdown.withValues(alpha: 0.20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text("THE COST", 9, TextType.Bold, + color: colorStandingLockdown, letterSpacing: 1.2), + const SizedBox(height: 8), + text( + "Abandoning is the most expensive outcome there is. It costs double weight and will not decay for 30 days.", + 13, + TextType.Regular, + color: colorPrimaryDark, + height: 1.5, + ), + ], + ), + ), + const SizedBox(height: 20), + inputField( + "Reason", + reason, + hint: "Why is this never happening?", + validator: Validators.excuse, + maxLines: 3, + ), + const SizedBox(height: 24), + destructiveButton( + "Abandon permanently", + () { + if (formKey.currentState?.validate() != true) { + return; + } + Navigator.pop(sheetContext); + _model?.abandon(item, reason.text); + }, + icon: CupertinoIcons.xmark_circle, + ), + const SizedBox(height: 8), + Center( + child: textButton( + "Keep it", + () => Navigator.pop(sheetContext), + textSize: 13, + ), + ), + ], + ), + ), + ), + ), + ); + }, + ); + } + + /// Proof types other than Honour need their artefact. The sheet states what + /// is required rather than letting the user tap a checkbox and move on. + void _openProofSheet(Commitment item) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + barrierColor: colorPrimaryDark.withValues(alpha: 0.6), + builder: (BuildContext sheetContext) { + return Container( + decoration: BoxDecoration( + color: colorSheetBackground, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28), + ), + ), + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 16, 24, 32), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + width: 36, + height: 4, + margin: const EdgeInsets.only(bottom: 20), + decoration: BoxDecoration( + color: colorGrey.withValues(alpha: 0.35), + borderRadius: BorderRadius.circular(999), + ), + ), + ), + text("PROOF REQUIRED", 9, TextType.Bold, + color: colorGrey2, letterSpacing: 1.2), + const SizedBox(height: 10), + text(proofLabel(item.proofType), 26, TextType.Light, + color: colorPrimaryDark, height: 1.2), + const SizedBox(height: 14), + text( + _proofDescription(item), + 14, + TextType.Regular, + color: colorGrey2, + height: 1.55, + ), + const SizedBox(height: 24), + roundedCornerButton( + _proofAction(item.proofType), + () { + Navigator.pop(sheetContext); + _model?.complete( + item, + CompletionRequest( + commitmentId: item.id ?? "", + proofType: item.proofType.name, + ), + ); + }, + icon: _proofIcon(item.proofType), + ), + const SizedBox(height: 8), + Center( + child: textButton( + "Not now", + () => Navigator.pop(sheetContext), + textSize: 13, + ), + ), + ], + ), + ), + ); + }, + ); + } + + String _proofDescription(Commitment item) { + switch (item.proofType) { + case ProofType.Honour: + return "Your word is enough for this one."; + case ProofType.Photo: + return "Camera only — no gallery imports. The timestamp is embedded and near-duplicate submissions are flagged."; + case ProofType.Timer: + return "A foreground session of at least ${item.proofTimerMinutes} minutes. Leaving the app pauses the clock."; + case ProofType.Location: + return "You need to have actually been there. Dwell time inside the geofence counts, passing by does not."; + case ProofType.Health: + return "Your health platform has to confirm a workout inside the window."; + case ProofType.Witness: + return "Your accountability partner confirms this one."; + } + } + + String _proofAction(ProofType type) { + switch (type) { + case ProofType.Honour: + return "Mark complete"; + case ProofType.Photo: + return "Open camera"; + case ProofType.Timer: + return "Start the timer"; + case ProofType.Location: + return "Check my location"; + case ProofType.Health: + return "Check health data"; + case ProofType.Witness: + return "Request confirmation"; + } + } + + IconData _proofIcon(ProofType type) { + switch (type) { + case ProofType.Honour: + return CupertinoIcons.checkmark; + case ProofType.Photo: + return CupertinoIcons.camera_fill; + case ProofType.Timer: + return CupertinoIcons.timer; + case ProofType.Location: + return CupertinoIcons.location_fill; + case ProofType.Health: + return CupertinoIcons.heart_fill; + case ProofType.Witness: + return CupertinoIcons.person_2_fill; + } + } + + // ── Views ───────────────────────────────────────────────────────────────── + + Widget _mobileView(BoxConstraints constraints) { + return Sheet( + eyebrow: "Outstanding", + title: "What you owe", + chrome: _queue.isEmpty ? colorPrimaryDark : standingColor(_standing), + onBack: _onBack, + banner: _debtBanner(), + child: _queue.isEmpty + ? emptyState( + CupertinoIcons.checkmark_seal, + "Nothing outstanding", + "The queue is empty. Your standing recovers as the remaining debt decays.", + accent: colorPositive, + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + sectionBreak( + "The queue", + caption: "oldest first", + trailing: pill( + "${_queue.length} / ${Thresholds.maxOpenOverdue}", + _queue.length >= Thresholds.maxOpenOverdue + ? colorStandingGrounded + : colorGrey2, + _queue.length >= Thresholds.maxOpenOverdue + ? colorStandingGroundedBg + : colorMuted, + textSize: 9, + ), + ), + ..._queue.map(_queueRow), + ], + ), + ); + } + + Widget _debtBanner() { + return Container( + padding: const EdgeInsets.fromLTRB(16, 14, 16, 14), + decoration: BoxDecoration( + color: colorWhite.withValues(alpha: 0.10), + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text("TOTAL DEBT", 9, TextType.Bold, + color: colorWhite.withValues(alpha: 0.55), + letterSpacing: 1.2), + const SizedBox(height: 6), + text(formatDebt(_debt), 32, TextType.Light, color: colorWhite), + ], + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + text("STANDING", 9, TextType.Bold, + color: colorWhite.withValues(alpha: 0.55), + letterSpacing: 1.2), + const SizedBox(height: 6), + text(standingLabel(_standing), 15, TextType.Medium, + color: colorWhite), + ], + ), + ], + ), + ); + } + + Widget _queueRow(Commitment item) { + final Color accent = classColor(item.commitmentClass); + + final bool deferrable = + item.deferrableUnder(Thresholds.maxDeferralsPerTask); + + return Container( + margin: const EdgeInsets.only(bottom: 12), + child: card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text(item.title, 17, TextType.Medium, + color: colorPrimaryDark, maxLines: 2, + overflow: TextOverflow.ellipsis), + const SizedBox(height: 8), + text(formatWindow(item), 11, TextType.Regular, + color: colorGrey2), + ], + ), + ), + const SizedBox(width: 10), + pill( + classLabel(item.commitmentClass), + accent, + classBackground(item.commitmentClass), + textSize: 9, + ), + ], + ), + const SizedBox(height: 16), + Row( + children: [ + Expanded( + child: labelled( + "Overdue", + overdueLabel(item), + valueSize: 13, + valueColor: colorStandingGrounded, + ), + ), + Expanded( + child: labelled( + "Costing", + formatDebt(DebtEngine.commitmentDebt(item)), + valueSize: 13, + ), + ), + Expanded( + child: labelled( + "Deferred", + "${item.deferralCount}/${Thresholds.maxDeferralsPerTask}", + valueSize: 13, + valueColor: + deferrable ? colorPrimaryDark : colorStandingGrounded, + ), + ), + ], + ), + hairline(margin: const EdgeInsets.symmetric(vertical: 16)), + Row( + children: [ + Expanded( + child: roundedCornerButton( + "Complete", + () => _onComplete(item), + icon: CupertinoIcons.checkmark, + verticalPadding: 13, + ), + ), + const SizedBox(width: 8), + Expanded( + child: outlinedActionButton( + deferrable ? "Defer" : "No deferrals", + () => _onDefer(item), + enabled: deferrable && + item.commitmentClass != CommitmentClass.NonNegotiable, + icon: CupertinoIcons.clock, + ), + ), + ], + ), + const SizedBox(height: 6), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + textButton("Amnesty", () => _onAmnesty(item), + textSize: 12, color: colorGrey2), + textButton("Abandon", () => _onAbandon(item), + textSize: 12, color: colorStandingLockdown), + ], + ), + ], + ), + ), + ); + } + + // ── ConnectOverdueQueue ─────────────────────────────────────────────────── + + @override + void onQueueLoaded(List queue, Standing standing, double debt) { + setState(() { + _queue = queue; + _standing = standing; + _debt = debt; + }); + } + + @override + void onItemCleared(Commitment item, double reliefApplied, String verb) { + _changed = true; + + _model?.showApplicationNotification( + verb == "Abandoned" + ? NotificationType.warning + : NotificationType.success, + verb, + verb == "Late complete" + ? "Recorded as a late complete. It reduces the debt but does not clear it — the miss stays in your history." + : reliefApplied > 0 + ? "${formatDebt(reliefApplied)} came off your debt." + : "Recorded.", + true, + true, + null, + ); + } + + @override + void onDeferralRefused(String reason) { + _model?.showApplicationNotification( + NotificationType.warning, + "Not deferrable", + reason, + true, + true, + null, + ); + } + + @override + void onAmnestySpent(int remaining) { + _changed = true; + + _model?.showApplicationNotification( + NotificationType.success, + "Amnesty applied", + "No questions asked. $remaining token${remaining == 1 ? "" : "s"} left this month.", + true, + true, + null, + ); + } + + @override + void onAmnestyRefused() { + _model?.showApplicationNotification( + NotificationType.info, + "No tokens left", + "You have spent this month's amnesty. They reset at the start of next month.", + true, + true, + null, + ); + } +} diff --git a/frontend/lib/Grounded/see/overdue/ViewOverdueQueue.dart b/frontend/lib/Grounded/see/overdue/ViewOverdueQueue.dart new file mode 100644 index 0000000..2a9d949 --- /dev/null +++ b/frontend/lib/Grounded/see/overdue/ViewOverdueQueue.dart @@ -0,0 +1,190 @@ +import '../../about/external/data/Commitment.dart'; +import '../../about/external/data/pages/request/HistoryRequest.dart'; +import '../../about/external/data/pages/request/PageAndSort.dart'; +import '../../about/external/data/pages/request/Pageable.dart'; +import '../../about/external/data/pages/request/Sort.dart'; +import '../../about/external/data/pages/response/CommitmentPage.dart'; +import '../../about/external/initial/AbandonRequest.dart'; +import '../../about/external/initial/AmnestyRequest.dart'; +import '../../about/external/initial/CompletionRequest.dart'; +import '../../about/external/initial/DeferralRequest.dart'; +import '../../about/internal/application/CommitmentClass.dart'; +import '../../about/internal/application/Standing.dart'; +import '../../about/internal/application/UserDetails.dart'; +import '../../utils/DebtEngine.dart'; +import '../../utils/GuardrailEngine.dart'; +import '../../utils/StandingEngine.dart'; +import '../../utils/Thresholds.dart'; +import '../../utils/ToneEngine.dart'; +import '../parent/ParentViewModel.dart'; +import 'ConnectOverdueQueue.dart'; + +class ViewOverdueQueue extends ParentViewModel { + ConnectOverdueQueue connection; + + ViewOverdueQueue(super.context, this.connection); + + void loadQueue() async { + if (!await hasNetwork(() => loadQueue())) return; + + showLoading("Loading what you owe"); + + try { + final response = await getDataManager().getOverdueQueue(HistoryRequest( + query: PageAndSort( + sort: Sort('desc', 'dueEnd'), + page: Pageable(0, 0, 50, 0), + ), + )); + + final CommitmentPage page = CommitmentPage.fromJson(response.data); + + final UserDetails details = await getDataManager().getUserDetails(); + + final double debt = DebtEngine.totalDebt(page.content); + + final Standing standing = StandingEngine.evaluate( + debt, + missedNonNegotiables: DebtEngine.missedNonNegotiables(page.content), + sickMode: details.sickMode, + ); + + await getDataManager().setCachedDebtScore(debt); + + closeLoading(); + + connection.onQueueLoaded(page.content, standing, debt); + } catch (e) { + handleError(e, () => loadQueue(), () => dismissError(), "Retry"); + } + } + + /// Completion outside the due window is recorded as a late complete, not a + /// complete. The server decides which — the client never claims one. + void complete(Commitment item, CompletionRequest request) async { + if (!await hasNetwork(() => complete(item, request))) return; + + showLoading("Recording"); + + try { + await getDataManager().completeCommitmentEntry(request); + + closeLoading(); + + connection.onItemCleared( + item, + DebtEngine.reliefFromClearing(item), + item.wouldBeLate ? "Late complete" : "Complete", + ); + + loadQueue(); + } catch (e) { + handleError( + e, () => complete(item, request), () => dismissError(), "Retry"); + } + } + + /// The deferral gate. Non-negotiables never defer; everything else runs out + /// of deferrals, after which the only routes left are completing or + /// abandoning. + void defer(Commitment item, String excuse) async { + if (item.commitmentClass == CommitmentClass.NonNegotiable) { + connection.onDeferralRefused(ToneEngine.nonNegotiableRefused()); + return; + } + + if (!item.deferrableUnder(Thresholds.maxDeferralsPerTask)) { + connection.onDeferralRefused( + ToneEngine.deferralRefused(Thresholds.maxDeferralsPerTask)); + return; + } + + if (excuse.trim().length < Thresholds.minExcuseLength) { + connection + .onDeferralRefused(ToneEngine.excuseTooShort(Thresholds.minExcuseLength)); + return; + } + + if (!await hasNetwork(() => defer(item, excuse))) return; + + showLoading("Recording the deferral"); + + try { + // The new window opens tomorrow at the same time — a deferral moves the + // window, it never removes it. + final DateTime start = + (item.dueStart ?? DateTime.now()).add(const Duration(days: 1)); + final DateTime end = + (item.dueEnd ?? DateTime.now()).add(const Duration(days: 1)); + + await getDataManager().deferCommitmentEntry(DeferralRequest( + commitmentId: item.id ?? "", + excuseText: excuse.trim(), + newDueStart: start.toIso8601String(), + newDueEnd: end.toIso8601String(), + )); + + closeLoading(); + + connection.onItemCleared(item, 0, "Deferred"); + + loadQueue(); + } catch (e) { + handleError(e, () => defer(item, excuse), () => dismissError(), "Retry"); + } + } + + /// Abandoning costs the most debt of all, and resists decay for a month. + void abandon(Commitment item, String reason) async { + if (!await hasNetwork(() => abandon(item, reason))) return; + + showLoading("Recording"); + + try { + await getDataManager().abandonCommitmentEntry(AbandonRequest( + commitmentId: item.id ?? "", + reason: reason.trim(), + )); + + closeLoading(); + + connection.onItemCleared(item, 0, "Abandoned"); + + loadQueue(); + } catch (e) { + handleError( + e, () => abandon(item, reason), () => dismissError(), "Retry"); + } + } + + /// Rationed, so a bad flu does not destroy three months of progress. + void spendAmnesty(Commitment item) async { + final int spent = await getDataManager().getAmnestySpent(); + + if (!GuardrailEngine.canSpendAmnesty( + Thresholds.amnestyTokensPerMonth, spent)) { + connection.onAmnestyRefused(); + return; + } + + if (!await hasNetwork(() => spendAmnesty(item))) return; + + showLoading("Applying amnesty"); + + try { + await getDataManager() + .spendAmnestyToken(AmnestyRequest(commitmentId: item.id ?? "")); + + await getDataManager().setAmnestySpent(spent + 1); + + closeLoading(); + + connection.onAmnestySpent(GuardrailEngine.tokensRemaining( + Thresholds.amnestyTokensPerMonth, spent + 1)); + + loadQueue(); + } catch (e) { + handleError(e, () => spendAmnesty(item), () => dismissError(), "Retry"); + } + } +} diff --git a/frontend/lib/Grounded/see/parent/ParentViewModel.dart b/frontend/lib/Grounded/see/parent/ParentViewModel.dart new file mode 100644 index 0000000..93288b5 --- /dev/null +++ b/frontend/lib/Grounded/see/parent/ParentViewModel.dart @@ -0,0 +1,652 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../about/external/data/GroundedError.dart'; +import '../../about/external/data/Severity.dart'; +import '../../about/internal/application/NavigatorType.dart'; +import '../../about/internal/application/NotificationType.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../about/internal/file/FileStorage.dart'; +import '../../comms/Comms.dart'; +import '../../configs/Navigator.dart'; +import '../../designs/Component.dart'; +import '../../designs/buttons/Buttons.dart'; +import '../../designs/text/Text.dart'; +import '../../informatics/AppDataManager.dart'; +import '../../informatics/DataManager.dart'; +import '../../memory/InternalMemory.dart'; +import '../../utils/Colors.dart'; +import '../system/sessionexpired/SessionExpired.dart'; +import '../system/updateme/UpdateMe.dart'; + +/// Everything cross-cutting lives here: the single data gateway, the loading, +/// network and error overlays, and the error decision tree that lets the +/// backend steer the client. +class ParentViewModel extends ChangeNotifier { + OverlayEntry? loadingEntry; + + OverlayEntry? networkEntry; + + OverlayEntry? errorEntry; + + late DataManager dataManager; + + BuildContext context; + + OverlayState? overlayState; + + ParentViewModel(this.context) { + overlayState = Overlay.of(context); + dataManager = + AppDataManager(InternalMemory(), Comms(InternalMemory()), FileStorage()); + } + + DataManager getDataManager() { + return dataManager; + } + + // ── Loading ─────────────────────────────────────────────────────────────── + + void showLoading(String loadingText) async { + if (loadingEntry == null) { + _hideKeyboard(); + loadingEntry = OverlayEntry(builder: (context) { + return Scaffold( + backgroundColor: colorPrimaryDark, + body: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 28), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Align( + alignment: Alignment.centerLeft, + child: text( + "GROUNDED", + 10, + TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), + letterSpacing: 2.0, + ), + ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: 46, + height: 46, + child: CircularProgressIndicator( + strokeWidth: 2.5, + backgroundColor: colorWhite.withValues(alpha: 0.12), + color: colorWhite, + ), + ), + const SizedBox(height: 32), + text( + loadingText, + 30, + TextType.Light, + color: colorWhite, + align: TextAlign.center, + height: 1.15, + ), + ], + ), + text( + "Keep your connection active", + 12, + TextType.Regular, + color: colorWhite.withValues(alpha: 0.45), + ), + ], + ), + ), + ), + ); + }); + + overlayState?.insert(loadingEntry!); + notifyListeners(); + } + } + + closeLoading() { + if (loadingEntry != null) { + loadingEntry?.remove(); + loadingEntry = null; + notifyListeners(); + } + } + + // ── Network ─────────────────────────────────────────────────────────────── + + void noNetwork(Function() actions) async { + if (networkEntry == null) { + _hideKeyboard(); + networkEntry = OverlayEntry(builder: (context) { + return Scaffold( + backgroundColor: colorPrimaryLight, + body: SafeArea( + child: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(28), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: colorPrimaryDark, + borderRadius: BorderRadius.circular(18), + ), + child: Icon(CupertinoIcons.wifi_slash, + size: 26, color: colorWhite), + ), + const SizedBox(height: 28), + text("CONNECTION", 10, TextType.Bold, + color: colorGrey2, letterSpacing: 1.2), + const SizedBox(height: 10), + text("You are offline.", 34, TextType.Light, + color: colorPrimaryDark, height: 1.15), + const SizedBox(height: 12), + text( + "Reconnect and we will pick up where you left off. Nothing has been lost.", + 14, + TextType.Regular, + color: colorGrey2, + height: 1.5, + ), + const SizedBox(height: 32), + SizedBox( + width: double.infinity, + child: roundedCornerButton( + "Try again", + actions, + icon: CupertinoIcons.refresh, + ), + ), + ], + ), + ), + ), + ), + ); + }); + + overlayState?.insert(networkEntry!); + notifyListeners(); + } + } + + closeNetwork() { + if (networkEntry != null) { + networkEntry?.remove(); + networkEntry = null; + notifyListeners(); + } + } + + /// Guard every network call with this. Returns false and raises the offline + /// overlay wired to [actions] when there is nothing to talk to. + Future hasNetwork(Function() actions) async { + closeLoading(); + + List resultList = + await (Connectivity().checkConnectivity()); + + ConnectivityResult connectivityResult = ConnectivityResult.none; + + if (resultList.isNotEmpty) { + connectivityResult = resultList.first; + } + + if (connectivityResult == ConnectivityResult.mobile || + connectivityResult == ConnectivityResult.wifi || + connectivityResult == ConnectivityResult.ethernet) { + closeNetwork(); + return true; + } + + noNetwork(actions); + return false; + } + + // ── Errors ──────────────────────────────────────────────────────────────── + + /// The standard catch handler. Session expiry and the server-directed + /// redirects live here, so the backend can steer the client from any screen. + handleError(Object? error, Function() actions, Function() closeAction, + String buttonName) { + closeLoading(); + + if (error is DioException) { + DioException dioError = error; + + if (error.type == DioExceptionType.connectionTimeout) { + showError( + actions, + closeAction, + GroundedError( + code: 5000.01, + message: "An error occurred while processing your request.", + helper: + "Kindly ensure that you have a stable internet connection.", + title: "Grounded Error", + severity: Severity.message.name), + buttonName); + } else if (error.type == DioExceptionType.receiveTimeout) { + showError( + actions, + closeAction, + GroundedError( + code: 5000.02, + message: "An error occurred while processing your request.", + helper: + "Kindly ensure that you have a stable internet connection.", + title: "Grounded Error", + severity: Severity.message.name), + buttonName); + } else if (dioError.response?.statusCode == 401) { + sessionExpired(); + } else if (dioError.response?.statusCode == 403) { + showError( + actions, + closeAction, + GroundedError( + code: 5100.00, + message: + "A connection error occurred while processing your request. Usually a result of your network security blocking the request.", + helper: + "Try using your mobile data or switching to a different network.", + title: "Grounded Error", + severity: Severity.message.name), + buttonName); + } else if (dioError.response?.statusCode == 413) { + showError(actions, closeAction, + getGroundedError(dioError.response?.data), buttonName); + } else if (_isBusinessStatus(dioError.response?.statusCode)) { + _handleBusinessError(dioError, actions, closeAction, buttonName); + } else { + showError( + actions, + closeAction, + GroundedError( + code: 5500.02, + message: "An error occurred while processing your request.", + helper: + "Kindly relaunch the application and try again. If the problem persists, contact us.", + title: "Grounded Error", + severity: Severity.message.name), + buttonName); + } + } else if (error is Exception) { + showError( + actions, + closeAction, + GroundedError( + code: 6000.01, + message: "An error occurred while processing your request.", + helper: + "Kindly relaunch the application and try again. If the problem persists, contact us.", + title: "Grounded Error", + severity: Severity.message.name), + buttonName); + } else if (error is int) { + showError( + actions, + closeAction, + GroundedError( + code: 6000.02, + message: "An error occurred while processing your request.", + helper: + "Kindly relaunch or reinstall the application and retry below.", + title: "Grounded Error", + severity: Severity.message.name), + buttonName); + } else { + showError( + actions, + closeAction, + GroundedError( + code: 8700.02, + message: "An error occurred while processing your request.", + helper: + "Kindly relaunch the application and try again. If the problem persists, contact us.", + title: "Grounded Error", + severity: Severity.message.name), + buttonName); + } + } + + /// Statuses that carry an actionable error body the user should see. 500 is + /// included for services not yet migrated to the per-status scheme. + bool _isBusinessStatus(int? status) { + return status == 400 || + status == 404 || + status == 409 || + status == 422 || + status == 429 || + status == 500; + } + + /// Decodes the body and routes the app-flow control codes before falling + /// back to showing the error. + void _handleBusinessError(DioException dioError, Function() actions, + Function() closeAction, String buttonName) { + GroundedError error = getGroundedError(dioError.response?.data); + + if (error.code == 5000.901) { + updateMe(); + } else { + showError(actions, closeAction, error, buttonName); + } + } + + void showError(Function() actions, Function() closeActions, + GroundedError error, String buttonText) async { + if (errorEntry == null) { + _hideKeyboard(); + errorEntry = OverlayEntry(builder: (context) { + return Scaffold( + backgroundColor: colorPrimaryLight, + body: SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 48, 24, 32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text("SYSTEM NOTICE", 10, TextType.Bold, + color: colorGrey2, letterSpacing: 1.2), + const SizedBox(height: 12), + text(error.title, 34, TextType.Light, + color: colorPrimaryDark, height: 1.15), + const SizedBox(height: 24), + card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 44, + height: 44, + alignment: Alignment.center, + decoration: BoxDecoration( + color: colorStandingGroundedBg, + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + CupertinoIcons.exclamationmark_triangle_fill, + size: 20, + color: colorStandingGrounded, + ), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text("CODE", 9, TextType.Bold, + color: colorGrey2, letterSpacing: 1.0), + const SizedBox(height: 4), + text(error.code.toString(), 17, + TextType.Medium, + color: colorPrimaryDark), + ], + ), + ), + ], + ), + hairline( + margin: const EdgeInsets.symmetric(vertical: 20)), + text("WHAT HAPPENED", 9, TextType.Bold, + color: colorGrey2, letterSpacing: 1.0), + const SizedBox(height: 6), + text(error.message, 14, TextType.Regular, + color: colorPrimaryDark, height: 1.5), + const SizedBox(height: 20), + text("WHAT TO DO", 9, TextType.Bold, + color: colorGrey2, letterSpacing: 1.0), + const SizedBox(height: 6), + text(error.helper, 14, TextType.Regular, + color: colorPrimaryDark, height: 1.5), + ], + ), + ), + const SizedBox(height: 28), + SizedBox( + width: double.infinity, + child: roundedCornerButton( + buttonText, + actions, + icon: CupertinoIcons.arrow_clockwise, + ), + ), + const SizedBox(height: 10), + SizedBox( + width: double.infinity, + child: outlinedActionButton("Close", () { + dismissError(); + closeActions(); + }), + ), + ], + ), + ), + ), + ); + }); + + overlayState?.insert(errorEntry!); + notifyListeners(); + } + } + + dismissError() { + if (errorEntry != null) { + errorEntry?.remove(); + errorEntry = null; + notifyListeners(); + } + } + + GroundedError getGroundedError(data) { + GroundedError error; + try { + error = GroundedError.fromJson(data); + } catch (e) { + error = GroundedError( + code: 900, + message: "An error occurred while processing your request.", + helper: "Kindly ensure that your internet connection is working.", + title: "Grounded Error", + severity: Severity.message.name); + } + + return error; + } + + // ── In-app notification ─────────────────────────────────────────────────── + + showApplicationNotification( + NotificationType type, + String title, + String description, + bool enableDrag, + bool barrierDismiss, + VoidCallback? closeAction, { + String? action, + VoidCallback? positiveAction, + }) async { + Color actionColor; + Color actionBgColor; + IconData actionIconData; + + switch (type) { + case NotificationType.success: + actionIconData = Icons.check_circle_rounded; + actionColor = colorPositive; + actionBgColor = colorStandingGoodBg; + break; + case NotificationType.info: + actionIconData = Icons.info_rounded; + actionColor = colorPrimaryDark; + actionBgColor = colorMuted; + break; + case NotificationType.warning: + actionIconData = Icons.warning_amber_rounded; + actionColor = colorStandingWarned; + actionBgColor = colorStandingWarnedBg; + break; + case NotificationType.error: + actionIconData = Icons.error_rounded; + actionColor = colorStandingGrounded; + actionBgColor = colorStandingGroundedBg; + break; + } + + final bool hasAction = action != null && positiveAction != null; + + await showModalBottomSheet( + context: context, + barrierColor: colorPrimaryDark.withValues(alpha: 0.6), + backgroundColor: Colors.transparent, + elevation: 0, + enableDrag: enableDrag, + isDismissible: barrierDismiss, + isScrollControlled: true, + builder: (BuildContext context) { + return Container( + decoration: BoxDecoration( + color: colorSheetBackground, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + width: 36, + height: 4, + margin: const EdgeInsets.only(top: 12, bottom: 8), + decoration: BoxDecoration( + color: colorGrey.withValues(alpha: 0.35), + borderRadius: BorderRadius.circular(999), + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(24, 12, 24, 32), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 52, + height: 52, + alignment: Alignment.center, + decoration: BoxDecoration( + color: actionBgColor, + borderRadius: BorderRadius.circular(15), + ), + child: Icon(actionIconData, + size: 24, color: actionColor), + ), + iconButton( + Icon(Icons.close_rounded, + size: 17, color: colorGrey2), + () { + Navigator.pop(context); + closeAction?.call(); + }, + bordered: true, + ), + ], + ), + const SizedBox(height: 20), + text(type.name.toUpperCase(), 9, TextType.Bold, + color: actionColor, letterSpacing: 1.2), + const SizedBox(height: 10), + text(title, 26, TextType.Light, + color: colorPrimaryDark, height: 1.2), + const SizedBox(height: 12), + text(description, 14, TextType.Regular, + color: colorGrey2, height: 1.55), + const SizedBox(height: 28), + if (hasAction) ...[ + SizedBox( + width: double.infinity, + child: roundedCornerButton( + action, + positiveAction, + background: actionColor, + icon: actionIconData, + ), + ), + const SizedBox(height: 10), + ], + Center( + child: textButton( + hasAction ? "Cancel" : "Dismiss", + () { + Navigator.pop(context); + closeAction?.call(); + }, + color: colorGrey2, + textSize: 13, + ), + ), + ], + ), + ), + ], + ), + ); + }, + ); + } + + // ── Server-directed navigation ──────────────────────────────────────────── + + void sessionExpired() { + GroundedNavigation().navigateToPage( + NavigatorType.makeNewMain, const SessionExpired(), context); + } + + void updateMe() { + GroundedNavigation() + .navigateToPage(NavigatorType.makeNewMain, const UpdateMe(), context); + } + + _hideKeyboard() { + SystemChannels.textInput.invokeMethod('TextInput.hide'); + } + + @override + void dispose() { + closeLoading(); + closeNetwork(); + dismissError(); + super.dispose(); + } +} diff --git a/frontend/lib/Grounded/see/reportcard/ConnectReportCardScreen.dart b/frontend/lib/Grounded/see/reportcard/ConnectReportCardScreen.dart new file mode 100644 index 0000000..b1aa21b --- /dev/null +++ b/frontend/lib/Grounded/see/reportcard/ConnectReportCardScreen.dart @@ -0,0 +1,5 @@ +import '../../about/external/data/ReportCard.dart'; + +abstract class ConnectReportCardScreen { + void onReportLoaded(ReportCard report); +} diff --git a/frontend/lib/Grounded/see/reportcard/ReportCardScreen.dart b/frontend/lib/Grounded/see/reportcard/ReportCardScreen.dart new file mode 100644 index 0000000..f14375e --- /dev/null +++ b/frontend/lib/Grounded/see/reportcard/ReportCardScreen.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'ReportCardScreenState.dart'; + +class ReportCardScreen extends StatefulWidget { + const ReportCardScreen({super.key}); + + @override + State createState() => ReportCardScreenState(); +} diff --git a/frontend/lib/Grounded/see/reportcard/ReportCardScreenState.dart b/frontend/lib/Grounded/see/reportcard/ReportCardScreenState.dart new file mode 100644 index 0000000..0b82b0b --- /dev/null +++ b/frontend/lib/Grounded/see/reportcard/ReportCardScreenState.dart @@ -0,0 +1,374 @@ +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/external/data/ReportCard.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../designs/Component.dart'; +import '../../designs/Responsive.dart'; +import '../../designs/Shell.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/Colors.dart'; +import '../../utils/CommonUtils.dart'; +import 'ConnectReportCardScreen.dart'; +import 'ReportCardScreen.dart'; +import 'ViewReportCardScreen.dart'; + +class ReportCardScreenState extends State + implements ConnectReportCardScreen { + ViewReportCardScreen? _model; + + ReportCard _report = ReportCard(); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewReportCardScreen(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ); + } + + void _initiate() { + _model?.loadReport(); + } + + void _onBack() { + Navigator.pop(context); + } + + Widget _mobileView(BoxConstraints constraints) { + return Sheet( + eyebrow: "This week", + title: "Report card", + onBack: _onBack, + banner: _gradeBanner(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + if (_report.praise.isNotEmpty) ...[ + _praiseCard(), + const SizedBox(height: 24), + ], + _assignedAction(), + const SizedBox(height: 28), + sectionBreak("Debt", caption: "across the week"), + _debtTrend(), + const SizedBox(height: 28), + sectionBreak("Completion", caption: "by class"), + ..._report.completionByClass.entries.map(_completionRow), + const SizedBox(height: 20), + _worstHour(), + const SizedBox(height: 28), + if (_report.deferralLeaderboard.isNotEmpty) ...[ + sectionBreak("Most dodged", caption: "the leaderboard"), + ..._report.deferralLeaderboard.take(5).map(_deferralRow), + const SizedBox(height: 28), + ], + if (_report.estimationAccuracy.isNotEmpty) ...[ + sectionBreak("Your estimates", caption: "against reality"), + ..._report.estimationAccuracy.entries.map(_estimationRow), + const SizedBox(height: 28), + ], + if (_report.trainingAdherence > 0) ...[ + sectionBreak("Training"), + _trainingCard(), + ], + ], + ), + ); + } + + /// The grade sits in the chrome. Cosmetic, but it is the thing people + /// actually react to. + Widget _gradeBanner() { + return Container( + padding: const EdgeInsets.fromLTRB(16, 14, 16, 14), + decoration: BoxDecoration( + color: colorWhite.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text("PERIOD", 9, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), + letterSpacing: 1.2), + const SizedBox(height: 6), + text( + "${formatDate(_report.periodStart)} — ${formatDate(_report.periodEnd)}", + 13, + TextType.Medium, + color: colorWhite, + ), + ], + ), + ), + if (_report.grade.isNotEmpty) + Container( + width: 54, + height: 54, + alignment: Alignment.center, + decoration: BoxDecoration( + color: colorWhite, + borderRadius: BorderRadius.circular(16), + ), + child: text(_report.grade, 26, TextType.Light, + color: colorPrimaryDark), + ), + ], + ), + ); + } + + /// Rationed but real. Only rendered when something specific was earned. + Widget _praiseCard() { + return card( + background: colorStandingGoodBg, + borderColor: colorPositive.withValues(alpha: 0.20), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(CupertinoIcons.checkmark_seal_fill, + size: 18, color: colorPositive), + const SizedBox(width: 12), + Expanded( + child: text(_report.praise, 14, TextType.Regular, + color: colorPrimaryDark, height: 1.55), + ), + ], + ), + ); + } + + /// One assigned action for next week. Not five. + Widget _assignedAction() { + return card( + background: colorPrimaryDark, + borderColor: colorPrimaryDark, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text("NEXT WEEK, ONE THING", 9, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), letterSpacing: 1.2), + const SizedBox(height: 12), + text( + _report.assignedAction.isEmpty + ? "Not enough history yet to assign anything." + : _report.assignedAction, + 19, + TextType.Light, + color: colorWhite, + height: 1.4, + ), + ], + ), + ); + } + + Widget _debtTrend() { + if (_report.debtTrend.isEmpty) { + return card( + child: text("No debt recorded this week.", 13, TextType.Regular, + color: colorGrey2), + ); + } + + final List spots = []; + for (int index = 0; index < _report.debtTrend.length; index++) { + spots.add(FlSpot(index.toDouble(), _report.debtTrend[index])); + } + + return card( + padding: const EdgeInsets.fromLTRB(8, 20, 16, 8), + child: SizedBox( + height: 150, + child: LineChart( + LineChartData( + gridData: FlGridData( + show: true, + drawVerticalLine: false, + getDrawingHorizontalLine: (value) => + const FlLine(color: colorChartGrid, strokeWidth: 1), + ), + titlesData: FlTitlesData( + topTitles: + const AxisTitles(sideTitles: SideTitles(showTitles: false)), + rightTitles: + const AxisTitles(sideTitles: SideTitles(showTitles: false)), + leftTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: true, reservedSize: 32)), + bottomTitles: + const AxisTitles(sideTitles: SideTitles(showTitles: false)), + ), + borderData: FlBorderData(show: false), + lineBarsData: [ + LineChartBarData( + spots: spots, + isCurved: true, + barWidth: 2.5, + color: colorDebtLine, + dotData: const FlDotData(show: false), + belowBarData: BarAreaData(show: true, color: colorDebtFill), + ), + ], + ), + ), + ), + ); + } + + Widget _completionRow(MapEntry entry) { + return Container( + margin: const EdgeInsets.only(bottom: 14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + text(entry.key, 13, TextType.Medium, color: colorPrimaryDark), + text("${(entry.value * 100).round()}%", 13, TextType.Bold, + color: colorGrey2), + ], + ), + const SizedBox(height: 8), + meter( + entry.value, + fill: entry.value >= 0.8 + ? colorPositive + : entry.value >= 0.5 + ? colorStandingWarned + : colorStandingGrounded, + ), + ], + ), + ); + } + + /// The recurring window where things go to die. + Widget _worstHour() { + if (_report.worstHour < 0) { + return const SizedBox.shrink(); + } + + return card( + background: colorStandingWarnedBg, + borderColor: colorStandingWarned.withValues(alpha: 0.20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text("YOUR WORST HOUR", 9, TextType.Bold, + color: colorStandingWarned, letterSpacing: 1.2), + const SizedBox(height: 10), + text(hourLabel(_report.worstHour), 30, TextType.Light, + color: colorPrimaryDark), + const SizedBox(height: 8), + text( + "This is where things go to die. Stop scheduling anything that matters into it.", + 13, + TextType.Regular, + color: colorGrey2, + height: 1.5, + ), + ], + ), + ); + } + + Widget _deferralRow(DeferralCount item) { + return Container( + margin: const EdgeInsets.only(bottom: 8), + child: card( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 13), + child: Row( + children: [ + Expanded( + child: text(item.title, 13, TextType.Regular, + color: colorPrimaryDark, + maxLines: 1, + overflow: TextOverflow.ellipsis), + ), + pill("${item.count}×", colorStandingGrounded, + colorStandingGroundedBg, textSize: 9), + ], + ), + ), + ); + } + + /// Your estimates are wrong, and this is by how much. + Widget _estimationRow(MapEntry entry) { + return Container( + margin: const EdgeInsets.only(bottom: 8), + child: card( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 13), + child: Row( + children: [ + Expanded( + child: text(entry.key, 13, TextType.Regular, + color: colorPrimaryDark), + ), + text("${entry.value.toStringAsFixed(1)}×", 15, TextType.Bold, + color: entry.value > 1.4 ? colorStandingGrounded : colorGrey2), + ], + ), + ), + ); + } + + Widget _trainingCard() { + return card( + child: Row( + children: [ + Expanded( + child: labelled( + "Adherence", + "${(_report.trainingAdherence * 100).round()}%", + valueSize: 26, + valueType: TextType.Light, + ), + ), + Expanded( + child: labelled( + "Integrity", + "${(_report.programIntegrity * 100).round()}%", + valueSize: 26, + valueType: TextType.Light, + valueColor: _report.programIntegrity < 0.7 + ? colorStandingGrounded + : colorPrimaryDark, + ), + ), + ], + ), + ); + } + + @override + void onReportLoaded(ReportCard report) { + setState(() { + _report = report; + }); + } +} diff --git a/frontend/lib/Grounded/see/reportcard/ViewReportCardScreen.dart b/frontend/lib/Grounded/see/reportcard/ViewReportCardScreen.dart new file mode 100644 index 0000000..f83753e --- /dev/null +++ b/frontend/lib/Grounded/see/reportcard/ViewReportCardScreen.dart @@ -0,0 +1,37 @@ +import '../../about/external/data/ReportCard.dart'; +import '../../about/external/initial/ReportCardRequest.dart'; +import '../parent/ParentViewModel.dart'; +import 'ConnectReportCardScreen.dart'; + +class ViewReportCardScreen extends ParentViewModel { + ConnectReportCardScreen connection; + + ViewReportCardScreen(super.context, this.connection); + + void loadReport() async { + if (!await hasNetwork(() => loadReport())) return; + + showLoading("Preparing your report"); + + try { + final DateTime now = DateTime.now(); + + // The week runs Monday to now, so the card always covers the week you + // are actually in rather than a trailing seven days. + final DateTime start = + now.subtract(Duration(days: now.weekday - 1)); + + final response = + await getDataManager().getWeeklyReportCard(ReportCardRequest( + periodStart: start.toIso8601String(), + periodEnd: now.toIso8601String(), + )); + + closeLoading(); + + connection.onReportLoaded(ReportCard.fromJson(response.data)); + } catch (e) { + handleError(e, () => loadReport(), () => dismissError(), "Retry"); + } + } +} diff --git a/frontend/lib/Grounded/see/settings/ConnectSettings.dart b/frontend/lib/Grounded/see/settings/ConnectSettings.dart new file mode 100644 index 0000000..89a1ae3 --- /dev/null +++ b/frontend/lib/Grounded/see/settings/ConnectSettings.dart @@ -0,0 +1,11 @@ +import '../../about/internal/application/UserDetails.dart'; + +abstract class ConnectSettings { + void onUserLoaded(UserDetails details, int amnestyRemaining); + + void onToneChanged(UserDetails details); + + void onSickModeChanged(UserDetails details); + + void onSignedOut(); +} diff --git a/frontend/lib/Grounded/see/settings/Settings.dart b/frontend/lib/Grounded/see/settings/Settings.dart new file mode 100644 index 0000000..96cdc55 --- /dev/null +++ b/frontend/lib/Grounded/see/settings/Settings.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'SettingsState.dart'; + +class Settings extends StatefulWidget { + const Settings({super.key}); + + @override + State createState() => SettingsState(); +} diff --git a/frontend/lib/Grounded/see/settings/SettingsState.dart b/frontend/lib/Grounded/see/settings/SettingsState.dart new file mode 100644 index 0000000..313d284 --- /dev/null +++ b/frontend/lib/Grounded/see/settings/SettingsState.dart @@ -0,0 +1,344 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/internal/application/NavigatorType.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../about/internal/application/ToneLevel.dart'; +import '../../about/internal/application/UserDetails.dart'; +import '../../configs/Navigator.dart'; +import '../../designs/Component.dart'; +import '../../designs/Responsive.dart'; +import '../../designs/Shell.dart'; +import '../../designs/buttons/Buttons.dart'; +import '../../designs/input/InputFields.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/Colors.dart'; +import '../../utils/Thresholds.dart'; +import '../../utils/Validators.dart'; +import '../login/Login.dart'; +import 'ConnectSettings.dart'; +import 'Settings.dart'; +import 'ViewSettings.dart'; + +class SettingsState extends State implements ConnectSettings { + ViewSettings? _model; + + UserDetails _user = UserDetails(pic: '', name: ''); + + int _amnestyRemaining = 0; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewSettings(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ); + } + + void _initiate() { + _model?.initialise(); + } + + // ── Handlers ────────────────────────────────────────────────────────────── + + void _onBack() { + Navigator.pop(context); + } + + void _onToneSelected(ToneLevel tone) { + _model?.changeTone(tone); + } + + void _onToggleSickMode() { + if (_user.sickMode) { + _model?.setSickMode(false, ""); + return; + } + + _openSickModeSheet(); + } + + void _onSignOut() { + _model?.signOut(); + } + + void _openSickModeSheet() { + final TextEditingController reason = TextEditingController(); + final GlobalKey formKey = GlobalKey(); + + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + barrierColor: colorPrimaryDark.withValues(alpha: 0.6), + builder: (BuildContext sheetContext) { + return Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(sheetContext).viewInsets.bottom, + ), + child: Container( + decoration: BoxDecoration( + color: colorSheetBackground, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28), + ), + ), + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 16, 24, 32), + child: Form( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + width: 36, + height: 4, + margin: const EdgeInsets.only(bottom: 20), + decoration: BoxDecoration( + color: colorGrey.withValues(alpha: 0.35), + borderRadius: BorderRadius.circular(999), + ), + ), + ), + text("PAUSE", 9, TextType.Bold, + color: colorGrey2, letterSpacing: 1.2), + const SizedBox(height: 10), + text("Sick or travelling.", 26, TextType.Light, + color: colorPrimaryDark, height: 1.2), + const SizedBox(height: 14), + text( + "Debt stops accruing entirely while this is on. It is logged in your history, which is the only reason it stays honest.", + 14, + TextType.Regular, + color: colorGrey2, + height: 1.55, + ), + const SizedBox(height: 24), + inputField( + "Reason", + reason, + hint: "What is going on?", + validator: Validators.excuse, + maxLines: 3, + ), + const SizedBox(height: 24), + roundedCornerButton( + "Pause everything", + () { + if (formKey.currentState?.validate() != true) { + return; + } + Navigator.pop(sheetContext); + _model?.setSickMode(true, reason.text.trim()); + }, + icon: CupertinoIcons.pause_fill, + ), + const SizedBox(height: 8), + Center( + child: textButton( + "Cancel", + () => Navigator.pop(sheetContext), + textSize: 13, + ), + ), + ], + ), + ), + ), + ), + ); + }, + ); + } + + // ── Views ───────────────────────────────────────────────────────────────── + + Widget _mobileView(BoxConstraints constraints) { + return Sheet( + eyebrow: _user.name.isEmpty ? "Account" : _user.name, + title: "Settings", + onBack: _onBack, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + sectionBreak("Tone", caption: "how it speaks to you"), + segmentedSelector( + options: ToneLevel.values, + selected: _user.tone, + label: toneLabel, + onSelected: _onToneSelected, + ), + const SizedBox(height: 12), + text( + _toneDescription(_user.tone), + 12, + TextType.Regular, + color: colorGrey2, + height: 1.5, + ), + const SizedBox(height: 10), + text( + "Whatever you pick, nothing here will attack you as a person. It criticises what you did, never who you are.", + 11, + TextType.Regular, + color: colorGrey, + height: 1.5, + ), + const SizedBox(height: 28), + sectionBreak("Amnesty", caption: "rationed on purpose"), + card( + child: Row( + children: [ + Expanded( + child: labelled( + "Remaining this month", + "$_amnestyRemaining of ${Thresholds.amnestyTokensPerMonth}", + valueSize: 20, + valueType: TextType.Light, + ), + ), + Container( + width: 46, + height: 46, + alignment: Alignment.center, + decoration: BoxDecoration( + color: _amnestyRemaining > 0 + ? colorStandingGoodBg + : colorMuted, + borderRadius: BorderRadius.circular(14), + ), + child: Icon( + CupertinoIcons.checkmark_shield_fill, + size: 20, + color: _amnestyRemaining > 0 ? colorPositive : colorGrey, + ), + ), + ], + ), + ), + const SizedBox(height: 10), + text( + "Tokens wipe an item's debt, no questions asked. They exist so one bad flu does not undo three months.", + 12, + TextType.Regular, + color: colorGrey2, + height: 1.5, + ), + const SizedBox(height: 28), + sectionBreak("Pause", caption: "sick or travel"), + card( + background: _user.sickMode ? colorStandingGoodBg : colorCard, + borderColor: _user.sickMode + ? colorPositive.withValues(alpha: 0.20) + : colorBorder, + onTap: _onToggleSickMode, + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text( + _user.sickMode ? "Paused" : "Running", + 16, + TextType.Medium, + color: colorPrimaryDark, + ), + const SizedBox(height: 6), + text( + _user.sickMode + ? "Debt is not accruing. Tap to resume." + : "Debt is accruing normally. Tap to pause.", + 12, + TextType.Regular, + color: colorGrey2, + ), + ], + ), + ), + Icon( + _user.sickMode + ? CupertinoIcons.pause_circle_fill + : CupertinoIcons.play_circle, + size: 26, + color: _user.sickMode ? colorPositive : colorGrey2, + ), + ], + ), + ), + const SizedBox(height: 36), + outlinedActionButton( + "Sign out", + _onSignOut, + foreground: colorStandingLockdown, + icon: CupertinoIcons.square_arrow_right, + ), + const SizedBox(height: 20), + Center( + child: text("Grounded 1.0.0", 11, TextType.Regular, + color: colorGrey), + ), + ], + ), + ); + } + + String _toneDescription(ToneLevel tone) { + switch (tone) { + case ToneLevel.Firm: + return "Direct and unsentimental. States the facts and leaves them there."; + case ToneLevel.Strict: + return "Holds you to what you said. Disappointment rather than anger, because it works better."; + case ToneLevel.DrillSergeant: + return "Blunt and relentless about the behaviour. Still never about you."; + } + } + + // ── ConnectSettings ─────────────────────────────────────────────────────── + + @override + void onUserLoaded(UserDetails details, int amnestyRemaining) { + setState(() { + _user = details; + _amnestyRemaining = amnestyRemaining; + }); + } + + @override + void onToneChanged(UserDetails details) { + setState(() { + _user = details; + }); + } + + @override + void onSickModeChanged(UserDetails details) { + setState(() { + _user = details; + }); + } + + @override + void onSignedOut() { + GroundedNavigation() + .navigateToPage(NavigatorType.makeNewMain, const Login(), context); + } +} diff --git a/frontend/lib/Grounded/see/settings/ViewSettings.dart b/frontend/lib/Grounded/see/settings/ViewSettings.dart new file mode 100644 index 0000000..40d07c8 --- /dev/null +++ b/frontend/lib/Grounded/see/settings/ViewSettings.dart @@ -0,0 +1,91 @@ +import '../../about/external/initial/SickModeRequest.dart'; +import '../../about/external/initial/ToneRequest.dart'; +import '../../about/internal/application/MeDescription.dart'; +import '../../about/internal/application/ToneLevel.dart'; +import '../../about/internal/application/UserDetails.dart'; +import '../../utils/GuardrailEngine.dart'; +import '../../utils/Thresholds.dart'; +import '../parent/ParentViewModel.dart'; +import 'ConnectSettings.dart'; + +class ViewSettings extends ParentViewModel { + ConnectSettings connection; + + ViewSettings(super.context, this.connection); + + void initialise() async { + final UserDetails details = await getDataManager().getUserDetails(); + + final int spent = await getDataManager().getAmnestySpent(); + + connection.onUserLoaded( + details, + GuardrailEngine.tokensRemaining(Thresholds.amnestyTokensPerMonth, spent), + ); + } + + /// The tone slider is capped: it changes register, never cruelty. + void changeTone(ToneLevel tone) async { + if (!await hasNetwork(() => changeTone(tone))) return; + + showLoading("Saving"); + + try { + await getDataManager().updateTone(ToneRequest(tone: tone.name)); + + final UserDetails details = await getDataManager().getUserDetails(); + details.tone = tone; + await getDataManager().setUserDetails(details); + + closeLoading(); + + connection.onToneChanged(details); + } catch (e) { + handleError(e, () => changeTone(tone), () => dismissError(), "Retry"); + } + } + + /// Sick mode pauses debt accrual entirely. It requires a reason and is + /// logged, so it stays honest without being punitive. + void setSickMode(bool enabled, String reason) async { + if (!await hasNetwork(() => setSickMode(enabled, reason))) return; + + showLoading(enabled ? "Pausing" : "Resuming"); + + try { + await getDataManager().updateSickMode(SickModeRequest( + enabled: enabled, + reason: reason, + )); + + final UserDetails details = await getDataManager().getUserDetails(); + details.sickMode = enabled; + await getDataManager().setUserDetails(details); + + closeLoading(); + + connection.onSickModeChanged(details); + } catch (e) { + handleError(e, () => setSickMode(enabled, reason), () => dismissError(), + "Retry"); + } + } + + void signOut() async { + showLoading("Signing out"); + + try { + await getDataManager().logout(); + } catch (e) { + // A failed logout call must never trap the user in the app; the local + // session is cleared either way. + } + + await getDataManager() + .setMyDescription(MeDescription(id: "", name: "", token: "")); + + closeLoading(); + + connection.onSignedOut(); + } +} diff --git a/frontend/lib/Grounded/see/splash/ConnectSplash.dart b/frontend/lib/Grounded/see/splash/ConnectSplash.dart new file mode 100644 index 0000000..46f5e2c --- /dev/null +++ b/frontend/lib/Grounded/see/splash/ConnectSplash.dart @@ -0,0 +1,7 @@ +import '../../about/internal/application/MeDescription.dart'; + +abstract class ConnectSplash { + void launchHome(MeDescription value); + + void launchLogin(); +} diff --git a/frontend/lib/Grounded/see/splash/Splash.dart b/frontend/lib/Grounded/see/splash/Splash.dart new file mode 100644 index 0000000..957cfa6 --- /dev/null +++ b/frontend/lib/Grounded/see/splash/Splash.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'SplashState.dart'; + +class Splash extends StatefulWidget { + const Splash({super.key}); + + @override + State createState() => SplashState(); +} diff --git a/frontend/lib/Grounded/see/splash/SplashState.dart b/frontend/lib/Grounded/see/splash/SplashState.dart new file mode 100644 index 0000000..a77a051 --- /dev/null +++ b/frontend/lib/Grounded/see/splash/SplashState.dart @@ -0,0 +1,124 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/internal/application/MeDescription.dart'; +import '../../about/internal/application/NavigatorType.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../configs/Navigator.dart'; +import '../../designs/Responsive.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/Colors.dart'; +import '../../utils/Images.dart'; +import '../home/Home.dart'; +import '../login/Login.dart'; +import 'ConnectSplash.dart'; +import 'Splash.dart'; +import 'ViewSplash.dart'; + +class SplashState extends State implements ConnectSplash { + ViewSplash? _model; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewSplash(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => PopScope( + canPop: false, + child: Scaffold( + backgroundColor: colorPrimaryDark, + body: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ), + ), + ); + } + + Future _initiate() async { + // A beat on the wordmark, then the session decides where we land. + await Future.delayed(const Duration(milliseconds: 900)); + _model?.initialize(); + } + + Widget _mobileView(BoxConstraints constraints) { + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 36), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + text("GROUNDED", 10, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), letterSpacing: 2.0), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // The same mark the native splash shows, so the handover from + // the OS screen into the app is invisible. + Image.asset( + splashMark, + width: 108, + height: 108, + errorBuilder: (context, error, stackTrace) => + const SizedBox(height: 108), + ), + const SizedBox(height: 28), + text("Grounded", 52, TextType.Light, + color: colorWhite, height: 1.05), + const SizedBox(height: 16), + Container( + width: 44, + height: 2, + color: colorWhite.withValues(alpha: 0.30), + ), + const SizedBox(height: 20), + text( + "A to-do app that does not believe you.", + 15, + TextType.Regular, + color: colorWhite.withValues(alpha: 0.60), + height: 1.5, + ), + ], + ), + SizedBox( + width: 22, + height: 22, + child: CircularProgressIndicator( + strokeWidth: 2, + backgroundColor: colorWhite.withValues(alpha: 0.12), + color: colorWhite.withValues(alpha: 0.70), + ), + ), + ], + ), + ), + ); + } + + @override + void launchHome(MeDescription value) { + GroundedNavigation() + .navigateToPage(NavigatorType.makeNewMain, const Home(), context); + } + + @override + void launchLogin() { + GroundedNavigation() + .navigateToPage(NavigatorType.makeNewMain, const Login(), context); + } +} diff --git a/frontend/lib/Grounded/see/splash/ViewSplash.dart b/frontend/lib/Grounded/see/splash/ViewSplash.dart new file mode 100644 index 0000000..0652b84 --- /dev/null +++ b/frontend/lib/Grounded/see/splash/ViewSplash.dart @@ -0,0 +1,29 @@ +import '../parent/ParentViewModel.dart'; +import 'ConnectSplash.dart'; + +class ViewSplash extends ParentViewModel { + ConnectSplash connection; + + ViewSplash(super.context, this.connection); + + void initialize() { + // App opens feed the distress conjunction, so the count is bumped before + // anything else happens. + _recordEngagement(); + + getDataManager().getMyDescription().then((value) { + if (value.token != "") { + connection.launchHome(value); + } else { + connection.launchLogin(); + } + }).onError((error, stackTrace) { + connection.launchLogin(); + }); + } + + void _recordEngagement() async { + final int count = await getDataManager().getEngagementCount(); + await getDataManager().setEngagementCount(count + 1); + } +} diff --git a/frontend/lib/Grounded/see/system/sessionexpired/ConnectSessionExpired.dart b/frontend/lib/Grounded/see/system/sessionexpired/ConnectSessionExpired.dart new file mode 100644 index 0000000..fb9a3fb --- /dev/null +++ b/frontend/lib/Grounded/see/system/sessionexpired/ConnectSessionExpired.dart @@ -0,0 +1,3 @@ +abstract class ConnectSessionExpired { + void launchLogin(); +} diff --git a/frontend/lib/Grounded/see/system/sessionexpired/SessionExpired.dart b/frontend/lib/Grounded/see/system/sessionexpired/SessionExpired.dart new file mode 100644 index 0000000..4fdafe2 --- /dev/null +++ b/frontend/lib/Grounded/see/system/sessionexpired/SessionExpired.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'SessionExpiredState.dart'; + +class SessionExpired extends StatefulWidget { + const SessionExpired({super.key}); + + @override + State createState() => SessionExpiredState(); +} diff --git a/frontend/lib/Grounded/see/system/sessionexpired/SessionExpiredState.dart b/frontend/lib/Grounded/see/system/sessionexpired/SessionExpiredState.dart new file mode 100644 index 0000000..7c9ef0b --- /dev/null +++ b/frontend/lib/Grounded/see/system/sessionexpired/SessionExpiredState.dart @@ -0,0 +1,117 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../../about/internal/application/NavigatorType.dart'; +import '../../../about/internal/application/TextType.dart'; +import '../../../configs/Navigator.dart'; +import '../../../designs/Responsive.dart'; +import '../../../designs/buttons/Buttons.dart'; +import '../../../designs/text/Text.dart'; +import '../../../utils/Colors.dart'; +import '../../login/Login.dart'; +import 'ConnectSessionExpired.dart'; +import 'SessionExpired.dart'; +import 'ViewSessionExpired.dart'; + +class SessionExpiredState extends State + implements ConnectSessionExpired { + ViewSessionExpired? _model; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewSessionExpired(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => PopScope( + canPop: false, + child: Scaffold( + backgroundColor: colorPrimaryDark, + body: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ), + ), + ); + } + + void _initiate() {} + + void _onSignIn() { + _model?.signOut(); + } + + Widget _mobileView(BoxConstraints constraints) { + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 32), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + text("GROUNDED", 10, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), letterSpacing: 2.0), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + alignment: Alignment.center, + decoration: BoxDecoration( + color: colorWhite.withValues(alpha: 0.10), + borderRadius: BorderRadius.circular(18), + ), + child: Icon(CupertinoIcons.lock_fill, + size: 24, color: colorWhite), + ), + const SizedBox(height: 28), + text("SESSION", 10, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), + letterSpacing: 1.2), + const SizedBox(height: 10), + text("Your session has ended.", 34, TextType.Light, + color: colorWhite, height: 1.15), + const SizedBox(height: 14), + text( + "Sign in again to pick up where you left off. Your record is intact — nothing was cleared while you were away.", + 14, + TextType.Regular, + color: colorWhite.withValues(alpha: 0.60), + height: 1.55, + ), + ], + ), + SizedBox( + width: double.infinity, + child: roundedCornerButton( + "Sign in", + _onSignIn, + background: colorWhite, + foreground: colorPrimaryDark, + icon: CupertinoIcons.arrow_right, + ), + ), + ], + ), + ), + ); + } + + @override + void launchLogin() { + GroundedNavigation() + .navigateToPage(NavigatorType.makeNewMain, const Login(), context); + } +} diff --git a/frontend/lib/Grounded/see/system/sessionexpired/ViewSessionExpired.dart b/frontend/lib/Grounded/see/system/sessionexpired/ViewSessionExpired.dart new file mode 100644 index 0000000..28a63b7 --- /dev/null +++ b/frontend/lib/Grounded/see/system/sessionexpired/ViewSessionExpired.dart @@ -0,0 +1,18 @@ +import '../../../about/internal/application/MeDescription.dart'; +import '../../parent/ParentViewModel.dart'; +import 'ConnectSessionExpired.dart'; + +class ViewSessionExpired extends ParentViewModel { + ConnectSessionExpired connection; + + ViewSessionExpired(super.context, this.connection); + + /// Clears the stored session before sending the user back to login, so an + /// expired token can never be replayed. + void signOut() async { + await getDataManager() + .setMyDescription(MeDescription(id: "", name: "", token: "")); + + connection.launchLogin(); + } +} diff --git a/frontend/lib/Grounded/see/system/updateme/ConnectUpdateMe.dart b/frontend/lib/Grounded/see/system/updateme/ConnectUpdateMe.dart new file mode 100644 index 0000000..cb38ca0 --- /dev/null +++ b/frontend/lib/Grounded/see/system/updateme/ConnectUpdateMe.dart @@ -0,0 +1,3 @@ +abstract class ConnectUpdateMe { + void onStoreOpened(); +} diff --git a/frontend/lib/Grounded/see/system/updateme/UpdateMe.dart b/frontend/lib/Grounded/see/system/updateme/UpdateMe.dart new file mode 100644 index 0000000..7d72b22 --- /dev/null +++ b/frontend/lib/Grounded/see/system/updateme/UpdateMe.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'UpdateMeState.dart'; + +class UpdateMe extends StatefulWidget { + const UpdateMe({super.key}); + + @override + State createState() => UpdateMeState(); +} diff --git a/frontend/lib/Grounded/see/system/updateme/UpdateMeState.dart b/frontend/lib/Grounded/see/system/updateme/UpdateMeState.dart new file mode 100644 index 0000000..f7fa51f --- /dev/null +++ b/frontend/lib/Grounded/see/system/updateme/UpdateMeState.dart @@ -0,0 +1,116 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../../about/internal/application/TextType.dart'; +import '../../../designs/Responsive.dart'; +import '../../../designs/buttons/Buttons.dart'; +import '../../../designs/text/Text.dart'; +import '../../../utils/Colors.dart'; +import 'ConnectUpdateMe.dart'; +import 'UpdateMe.dart'; +import 'ViewUpdateMe.dart'; + +class UpdateMeState extends State implements ConnectUpdateMe { + ViewUpdateMe? _model; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewUpdateMe(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => PopScope( + canPop: false, + child: Scaffold( + backgroundColor: colorPrimaryDark, + body: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ), + ), + ); + } + + void _initiate() {} + + void _onUpdate() { + _model?.openStore(); + } + + Widget _mobileView(BoxConstraints constraints) { + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 32), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + text("GROUNDED", 10, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), letterSpacing: 2.0), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + alignment: Alignment.center, + decoration: BoxDecoration( + color: colorWhite.withValues(alpha: 0.10), + borderRadius: BorderRadius.circular(18), + ), + child: Icon(CupertinoIcons.arrow_up_circle_fill, + size: 24, color: colorWhite), + ), + const SizedBox(height: 28), + text("UPDATE REQUIRED", 10, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), + letterSpacing: 1.2), + const SizedBox(height: 10), + text("This version is out of date.", 34, TextType.Light, + color: colorWhite, height: 1.15), + const SizedBox(height: 14), + text( + "Update to continue. Your commitments, debt and history are on the server and will be waiting.", + 14, + TextType.Regular, + color: colorWhite.withValues(alpha: 0.60), + height: 1.55, + ), + ], + ), + SizedBox( + width: double.infinity, + child: roundedCornerButton( + "Update now", + _onUpdate, + background: colorWhite, + foreground: colorPrimaryDark, + icon: CupertinoIcons.cloud_download_fill, + ), + ), + ], + ), + ), + ); + } + + @override + void onStoreOpened() async { + final Uri store = Uri.parse("https://grounded.app/download"); + if (await canLaunchUrl(store)) { + await launchUrl(store, mode: LaunchMode.externalApplication); + } + } +} diff --git a/frontend/lib/Grounded/see/system/updateme/ViewUpdateMe.dart b/frontend/lib/Grounded/see/system/updateme/ViewUpdateMe.dart new file mode 100644 index 0000000..bd731dd --- /dev/null +++ b/frontend/lib/Grounded/see/system/updateme/ViewUpdateMe.dart @@ -0,0 +1,12 @@ +import '../../parent/ParentViewModel.dart'; +import 'ConnectUpdateMe.dart'; + +class ViewUpdateMe extends ParentViewModel { + ConnectUpdateMe connection; + + ViewUpdateMe(super.context, this.connection); + + void openStore() { + connection.onStoreOpened(); + } +} diff --git a/frontend/lib/Grounded/see/training/ConnectTraining.dart b/frontend/lib/Grounded/see/training/ConnectTraining.dart new file mode 100644 index 0000000..705e395 --- /dev/null +++ b/frontend/lib/Grounded/see/training/ConnectTraining.dart @@ -0,0 +1,16 @@ +import '../../about/external/data/Program.dart'; +import '../../about/external/data/SessionLog.dart'; +import '../../about/external/data/SessionTemplate.dart'; + +abstract class ConnectTraining { + void onProgramLoaded(Program program, List sessions); + + void onHistoryLoaded(List sessions); + + /// The weekly plyometric contact ceiling has been reached — the app stops + /// you rather than pushing you. + void onContactCeilingReached(int contacts, int ceiling); + + /// Not enough recovery since the last hard lower-body session. + void onRecoveryBlocked(int hoursRemaining); +} diff --git a/frontend/lib/Grounded/see/training/Training.dart b/frontend/lib/Grounded/see/training/Training.dart new file mode 100644 index 0000000..1b06192 --- /dev/null +++ b/frontend/lib/Grounded/see/training/Training.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'TrainingState.dart'; + +class Training extends StatefulWidget { + const Training({super.key}); + + @override + State createState() => TrainingState(); +} diff --git a/frontend/lib/Grounded/see/training/TrainingState.dart b/frontend/lib/Grounded/see/training/TrainingState.dart new file mode 100644 index 0000000..cd797de --- /dev/null +++ b/frontend/lib/Grounded/see/training/TrainingState.dart @@ -0,0 +1,350 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../about/external/data/Program.dart'; +import '../../about/external/data/SessionLog.dart'; +import '../../about/external/data/SessionTemplate.dart'; +import '../../about/internal/application/NotificationType.dart'; +import '../../about/internal/application/TextType.dart'; +import '../../designs/Component.dart'; +import '../../designs/Responsive.dart'; +import '../../designs/Shell.dart'; +import '../../designs/text/Text.dart'; +import '../../utils/Colors.dart'; +import '../../utils/CommonUtils.dart'; +import '../../utils/IntegrityEngine.dart'; +import 'ConnectTraining.dart'; +import 'Training.dart'; +import 'ViewTraining.dart'; + +class TrainingState extends State implements ConnectTraining { + ViewTraining? _model; + + Program _program = Program(); + + List _sessions = []; + + List _history = []; + + int _contacts = 0; + + bool _ceilingReached = false; + + int _recoveryHoursRemaining = 0; + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ViewTraining(context, this), + onViewModelReady: (viewModel) { + _model = viewModel; + _initiate(); + }, + builder: (context, viewModel, child) => LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Responsive( + mobile: _mobileView(constraints), + tablet: _mobileView(constraints), + desktop: _mobileView(constraints), + ); + }, + ), + ); + } + + void _initiate() { + _model?.loadProgram(); + } + + void _onBack() { + Navigator.pop(context); + } + + Widget _mobileView(BoxConstraints constraints) { + return Sheet( + eyebrow: _program.name.isEmpty ? "No program" : _program.name, + title: "Training", + onBack: _onBack, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + if (_ceilingReached || _recoveryHoursRemaining > 0) ...[ + _stopCard(), + const SizedBox(height: 24), + ], + if (_program.id == null) + emptyState( + CupertinoIcons.flame, + "No active program", + "Build a program with sessions, progression rules and scheduled deloads, and missed sessions start feeding your debt.", + ) + else ...[ + _programCard(), + const SizedBox(height: 28), + sectionBreak("Sessions", caption: "${_sessions.length} per cycle"), + if (_sessions.isEmpty) + text("No sessions defined yet.", 13, TextType.Regular, + color: colorGrey2) + else + ..._sessions.map(_sessionRow), + const SizedBox(height: 28), + sectionBreak("Volume", caption: "this week"), + _volumeCard(), + const SizedBox(height: 28), + sectionBreak("Recent", caption: "last sessions"), + if (_history.isEmpty) + text("Nothing logged yet.", 13, TextType.Regular, + color: colorGrey2) + else + ..._history.take(5).map(_historyRow), + ], + ], + ), + ); + } + + /// Plyo and recovery are the two places the app refuses rather than nags. + Widget _stopCard() { + final bool ceiling = _ceilingReached; + + return card( + background: colorStandingLockdownBg, + borderColor: colorStandingLockdown.withValues(alpha: 0.25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Icon(CupertinoIcons.hand_raised_fill, + size: 16, color: colorStandingLockdown), + const SizedBox(width: 8), + text(ceiling ? "CONTACT CEILING" : "RECOVERY", 9, TextType.Bold, + color: colorStandingLockdown, letterSpacing: 1.2), + ], + ), + const SizedBox(height: 12), + text( + ceiling ? "Stop plyometrics this week." : "Not recovered yet.", + 22, + TextType.Light, + color: colorPrimaryDark, + ), + const SizedBox(height: 10), + text( + ceiling + ? "You are at $_contacts of ${_program.weeklyContactCeiling} ground contacts. Connective tissue does not recover on a motivation schedule — this is the one place the app stops you." + : "$_recoveryHoursRemaining hours left before the next hard lower-body session. Training through this is not discipline, it is a shortcut to an injury.", + 13, + TextType.Regular, + color: colorGrey2, + height: 1.55, + ), + ], + ), + ); + } + + Widget _programCard() { + return card( + background: colorPrimaryDark, + borderColor: colorPrimaryDark, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text("ACTIVE PROGRAM", 9, TextType.Bold, + color: colorWhite.withValues(alpha: 0.45), letterSpacing: 1.2), + const SizedBox(height: 10), + text(_program.name, 24, TextType.Light, color: colorWhite), + const SizedBox(height: 20), + Row( + children: [ + Expanded( + child: labelled( + "Weeks", + "${_program.weeks}", + valueSize: 18, + valueColor: colorWhite, + labelColor: colorWhite.withValues(alpha: 0.45), + ), + ), + Expanded( + child: labelled( + "Per week", + "${_program.sessionsPerWeek}", + valueSize: 18, + valueColor: colorWhite, + labelColor: colorWhite.withValues(alpha: 0.45), + ), + ), + Expanded( + child: labelled( + "Deloads", + "${_program.deloadWeeks.length}", + valueSize: 18, + valueColor: colorWhite, + labelColor: colorWhite.withValues(alpha: 0.45), + ), + ), + ], + ), + ], + ), + ); + } + + Widget _sessionRow(SessionTemplate session) { + return Container( + margin: const EdgeInsets.only(bottom: 10), + child: card( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text(session.name, 15, TextType.Medium, + color: colorPrimaryDark), + const SizedBox(height: 6), + text("${session.prescriptions.length} exercises", 11, + TextType.Regular, color: colorGrey2), + ], + ), + ), + if (session.highIntensityLowerBody) + pill("HIGH LOAD", colorStandingWarned, colorStandingWarnedBg, + textSize: 9), + ], + ), + ), + ); + } + + /// Volume per muscle group, not "you went to the gym". + Widget _volumeCard() { + final Map volume = + IntegrityEngine.weeklySetsByMuscleGroup(_history); + + if (volume.isEmpty) { + return card( + child: text("No sets logged this week.", 13, TextType.Regular, + color: colorGrey2), + ); + } + + return card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: volume.entries.map((entry) { + return Padding( + padding: const EdgeInsets.only(bottom: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + text(entry.key, 13, TextType.Regular, + color: colorPrimaryDark), + text("${entry.value} sets", 12, TextType.Bold, + color: colorGrey2), + ], + ), + const SizedBox(height: 6), + meter(entry.value / 20), + ], + ), + ); + }).toList(), + ), + ); + } + + Widget _historyRow(SessionLog session) { + final bool watered = session.integrityScore > 0 && + session.integrityScore < 0.7; + + return Container( + margin: const EdgeInsets.only(bottom: 10), + child: card( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + text(session.templateName, 14, TextType.Medium, + color: colorPrimaryDark), + const SizedBox(height: 6), + text( + "${formatDate(session.startedAt)} · ${session.sets.length} sets · RPE ${session.sessionRpe.toStringAsFixed(1)}", + 11, + TextType.Regular, + color: colorGrey2, + ), + ], + ), + ), + if (session.integrityScore > 0) + pill( + "${(session.integrityScore * 100).round()}%", + watered ? colorStandingGrounded : colorPositive, + watered ? colorStandingGroundedBg : colorStandingGoodBg, + textSize: 9, + ), + ], + ), + ), + ); + } + + // ── ConnectTraining ─────────────────────────────────────────────────────── + + @override + void onProgramLoaded(Program program, List sessions) { + setState(() { + _program = program; + _sessions = sessions; + }); + } + + @override + void onHistoryLoaded(List sessions) { + setState(() { + _history = sessions; + }); + } + + @override + void onContactCeilingReached(int contacts, int ceiling) { + setState(() { + _contacts = contacts; + _ceilingReached = true; + }); + + _model?.showApplicationNotification( + NotificationType.warning, + "Contact ceiling reached", + "$contacts of $ceiling ground contacts this week. No more plyometrics until it resets.", + true, + true, + null, + ); + } + + @override + void onRecoveryBlocked(int hoursRemaining) { + setState(() { + _recoveryHoursRemaining = hoursRemaining; + }); + } +} diff --git a/frontend/lib/Grounded/see/training/ViewTraining.dart b/frontend/lib/Grounded/see/training/ViewTraining.dart new file mode 100644 index 0000000..a1634b2 --- /dev/null +++ b/frontend/lib/Grounded/see/training/ViewTraining.dart @@ -0,0 +1,129 @@ +import '../../about/external/data/Program.dart'; +import '../../about/external/data/SessionLog.dart'; +import '../../about/external/data/SessionTemplate.dart'; +import '../../about/external/data/pages/request/HistoryRequest.dart'; +import '../../about/external/data/pages/request/PageAndSort.dart'; +import '../../about/external/data/pages/request/Pageable.dart'; +import '../../about/external/data/pages/request/Sort.dart'; +import '../../about/external/data/pages/response/SessionLogPage.dart'; +import '../../about/external/initial/IdRequest.dart'; +import '../../utils/IntegrityEngine.dart'; +import '../../utils/ObjectConvertors.dart'; +import '../parent/ParentViewModel.dart'; +import 'ConnectTraining.dart'; + +class ViewTraining extends ParentViewModel { + ConnectTraining connection; + + ViewTraining(super.context, this.connection); + + void loadProgram() async { + if (!await hasNetwork(() => loadProgram())) return; + + showLoading("Loading your program"); + + try { + final response = await getDataManager().getMyPrograms(HistoryRequest( + query: PageAndSort( + sort: Sort('desc', 'active'), + page: Pageable(0, 0, 20, 0), + ), + )); + + final List programs = getProgramList(response.data); + + final Program active = programs.firstWhere( + (item) => item.active, + orElse: () => programs.isEmpty ? Program() : programs.first, + ); + + await getDataManager().setActiveProgram(active); + + final List sessions = await _loadSessions(active); + + closeLoading(); + + connection.onProgramLoaded(active, sessions); + + loadHistory(active); + } catch (e) { + handleError(e, () => loadProgram(), () => dismissError(), "Retry"); + } + } + + Future> _loadSessions(Program program) async { + if (program.id == null) { + return []; + } + + final response = await getDataManager() + .getProgramSessions(IdRequest(id: program.id ?? "")); + + return getSessionTemplateList(response.data); + } + + void loadHistory(Program program) async { + try { + final response = await getDataManager().getSessionHistory(HistoryRequest( + query: PageAndSort( + sort: Sort('desc', 'startedAt'), + page: Pageable(0, 0, 20, 0), + ), + )); + + final SessionLogPage page = SessionLogPage.fromJson(response.data); + + connection.onHistoryLoaded(page.content); + + _checkLoadCeilings(program, page.content); + } catch (e) { + handleError(e, () => loadHistory(program), () => dismissError(), "Retry"); + } + } + + /// Plyometrics is the one modality where the app should stop you rather than + /// push you — CNS and connective tissue do not recover on a motivation + /// schedule. + void _checkLoadCeilings(Program program, List history) { + final DateTime weekStart = + DateTime.now().subtract(Duration(days: DateTime.now().weekday - 1)); + + final List thisWeek = history + .where((session) => + session.startedAt != null && + session.startedAt!.isAfter(weekStart)) + .toList(); + + final int contacts = IntegrityEngine.weeklyContacts(thisWeek); + + if (IntegrityEngine.contactCeilingBreached( + thisWeek, program.weeklyContactCeiling)) { + connection.onContactCeilingReached( + contacts, program.weeklyContactCeiling); + return; + } + + _checkRecovery(program, history); + } + + void _checkRecovery(Program program, List history) { + // The most recent hard lower-body session gates the next one. + final List hard = history + .where((session) => session.sessionRpe >= 8 && session.startedAt != null) + .toList(); + + if (hard.isEmpty) { + return; + } + + final DateTime last = hard.first.startedAt!; + + if (IntegrityEngine.recoveredEnough(last, program.lowerBodyRecoveryHours)) { + return; + } + + final int elapsed = DateTime.now().difference(last).inHours; + + connection.onRecoveryBlocked(program.lowerBodyRecoveryHours - elapsed); + } +} diff --git a/frontend/lib/Grounded/utils/CapacityEngine.dart b/frontend/lib/Grounded/utils/CapacityEngine.dart new file mode 100644 index 0000000..03f6e67 --- /dev/null +++ b/frontend/lib/Grounded/utils/CapacityEngine.dart @@ -0,0 +1,134 @@ +import '../about/external/data/Commitment.dart'; +import '../about/internal/application/CapacityProfile.dart'; + +/// The result of a capacity check at plan time. +class CapacityVerdict { + /// Minutes the plan actually implies, after the estimation multipliers. + double projectedMinutes; + + /// Minutes history says get done on this weekday, at p50. + double historicalMinutes; + + /// The ceiling — historical minutes times the headroom factor. + double allowedMinutes; + + bool blocked; + + /// Minutes that must come out of the plan before it will be accepted. + double excessMinutes; + + String message; + + CapacityVerdict({ + this.projectedMinutes = 0, + this.historicalMinutes = 0, + this.allowedMinutes = 0, + this.blocked = false, + this.excessMinutes = 0, + this.message = "", + }); +} + +/// Chronic overdue is usually an overcommitment problem misdiagnosed as a +/// laziness problem. This is the check that catches it. +/// +/// ``` +/// projected_load = Sum (est_minutes x your_multiplier[category]) +/// if projected_load > 0.85 x p50(historical_completed_minutes[weekday]): +/// block, and force removal +/// ``` +class CapacityEngine { + /// Plan against 85% of what you historically get done, not 100%. + static const double headroom = 0.85; + + /// The plan as the app believes it, not as you estimated it. Your estimates + /// are wrong and the multiplier is applied silently. + static double projectedLoad( + List plan, + CapacityProfile profile, + ) { + double total = 0; + for (Commitment commitment in plan) { + total = + total + (commitment.estMinutes * profile.multiplierFor(commitment.category)); + } + return total; + } + + static CapacityVerdict check( + List plan, + CapacityProfile profile, + int weekday, + ) { + final double projected = projectedLoad(plan, profile); + final double historical = profile.capacityFor(weekday); + final double allowed = historical * headroom; + + // No history yet — the app has not earned the right to block anything. + if (historical <= 0) { + return CapacityVerdict( + projectedMinutes: projected, + historicalMinutes: 0, + allowedMinutes: 0, + blocked: false, + message: "Not enough history yet to judge this plan.", + ); + } + + if (projected <= allowed) { + return CapacityVerdict( + projectedMinutes: projected, + historicalMinutes: historical, + allowedMinutes: allowed, + blocked: false, + message: "This plan fits what you actually get done.", + ); + } + + final double excess = projected - allowed; + + return CapacityVerdict( + projectedMinutes: projected, + historicalMinutes: historical, + allowedMinutes: allowed, + blocked: true, + excessMinutes: excess, + message: + "You have allocated ${_hours(projected)} of tasks into a day where you historically complete ${_hours(historical)}. Cut ${_hours(excess)}.", + ); + } + + /// The learned multiplier for a category, from what you said against what it + /// took. Surfaced in the weekly review rather than hidden. + static double learnMultiplier( + List estimatedMinutes, + List actualMinutes, + ) { + if (estimatedMinutes.isEmpty || + estimatedMinutes.length != actualMinutes.length) { + return 1.0; + } + + double estimated = 0; + double actual = 0; + + for (int index = 0; index < estimatedMinutes.length; index++) { + estimated = estimated + estimatedMinutes[index]; + actual = actual + actualMinutes[index]; + } + + if (estimated <= 0) { + return 1.0; + } + + return actual / estimated; + } + + static String _hours(double minutes) { + if (minutes < 60) { + return "${minutes.round()}min"; + } + final double hours = minutes / 60; + return "${hours.toStringAsFixed(1)}h"; + } +} diff --git a/frontend/lib/Grounded/utils/Colors.dart b/frontend/lib/Grounded/utils/Colors.dart new file mode 100644 index 0000000..a171ee0 --- /dev/null +++ b/frontend/lib/Grounded/utils/Colors.dart @@ -0,0 +1,115 @@ +import 'package:flutter/material.dart'; + +MaterialColor createMaterialColor(Color color) { + List strengths = [.05]; + final swatch = {}; + final int r = (color.r * 255.0).round() & 0xff, + g = (color.g * 255.0).round() & 0xff, + b = (color.b * 255.0).round() & 0xff; + + for (int i = 1; i < 10; i++) { + strengths.add(0.1 * i); + } + for (var strength in strengths) { + final double ds = 0.5 - strength; + swatch[(strength * 1000).round()] = Color.fromRGBO( + r + ((ds < 0 ? r : (255 - r)) * ds).round(), + g + ((ds < 0 ? g : (255 - g)) * ds).round(), + b + ((ds < 0 ? b : (255 - b)) * ds).round(), + 1, + ); + } + return MaterialColor(color.toARGB32(), swatch); +} + +// ── Main scheme ─────────────────────────────────────────────────────────────── +// Grounded reads as an institution, not a toy: near-black ink, one hard +// accent, and a warning palette that carries the enforcement tiers. +var colorWhite = const Color(0xFFFFFFFF); +var colorPrimary = const Color(0xFF2F6F4E); +var colorPrimaryDark = const Color(0xFF141414); +var colorPrimaryDark2 = const Color(0xFF232323); +var colorPrimaryLight = const Color(0xFFEDEEEA); +var colorPrimaryLight2 = const Color(0xFFF4F5F2); + +var colorAccent = const Color(0xFFB5442F); +var colorMilkWhite = const Color(0xFFD9D8CE); +var colorMuted = const Color(0xFFE6EAE6); +var colorSecondary = const Color(0xFFF1F4F1); + +var colorPositive = const Color(0xFF2F6F4E); +var colorNegative = const Color(0xFFB3261E); +var colorGrey = const Color(0xFFA8A8A8); +var colorGrey2 = const Color(0xFF6E6E6E); +var colorGrey3 = const Color(0xFF8A8A8A); +var colorTinted = const Color(0xFFE0A88F); +var colorWarmYellow = const Color(0xFFD9A404); +var colorReddish = const Color(0xFFC0392B); +var colorDarkBlue = const Color(0xFF10161F); + +// ── Semantic ────────────────────────────────────────────────────────────────── +const Color colorSuccess = Color(0xFF2E7D4F); +const Color colorWarning = Color(0xFFC98A04); +const Color colorDestructive = Color(0xFFA32C1C); +const Color colorBlack = Color(0xFF000000); +const Color colorBorder = Color(0x14000000); +const Color colorDivider = Color(0x12000000); + +// ── Standing tiers ──────────────────────────────────────────────────────────── +// Each standing has one colour used consistently everywhere it appears, so the +// tier is legible at a glance without reading the label. +const Color colorStandingGood = Color(0xFF2F6F4E); +const Color colorStandingGoodBg = Color(0xFFE4EFE8); +const Color colorStandingWarned = Color(0xFFC98A04); +const Color colorStandingWarnedBg = Color(0xFFFAF0D8); +const Color colorStandingGrounded = Color(0xFFB5442F); +const Color colorStandingGroundedBg = Color(0xFFF7E3DE); +const Color colorStandingLockdown = Color(0xFF7A1F14); +const Color colorStandingLockdownBg = Color(0xFFEFD6D2); + +// ── Commitment classes ──────────────────────────────────────────────────────── +const Color colorClassNonNegotiable = Color(0xFF7A1F14); +const Color colorClassNonNegotiableBg = Color(0xFFF3DFDB); +const Color colorClassStandard = Color(0xFF2C4A63); +const Color colorClassStandardBg = Color(0xFFE0E8EF); +const Color colorClassElective = Color(0xFF6E6E6E); +const Color colorClassElectiveBg = Color(0xFFEDEDED); + +// ── Debt / charting ─────────────────────────────────────────────────────────── +const Color colorDebtLine = Color(0xFFB5442F); +const Color colorDebtFill = Color(0x1AB5442F); +const Color colorChartGrid = Color(0x0F000000); +const Color colorChartAxis = Color(0xFF8A8A8A); + +// ── Surfaces ────────────────────────────────────────────────────────────────── +const Color colorCard = Color(0xFFFFFFFF); +const Color colorSheetBackground = Color(0xFFF6F5F2); +const Color colorInset = Color(0xFFFAFAF8); + +// ── Dark surfaces ───────────────────────────────────────────────────────────── +const Color colorDarkBg = Color(0xFF080808); +const Color colorDarkCard = Color(0xFF141414); +const Color colorDarkSurface = Color(0xFF1C1C1C); +const Color colorDarkBorder = Color(0xFF262626); + +Color getColorFromHex(String hexColor) { + if (hexColor.isEmpty || hexColor.length <= 6) { + hexColor = "#ffffff"; + } + + hexColor = hexColor.toUpperCase().replaceAll("#", ""); + + if (hexColor.length == 6) { + hexColor = "FF$hexColor"; + } + + try { + return Color(int.parse(hexColor, radix: 16)); + } catch (e) { + return colorWhite; + } +} + +String colorToHex(Color color) { + return '#${color.toARGB32().toRadixString(16).padLeft(8, '0').toUpperCase()}'; +} diff --git a/frontend/lib/Grounded/utils/CommonUtils.dart b/frontend/lib/Grounded/utils/CommonUtils.dart new file mode 100644 index 0000000..cf1d2bc --- /dev/null +++ b/frontend/lib/Grounded/utils/CommonUtils.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +import '../about/external/data/Commitment.dart'; +import '../about/internal/application/CommitmentClass.dart'; +import '../about/internal/application/Standing.dart'; +import 'Colors.dart'; + +/// Formatting and small shared derivations. Anything a screen would otherwise +/// inline twice belongs here. + +/// "Mon 06:00–08:00" — the window, which is the whole point. +String formatWindow(Commitment commitment) { + if (commitment.dueStart == null || commitment.dueEnd == null) { + return "No window set"; + } + + final DateFormat day = DateFormat('EEE'); + final DateFormat time = DateFormat('HH:mm'); + + final String startDay = day.format(commitment.dueStart!); + final String startTime = time.format(commitment.dueStart!); + final String endTime = time.format(commitment.dueEnd!); + + final bool sameDay = commitment.dueStart!.day == commitment.dueEnd!.day && + commitment.dueStart!.month == commitment.dueEnd!.month; + + if (sameDay) { + return "$startDay $startTime–$endTime"; + } + + return "$startDay $startTime – ${day.format(commitment.dueEnd!)} $endTime"; +} + +String formatDate(DateTime? value) { + if (value == null) { + return ""; + } + return DateFormat('d MMM yyyy').format(value); +} + +String formatDateTime(DateTime? value) { + if (value == null) { + return ""; + } + return DateFormat('d MMM, HH:mm').format(value); +} + +/// "3h 20m" from raw minutes. +String formatMinutes(num minutes) { + final int total = minutes.round(); + if (total < 60) { + return "${total}m"; + } + final int hours = total ~/ 60; + final int remainder = total % 60; + if (remainder == 0) { + return "${hours}h"; + } + return "${hours}h ${remainder}m"; +} + +/// "12:04" from raw seconds — for the proof timer and rest clocks. +String formatClock(int seconds) { + final int safe = seconds < 0 ? 0 : seconds; + final int minutes = safe ~/ 60; + final int remainder = safe % 60; + return "${minutes.toString().padLeft(2, '0')}:${remainder.toString().padLeft(2, '0')}"; +} + +/// Debt is shown to one decimal — precise enough to move visibly when you +/// clear something, coarse enough not to look like a lie. +String formatDebt(double debt) { + return debt.toStringAsFixed(1); +} + +/// "2 days overdue", "Due in 40m", "Window closes in 3h". +String overdueLabel(Commitment commitment) { + if (commitment.dueEnd == null) { + return ""; + } + + final Duration difference = DateTime.now().difference(commitment.dueEnd!); + + if (difference.isNegative) { + final Duration remaining = difference.abs(); + if (remaining.inHours < 1) { + return "Closes in ${remaining.inMinutes}m"; + } + if (remaining.inDays < 1) { + return "Closes in ${remaining.inHours}h"; + } + return "Closes in ${remaining.inDays}d"; + } + + if (difference.inHours < 1) { + return "${difference.inMinutes}m overdue"; + } + if (difference.inDays < 1) { + return "${difference.inHours}h overdue"; + } + if (difference.inDays == 1) { + return "1 day overdue"; + } + return "${difference.inDays} days overdue"; +} + +Color standingColor(Standing standing) { + switch (standing) { + case Standing.Good: + return colorStandingGood; + case Standing.Warned: + return colorStandingWarned; + case Standing.Grounded: + return colorStandingGrounded; + case Standing.Lockdown: + return colorStandingLockdown; + } +} + +Color standingBackground(Standing standing) { + switch (standing) { + case Standing.Good: + return colorStandingGoodBg; + case Standing.Warned: + return colorStandingWarnedBg; + case Standing.Grounded: + return colorStandingGroundedBg; + case Standing.Lockdown: + return colorStandingLockdownBg; + } +} + +Color classColor(CommitmentClass value) { + switch (value) { + case CommitmentClass.NonNegotiable: + return colorClassNonNegotiable; + case CommitmentClass.Standard: + return colorClassStandard; + case CommitmentClass.Elective: + return colorClassElective; + } +} + +Color classBackground(CommitmentClass value) { + switch (value) { + case CommitmentClass.NonNegotiable: + return colorClassNonNegotiableBg; + case CommitmentClass.Standard: + return colorClassStandardBg; + case CommitmentClass.Elective: + return colorClassElectiveBg; + } +} + +/// The weekday name for a 1..7 index. +String weekdayName(int weekday) { + const List names = [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", + ]; + if (weekday < 1 || weekday > 7) { + return ""; + } + return names[weekday - 1]; +} + +/// "7pm" for an hour-of-day index. +String hourLabel(int hour) { + if (hour < 0) { + return ""; + } + if (hour == 0) { + return "midnight"; + } + if (hour < 12) { + return "${hour}am"; + } + if (hour == 12) { + return "noon"; + } + return "${hour - 12}pm"; +} diff --git a/frontend/lib/Grounded/utils/DebtEngine.dart b/frontend/lib/Grounded/utils/DebtEngine.dart new file mode 100644 index 0000000..1f8dc30 --- /dev/null +++ b/frontend/lib/Grounded/utils/DebtEngine.dart @@ -0,0 +1,159 @@ +import 'dart:math'; + +import '../about/external/data/Commitment.dart'; +import '../about/external/data/Habit.dart'; +import '../about/internal/application/CommitmentClass.dart'; +import '../about/internal/application/CommitmentStatus.dart'; + +/// The single number the whole system runs on. +/// +/// ``` +/// debt = Sum over open/missed commitments: +/// w(class) x severity(days_overdue) x recency_decay(t) +/// +/// w(class): non-negotiable 5.0 | standard 2.0 | elective 0.0 +/// severity(d): 1 + log2(1 + d) +/// recency_decay: 0.5 ^ (days_since / half_life) +/// abandonment: one-time +2x w(class), no decay for 30 days +/// late_complete: debt reduced to 30% of accrued, not 0 +/// ``` +/// +/// Sublinear severity is the load-bearing choice: with linear growth a single +/// ancient task swamps the score and the number stops meaning anything. +class DebtEngine { + /// Half-life of the recency decay, in days. + static const double halfLifeDays = 14; + + /// Multiplier applied once when a commitment is abandoned. + static const double abandonmentMultiplier = 2.0; + + /// Days an abandonment resists decay. + static const int abandonmentProtectionDays = 30; + + /// What a late completion leaves behind. Never zero — otherwise you learn + /// that everything is negotiable. + static const double lateCompleteRetention = 0.30; + + /// Debt weight per habit shortfall unit. + static const double habitShortfallWeight = 1.0; + + /// `severity(d) = 1 + log2(1 + d)`. + static double severity(int daysOverdue) { + final int days = daysOverdue < 0 ? 0 : daysOverdue; + return 1 + (log(1 + days) / ln2); + } + + /// `recency_decay(t) = 0.5 ^ (days_since / half_life)`. + static double recencyDecay(int daysSince) { + final int days = daysSince < 0 ? 0 : daysSince; + return pow(0.5, days / halfLifeDays).toDouble(); + } + + /// Debt contributed by one commitment, as of [now]. + static double commitmentDebt(Commitment commitment, {DateTime? now}) { + final DateTime moment = now ?? DateTime.now(); + + final double weight = classWeight(commitment.commitmentClass); + + // Electives never accrue debt, whatever happens to them. + if (weight == 0) { + return 0; + } + + // Cleanly completed and archived items are settled. + if (commitment.status == CommitmentStatus.Completed || + commitment.status == CommitmentStatus.Archived) { + return 0; + } + + if (commitment.dueEnd == null) { + return 0; + } + + final int daysSince = moment.difference(commitment.dueEnd!).inDays; + + // The window is still open — nothing is owed yet. + if (daysSince < 0) { + return 0; + } + + if (commitment.status == CommitmentStatus.Abandoned) { + // Abandonment is the most expensive outcome and resists decay for a + // month, so it cannot be waited out. + final double base = weight * abandonmentMultiplier * severity(daysSince); + if (daysSince <= abandonmentProtectionDays) { + return base; + } + return base * recencyDecay(daysSince - abandonmentProtectionDays); + } + + final double accrued = + weight * severity(daysSince) * recencyDecay(daysSince); + + if (commitment.status == CommitmentStatus.LateCompleted) { + return accrued * lateCompleteRetention; + } + + return accrued; + } + + /// Debt contributed by a habit. A single miss costs nothing — only falling + /// below the frequency target in the rolling window does. + static double habitDebt(Habit habit) { + if (!habit.behindTarget) { + return 0; + } + return habit.shortfall * habitShortfallWeight; + } + + /// The whole score, as of [now]. + static double totalDebt( + List commitments, { + List habits = const [], + DateTime? now, + }) { + final DateTime moment = now ?? DateTime.now(); + + double total = 0; + + for (Commitment commitment in commitments) { + total = total + commitmentDebt(commitment, now: moment); + } + + for (Habit habit in habits) { + total = total + habitDebt(habit); + } + + return total; + } + + /// Open items past their window — the count that [Thresholds.maxOpenOverdue] + /// caps. + static int openOverdueCount(List commitments) { + return commitments + .where((commitment) => + commitment.status == CommitmentStatus.Overdue || + (commitment.status == CommitmentStatus.Open && + commitment.windowClosed)) + .length; + } + + /// Missed non-negotiables — three of these force Grounded regardless of the + /// numeric score. + static int missedNonNegotiables(List commitments) { + return commitments + .where((commitment) => + commitment.commitmentClass == CommitmentClass.NonNegotiable && + (commitment.status == CommitmentStatus.Overdue || + commitment.status == CommitmentStatus.Abandoned || + (commitment.status == CommitmentStatus.Open && + commitment.windowClosed))) + .length; + } + + /// What clearing this item would remove from the score — used to show the + /// user the actual price of each row in the overdue queue. + static double reliefFromClearing(Commitment commitment, {DateTime? now}) { + return commitmentDebt(commitment, now: now); + } +} diff --git a/frontend/lib/Grounded/utils/ExcuseAnalyser.dart b/frontend/lib/Grounded/utils/ExcuseAnalyser.dart new file mode 100644 index 0000000..6e20707 --- /dev/null +++ b/frontend/lib/Grounded/utils/ExcuseAnalyser.dart @@ -0,0 +1,211 @@ +import '../about/external/data/CommitmentEvent.dart'; +import '../about/external/data/ExcuseCluster.dart'; +import '../about/internal/application/EventType.dart'; + +/// Clusters excuses over time and turns the pattern into a confrontation: +/// "Too tired has appeared 14 times this month, 11 of them on gym days, +/// 9 of them after 7pm. Consider moving gym to morning." +/// +/// The on-device pass is a cheap keyword bucketing so the confrontation works +/// offline; the server refines clusters and overwrites [ExcuseCluster.insight]. +class ExcuseAnalyser { + /// Excuse families the local pass recognises. Order matters — the first + /// family whose keyword appears wins. + static const Map> families = >{ + "Too tired": ["tired", "exhausted", "knackered", "no energy", "sleepy"], + "No time": ["no time", "busy", "ran out of time", "swamped"], + "Not feeling it": ["not feeling", "no motivation", "cant be", "cannot be"], + "Unwell": ["sick", "ill", "headache", "pain", "sore", "injured"], + "Interrupted": ["interrupted", "came up", "emergency", "had to"], + "Forgot": ["forgot", "slipped my mind", "missed it"], + "Weather": ["rain", "cold", "hot", "weather"], + "Travel": ["travel", "away", "trip", "commute", "traffic"], + }; + + static const String unclustered = "Other"; + + /// The family this excuse belongs to. + static String classify(String excuse) { + final String text = excuse.toLowerCase(); + + for (MapEntry> family in families.entries) { + for (String keyword in family.value) { + if (text.contains(keyword)) { + return family.key; + } + } + } + + return unclustered; + } + + /// Build the taxonomy from the event log. Only deferrals and misses carry + /// excuses worth clustering. + static List cluster(List events) { + final Map clusters = {}; + final Map> categoryCounts = + >{}; + + for (CommitmentEvent event in events) { + if (event.event != EventType.DEFERRED && + event.event != EventType.MISSED && + event.event != EventType.ABANDONED) { + continue; + } + + if (event.excuseText.isEmpty) { + continue; + } + + final String label = classify(event.excuseText); + + final ExcuseCluster cluster = + clusters.putIfAbsent(label, () => ExcuseCluster(label: label)); + + cluster.occurrences = cluster.occurrences + 1; + + if (event.at != null) { + final int weekday = event.at!.weekday; + final int hour = event.at!.hour; + cluster.byWeekday[weekday] = (cluster.byWeekday[weekday] ?? 0) + 1; + cluster.byHour[hour] = (cluster.byHour[hour] ?? 0) + 1; + } + + if (event.excuseClusterId != null && + event.excuseClusterId!.isNotEmpty) { + final Map counts = + categoryCounts.putIfAbsent(label, () => {}); + counts[event.excuseClusterId!] = + (counts[event.excuseClusterId!] ?? 0) + 1; + } + } + + final List result = clusters.values.toList(); + + for (ExcuseCluster cluster in result) { + cluster.dominantCategory = _dominant(categoryCounts[cluster.label]); + cluster.insight = describe(cluster); + } + + result.sort((a, b) => b.occurrences.compareTo(a.occurrences)); + + return result; + } + + /// The confrontation copy. Only the concentrations that are actually + /// meaningful get mentioned — a flat distribution says nothing. + static String describe(ExcuseCluster cluster) { + if (cluster.occurrences < 3) { + return ""; + } + + final StringBuffer buffer = StringBuffer(); + + buffer.write( + "'${cluster.label}' has appeared ${cluster.occurrences} times"); + + final MapEntry? weekday = _peak(cluster.byWeekday); + if (weekday != null && weekday.value >= (cluster.occurrences * 0.4)) { + buffer.write(", ${weekday.value} of them on ${_weekdayName(weekday.key)}s"); + } + + final MapEntry? hour = _peak(cluster.byHour); + if (hour != null && hour.value >= (cluster.occurrences * 0.35)) { + buffer.write(", ${hour.value} of them after ${_hourLabel(hour.key)}"); + } + + buffer.write("."); + + final String suggestion = _suggest(cluster, weekday, hour); + if (suggestion.isNotEmpty) { + buffer.write(" $suggestion"); + } + + return buffer.toString(); + } + + static String _suggest( + ExcuseCluster cluster, + MapEntry? weekday, + MapEntry? hour, + ) { + if (hour == null) { + return ""; + } + + if (hour.key >= 18 && cluster.label == "Too tired") { + return "Consider moving this to the morning."; + } + + if (hour.key >= 18) { + return "Evenings are not working for this. Try scheduling it earlier."; + } + + if (weekday != null) { + return "${_weekdayName(weekday.key)} is where this keeps failing."; + } + + return ""; + } + + static MapEntry? _peak(Map histogram) { + if (histogram.isEmpty) { + return null; + } + + MapEntry? peak; + for (MapEntry entry in histogram.entries) { + if (peak == null || entry.value > peak.value) { + peak = entry; + } + } + return peak; + } + + static String _dominant(Map? counts) { + if (counts == null || counts.isEmpty) { + return ""; + } + + String label = ""; + int best = 0; + + for (MapEntry entry in counts.entries) { + if (entry.value > best) { + best = entry.value; + label = entry.key; + } + } + + return label; + } + + static String _weekdayName(int weekday) { + const List names = [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", + ]; + if (weekday < 1 || weekday > 7) { + return ""; + } + return names[weekday - 1]; + } + + static String _hourLabel(int hour) { + if (hour == 0) { + return "midnight"; + } + if (hour < 12) { + return "${hour}am"; + } + if (hour == 12) { + return "noon"; + } + return "${hour - 12}pm"; + } +} diff --git a/frontend/lib/Grounded/utils/GuardrailEngine.dart b/frontend/lib/Grounded/utils/GuardrailEngine.dart new file mode 100644 index 0000000..e40e8d6 --- /dev/null +++ b/frontend/lib/Grounded/utils/GuardrailEngine.dart @@ -0,0 +1,44 @@ +/// The counterweights. An app built on guilt has an obvious failure mode: the +/// people who need it most delete it during their worst week. These are not +/// nice-to-haves — they are the retention strategy. +class GuardrailEngine { + /// Debt increase over the window that counts as a spike. + static const double debtSpikeDelta = 15; + + /// App opens per week below which engagement counts as dropped. + static const int engagementFloor = 3; + + /// Readiness score below which inputs count as degraded. + static const int readinessFloor = 4; + + /// Distress is the conjunction, not any single signal: debt spiking *and* + /// engagement dropping *and* readiness degrading. Strictness must never be + /// the response to someone who is actually struggling. + static bool detectDistress({ + required double debtDelta, + required int appOpensThisWeek, + required int meanReadiness, + }) { + final bool debtSpiking = debtDelta >= debtSpikeDelta; + final bool disengaging = appOpensThisWeek <= engagementFloor; + final bool degrading = meanReadiness > 0 && meanReadiness <= readinessFloor; + + return debtSpiking && disengaging && degrading; + } + + /// How many amnesty tokens remain this month. Rationed so they feel + /// valuable, but they exist so a bad flu does not destroy three months of + /// progress. + static int tokensRemaining(int granted, int spent) { + final int remaining = granted - spent; + return remaining > 0 ? remaining : 0; + } + + static bool canSpendAmnesty(int granted, int spent) { + return tokensRemaining(granted, spent) > 0; + } + + /// When distressed, the plan is cut to this many non-negotiables and nothing + /// else is asked for. + static const int distressPlanSize = 3; +} diff --git a/frontend/lib/Grounded/utils/Images.dart b/frontend/lib/Grounded/utils/Images.dart new file mode 100644 index 0000000..1262b80 --- /dev/null +++ b/frontend/lib/Grounded/utils/Images.dart @@ -0,0 +1,12 @@ +const String imagePath = "assets/images"; + +const String iconPath = "assets/icons"; + +/// The anchor mark, matching the native splash so the handover is invisible. +const String splashMark = "$iconPath/splash.png"; + +const String logoMark = "$iconPath/icon.png"; + +const String loadingBg = "$imagePath/loading.jpg"; + +const String emptyQueue = "$imagePath/empty_queue.png"; diff --git a/frontend/lib/Grounded/utils/IntegrityEngine.dart b/frontend/lib/Grounded/utils/IntegrityEngine.dart new file mode 100644 index 0000000..5031daa --- /dev/null +++ b/frontend/lib/Grounded/utils/IntegrityEngine.dart @@ -0,0 +1,191 @@ +import '../about/external/data/ExercisePrescription.dart'; +import '../about/external/data/SessionLog.dart'; +import '../about/external/data/SetLog.dart'; + +/// Did you do the session, or a watered-down version of it? +/// +/// ``` +/// integrity = 0.5 x (prescribed_sets_done / prescribed_sets) +/// + 0.3 x (1 - mean |actual_reps - target| / target) +/// + 0.2 x (hard_exercises_done / hard_exercises_prescribed) +/// ``` +/// +/// Skipping the hard exercise while doing the easy ones is the most common +/// form of self-deception in training, and the third term is what detects it. +class IntegrityEngine { + static const double setsWeight = 0.5; + static const double repsWeight = 0.3; + static const double hardWeight = 0.2; + + static double score( + SessionLog session, + List prescribed, + ) { + if (prescribed.isEmpty) { + return 0; + } + + return (setsWeight * _setCompletion(session, prescribed)) + + (repsWeight * _repAccuracy(session, prescribed)) + + (hardWeight * _hardCompletion(session, prescribed)); + } + + /// Prescribed sets actually done, capped at 1 so overshooting one lift does + /// not paper over skipping another. + static double _setCompletion( + SessionLog session, + List prescribed, + ) { + int prescribedSets = 0; + int doneSets = 0; + + for (ExercisePrescription item in prescribed) { + prescribedSets = prescribedSets + item.sets; + + final int done = session.sets + .where((entry) => + entry.exerciseId == item.exerciseId && entry.isPrescribed) + .length; + + doneSets = doneSets + (done > item.sets ? item.sets : done); + } + + if (prescribedSets == 0) { + return 0; + } + + return doneSets / prescribedSets; + } + + /// How close the reps landed to target, averaged across prescribed sets. + static double _repAccuracy( + SessionLog session, + List prescribed, + ) { + double totalError = 0; + int counted = 0; + + for (ExercisePrescription item in prescribed) { + if (item.targetReps <= 0) { + continue; + } + + final List done = session.sets + .where((entry) => + entry.exerciseId == item.exerciseId && entry.isPrescribed) + .toList(); + + for (SetLog entry in done) { + final double error = + (entry.reps - item.targetReps).abs() / item.targetReps; + totalError = totalError + (error > 1 ? 1 : error); + counted = counted + 1; + } + } + + if (counted == 0) { + return 0; + } + + return 1 - (totalError / counted); + } + + /// The term that catches cherry-picking. "Hard" comes from your historical + /// RPE on that exercise, not a static label. + static double _hardCompletion( + SessionLog session, + List prescribed, + ) { + final List hard = + prescribed.where((item) => item.hard).toList(); + + if (hard.isEmpty) { + return 1; + } + + int done = 0; + + for (ExercisePrescription item in hard) { + final bool touched = session.sets.any((entry) => + entry.exerciseId == item.exerciseId && entry.isPrescribed); + if (touched) { + done = done + 1; + } + } + + return done / hard.length; + } + + /// Whether an exercise counts as hard for this user, derived from historical + /// RPE rather than asserted up front. + static bool isHard(List historicalRpe, {double cutoff = 8.0}) { + if (historicalRpe.isEmpty) { + return false; + } + + double total = 0; + for (double value in historicalRpe) { + total = total + value; + } + + return (total / historicalRpe.length) >= cutoff; + } + + /// Weekly volume per muscle group, for under-target warnings — "you went to + /// the gym" is not a metric. + static Map weeklySetsByMuscleGroup(List sessions) { + final Map volume = {}; + + for (SessionLog session in sessions) { + for (SetLog entry in session.sets) { + if (entry.muscleGroup.isEmpty) { + continue; + } + volume[entry.muscleGroup] = (volume[entry.muscleGroup] ?? 0) + 1; + } + } + + return volume; + } + + /// Total plyometric ground contacts across the week. + static int weeklyContacts(List sessions) { + int total = 0; + for (SessionLog session in sessions) { + total = total + session.contacts; + } + return total; + } + + /// Plyo is the one modality where the app stops you rather than pushes you. + /// CNS and connective tissue do not recover on a motivation schedule. + static bool contactCeilingBreached(List sessions, int ceiling) { + if (ceiling <= 0) { + return false; + } + return weeklyContacts(sessions) >= ceiling; + } + + /// Whether enough recovery has passed since the last high-intensity lower + /// body session. + static bool recoveredEnough( + DateTime? lastHighIntensityLowerBody, + int requiredHours, { + DateTime? now, + }) { + if (lastHighIntensityLowerBody == null) { + return true; + } + final DateTime moment = now ?? DateTime.now(); + return moment.difference(lastHighIntensityLowerBody).inHours >= + requiredHours; + } + + /// Training through a deload logs as non-compliance, the same as skipping. + static bool violatesDeload(SessionLog session, bool isDeloadWeek) { + if (!isDeloadWeek) { + return false; + } + return session.sets.any((entry) => entry.rpe >= 8); + } +} diff --git a/frontend/lib/Grounded/utils/ObjectConvertors.dart b/frontend/lib/Grounded/utils/ObjectConvertors.dart new file mode 100644 index 0000000..09024aa --- /dev/null +++ b/frontend/lib/Grounded/utils/ObjectConvertors.dart @@ -0,0 +1,116 @@ +import '../about/external/data/Commitment.dart'; +import '../about/external/data/CommitmentEvent.dart'; +import '../about/external/data/DebtEntry.dart'; +import '../about/external/data/ExcuseCluster.dart'; +import '../about/external/data/ExercisePrescription.dart'; +import '../about/external/data/Goal.dart'; +import '../about/external/data/Habit.dart'; +import '../about/external/data/Program.dart'; +import '../about/external/data/RoutineChain.dart'; +import '../about/external/data/SessionLog.dart'; +import '../about/external/data/SessionTemplate.dart'; +import '../about/external/data/SetLog.dart'; +import '../about/external/data/StandingChange.dart'; + +/// List parsing lives here. ViewModels never inline +/// `.map((e) => T.fromJson(e)).toList()` — add a convertor if one is missing. + +List getCommitmentList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => Commitment.fromJson(item)).toList(); +} + +List getGoalList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => Goal.fromJson(item)).toList(); +} + +List getCommitmentEventList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => CommitmentEvent.fromJson(item)).toList(); +} + +List getDebtEntryList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => DebtEntry.fromJson(item)).toList(); +} + +List getStandingChangeList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => StandingChange.fromJson(item)).toList(); +} + +List getExcuseClusterList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => ExcuseCluster.fromJson(item)).toList(); +} + +List getHabitList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => Habit.fromJson(item)).toList(); +} + +List getRoutineChainList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => RoutineChain.fromJson(item)).toList(); +} + +List getProgramList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => Program.fromJson(item)).toList(); +} + +List getSessionTemplateList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => SessionTemplate.fromJson(item)).toList(); +} + +List getPrescriptionList(dynamic data) { + if (data == null) { + return []; + } + return (data as List) + .map((item) => ExercisePrescription.fromJson(item)) + .toList(); +} + +List getSessionLogList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => SessionLog.fromJson(item)).toList(); +} + +List getSetLogList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => SetLog.fromJson(item)).toList(); +} + +List getStringList(dynamic data) { + if (data == null) { + return []; + } + return (data as List).map((item) => item.toString()).toList(); +} diff --git a/frontend/lib/Grounded/utils/StandingEngine.dart b/frontend/lib/Grounded/utils/StandingEngine.dart new file mode 100644 index 0000000..ad3528f --- /dev/null +++ b/frontend/lib/Grounded/utils/StandingEngine.dart @@ -0,0 +1,91 @@ +import '../about/external/data/Commitment.dart'; +import '../about/internal/application/Standing.dart'; +import 'DebtEngine.dart'; +import 'Thresholds.dart'; + +/// Standing is computed continuously and drives real consequences. It is +/// derived, never set — there is no way to talk your way up a tier. +class StandingEngine { + /// The standing implied by the current debt and the missed-non-negotiable + /// count. Sick mode never escalates: strictness must never be the response + /// to someone who is actually struggling. + static Standing evaluate( + double debtScore, { + int missedNonNegotiables = 0, + bool sickMode = false, + bool distressed = false, + }) { + if (sickMode || distressed) { + return Standing.Good; + } + + if (debtScore >= Thresholds.lockdownThreshold) { + return Standing.Lockdown; + } + + if (debtScore >= Thresholds.groundedThreshold || + missedNonNegotiables >= Thresholds.nonNegotiableMissesForGrounded) { + return Standing.Grounded; + } + + if (debtScore >= Thresholds.warnedThreshold) { + return Standing.Warned; + } + + return Standing.Good; + } + + /// Evaluate straight from the commitment list. + static Standing evaluateFor( + List commitments, { + bool sickMode = false, + bool distressed = false, + DateTime? now, + }) { + return evaluate( + DebtEngine.totalDebt(commitments, now: now), + missedNonNegotiables: DebtEngine.missedNonNegotiables(commitments), + sickMode: sickMode, + distressed: distressed, + ); + } + + /// Whether a new commitment of this class may be created right now. + static bool permitsNewCommitment(Standing standing, {bool elective = false}) { + if (elective) { + return canAddElectives(standing); + } + return canAddCommitments(standing); + } + + /// Grounded replaces the home screen with the overdue queue — you do not get + /// to look at your nice plans, only at your mess. + static bool showsOverdueQueueAsHome(Standing standing) { + return standing == Standing.Grounded || standing == Standing.Lockdown; + } + + /// Lockdown puts a blocking interstitial in front of the app that must be + /// cleared one item at a time. + static bool blocksAppOnOpen(Standing standing) { + return standing == Standing.Lockdown; + } + + /// Whether the accountability partner is auto-notified at this tier. + static bool notifiesPartner(Standing standing) { + return standing == Standing.Grounded || standing == Standing.Lockdown; + } + + /// Debt still to shed before dropping a tier. Zero when already at Good. + static double debtToNextTierDown(double debtScore, Standing standing) { + switch (standing) { + case Standing.Lockdown: + return debtScore - Thresholds.lockdownThreshold; + case Standing.Grounded: + return debtScore - Thresholds.groundedThreshold; + case Standing.Warned: + return debtScore - Thresholds.warnedThreshold; + case Standing.Good: + return 0; + } + } +} diff --git a/frontend/lib/Grounded/utils/Thresholds.dart b/frontend/lib/Grounded/utils/Thresholds.dart new file mode 100644 index 0000000..68d6744 --- /dev/null +++ b/frontend/lib/Grounded/utils/Thresholds.dart @@ -0,0 +1,39 @@ +/// Configurable limits with sane defaults. These are the dials the user is +/// allowed to turn — everything else about enforcement is fixed. +class Thresholds { + /// Hard cap on unresolved overdue items. + static const int maxOpenOverdue = 5; + + /// After this, a task cannot be deferred again — only completed or + /// explicitly abandoned. + static const int maxDeferralsPerTask = 2; + + /// Minimum excuse length. Free text, no template buttons; the friction is + /// the point. + static const int minExcuseLength = 15; + + /// Standing thresholds on the debt score. + static const double warnedThreshold = 12; + static const double groundedThreshold = 30; + static const double lockdownThreshold = 60; + + /// Missed non-negotiables that force Grounded regardless of score. + static const int nonNegotiableMissesForGrounded = 3; + + /// Amnesty tokens granted per month. + static const int amnestyTokensPerMonth = 3; + + /// Capacity check headroom — plan against 85% of what you historically get + /// done, not 100%. + static const double capacityHeadroom = 0.85; + + /// Days after which an untouched elective auto-archives. + static const int electiveArchiveDays = 21; + + /// Snooze tax: each snooze shortens the next interval instead of extending + /// it, and the third becomes a full-screen alarm. + static const List snoozeLadderMinutes = [10, 5, 2]; + + /// Minutes a push can go unacknowledged before SMS fallback fires. + static const int smsFallbackMinutes = 20; +} diff --git a/frontend/lib/Grounded/utils/ToneEngine.dart b/frontend/lib/Grounded/utils/ToneEngine.dart new file mode 100644 index 0000000..33df8ed --- /dev/null +++ b/frontend/lib/Grounded/utils/ToneEngine.dart @@ -0,0 +1,117 @@ +import '../about/internal/application/EscalationTier.dart'; +import '../about/internal/application/Standing.dart'; +import '../about/internal/application/ToneLevel.dart'; + +/// All user-facing enforcement copy comes from here, so the hard cap is +/// enforceable in one place: language may criticise behaviour, never the +/// person. Nothing that mocks the user's worth ships. +class ToneEngine { + /// The escalation ladder. Disappointment is deliberately placed above anger + /// because it works better as a lever. + static EscalationTier tierFor(int unacknowledgedCount) { + if (unacknowledgedCount <= 0) { + return EscalationTier.Reminder; + } + if (unacknowledgedCount == 1) { + return EscalationTier.Nudge; + } + if (unacknowledgedCount == 2) { + return EscalationTier.Nag; + } + if (unacknowledgedCount == 3) { + return EscalationTier.Disappointed; + } + return EscalationTier.Cold; + } + + /// Notification body for a commitment at a given tier and tone. + static String nudge( + EscalationTier tier, + ToneLevel tone, + String title, + ) { + switch (tier) { + case EscalationTier.Reminder: + return "$title is due now."; + case EscalationTier.Nudge: + return "$title is still sitting there."; + case EscalationTier.Nag: + return tone == ToneLevel.Firm + ? "$title is overdue. It needs a decision." + : "Third time: $title. Do it or abandon it."; + case EscalationTier.Disappointed: + return "You said you would do $title. You have not."; + case EscalationTier.Cold: + return "$title. No more reminders about this one."; + } + } + + /// The header copy on app open, which shifts with standing. + static String standingHeadline(Standing standing, ToneLevel tone) { + switch (standing) { + case Standing.Good: + return "You are in good standing."; + case Standing.Warned: + return "You are slipping."; + case Standing.Grounded: + return tone == ToneLevel.Firm + ? "You are grounded until this is cleared." + : "Grounded. Nothing new until the queue is empty."; + case Standing.Lockdown: + return "Lockdown. One item at a time."; + } + } + + static String standingBody(Standing standing, ToneLevel tone) { + switch (standing) { + case Standing.Good: + return "Keep the plan honest and it stays this way."; + case Standing.Warned: + return "New electives are blocked. Clear some debt before adding more."; + case Standing.Grounded: + return "Your plans are hidden. This is what is actually outstanding."; + case Standing.Lockdown: + return "Complete or abandon each item below. There is no third option."; + } + } + + /// Praise is rationed but real. A parent who only criticises gets tuned out — + /// this returns empty unless something specific was genuinely earned. + static String praise(String specificAchievement) { + if (specificAchievement.isEmpty) { + return ""; + } + return specificAchievement; + } + + /// The register the app switches to when distress is detected. The strict + /// persona drops entirely — this is the difference between a product people + /// keep and one they resent. + static String distressHeadline() { + return "Let us cut this back."; + } + + static String distressBody() { + return "Something has clearly been hard lately. Pick three things that " + "genuinely matter this week and let the rest go. Nothing here is " + "counting against you right now."; + } + + /// Copy shown when a deferral is refused because the cap is spent. + static String deferralRefused(int maxDeferrals) { + return "This has been deferred $maxDeferrals times. It can now only be " + "completed or abandoned."; + } + + /// Copy shown when a non-negotiable deferral is attempted. + static String nonNegotiableRefused() { + return "Non-negotiables are not deferrable. That is what makes them " + "non-negotiable."; + } + + /// Copy shown when the excuse is too short. + static String excuseTooShort(int minimum) { + return "Write at least $minimum characters. If it is not worth explaining, " + "it is not worth deferring."; + } +} diff --git a/frontend/lib/Grounded/utils/Validators.dart b/frontend/lib/Grounded/utils/Validators.dart new file mode 100644 index 0000000..204331e --- /dev/null +++ b/frontend/lib/Grounded/utils/Validators.dart @@ -0,0 +1,80 @@ +import 'Thresholds.dart'; + +/// Field-level validation. Returns null when valid, so it drops straight into +/// a TextFormField validator. +class Validators { + static String? required(String? value, String field) { + if (value == null || value.trim().isEmpty) { + return "$field is required."; + } + return null; + } + + static String? title(String? value) { + final String? empty = required(value, "Title"); + if (empty != null) { + return empty; + } + if (value!.trim().length < 3) { + return "Give it a name you will recognise later."; + } + return null; + } + + /// The excuse gate. Free text only, minimum length, no template buttons — + /// the friction is the point. + static String? excuse(String? value) { + if (value == null || value.trim().isEmpty) { + return "An excuse is required to defer."; + } + if (value.trim().length < Thresholds.minExcuseLength) { + return "Write at least ${Thresholds.minExcuseLength} characters."; + } + return null; + } + + static String? estimateMinutes(String? value) { + final String? empty = required(value, "Estimate"); + if (empty != null) { + return empty; + } + final int? minutes = int.tryParse(value!.trim()); + if (minutes == null || minutes <= 0) { + return "Enter the minutes you think it will take."; + } + if (minutes > 720) { + return "Nothing on a daily plan takes more than 12 hours. Split it."; + } + return null; + } + + /// A window, not a date. The end must actually close after the start. + static String? window(DateTime? start, DateTime? end) { + if (start == null || end == null) { + return "Set a due window, not just a day."; + } + if (!end.isAfter(start)) { + return "The window has to close after it opens."; + } + return null; + } + + static String? username(String? value) { + final String? empty = required(value, "Username"); + if (empty != null) { + return empty; + } + return null; + } + + static String? password(String? value) { + final String? empty = required(value, "Password"); + if (empty != null) { + return empty; + } + if (value!.length < 8) { + return "Passwords are at least 8 characters."; + } + return null; + } +} diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart new file mode 100644 index 0000000..4d98518 --- /dev/null +++ b/frontend/lib/main.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; + +import 'Grounded/about/internal/application/TextType.dart'; +import 'Grounded/configs/NotificationServiceConfig.dart'; +import 'Grounded/designs/Component.dart'; +import 'Grounded/see/splash/Splash.dart'; +import 'Grounded/utils/Colors.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + await dotenv.load(fileName: ".env"); + + // Channels are created up front so a full-screen alarm can fire the first + // time a non-negotiable window closes, not the second. + await LocalNotificationEngine.init(); + + runApp(const GroundedApp()); +} + +class GroundedApp extends StatelessWidget { + const GroundedApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Grounded', + debugShowCheckedModeBanner: false, + theme: ThemeData( + disabledColor: colorGrey, + scaffoldBackgroundColor: colorPrimaryLight, + dividerColor: colorDivider, + colorScheme: ColorScheme.fromSwatch( + primarySwatch: createMaterialColor(colorPrimaryDark), + ).copyWith(error: colorNegative), + dialogTheme: DialogThemeData( + backgroundColor: colorWhite, + elevation: 0, + contentTextStyle: TextStyle( + color: colorGrey2, + fontFamily: getTextType(TextType.Regular), + fontSize: 13, + ), + titleTextStyle: TextStyle( + color: colorPrimaryDark, + fontFamily: getTextType(TextType.Bold), + fontSize: 17, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + ), + ), + home: const Splash(), + ); + } +} diff --git a/frontend/mason-lock.json b/frontend/mason-lock.json new file mode 100644 index 0000000..74c306e --- /dev/null +++ b/frontend/mason-lock.json @@ -0,0 +1 @@ +{"bricks":{"api_endpoint":{"path":"C:/Users/Alpha/Documents/Grounded/frontend/bricks/api_endpoint"},"internal_memory":{"path":"C:/Users/Alpha/Documents/Grounded/frontend/bricks/internal_memory"},"mvvc_template":{"path":"C:/Users/Alpha/Documents/Grounded/frontend/bricks/mvvc_template"}}} \ No newline at end of file diff --git a/frontend/mason.yaml b/frontend/mason.yaml new file mode 100644 index 0000000..1f9cc7e --- /dev/null +++ b/frontend/mason.yaml @@ -0,0 +1,7 @@ +bricks: + mvvc_template: + path: bricks/mvvc_template + api_endpoint: + path: bricks/api_endpoint + internal_memory: + path: bricks/internal_memory \ No newline at end of file diff --git a/frontend/pubspec.lock b/frontend/pubspec.lock new file mode 100644 index 0000000..4b43340 --- /dev/null +++ b/frontend/pubspec.lock @@ -0,0 +1,1242 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + ansicolor: + dependency: transitive + description: + name: ansicolor + sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" + url: "https://pub.dev" + source: hosted + version: "2.0.3" + archive: + dependency: transitive + description: + name: archive + sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff + url: "https://pub.dev" + source: hosted + version: "4.0.9" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + calendar_date_picker2: + dependency: "direct main" + description: + name: calendar_date_picker2 + sha256: d6ea697c7fc0eebc64c62ad2ac6232a8c38795010bd8466b687bfebcbbdd872e + url: "https://pub.dev" + source: hosted + version: "3.0.0" + camera: + dependency: "direct main" + description: + name: camera + sha256: "558230d6ce6ccea856b32d390db7e7b557adf4d9320aa614481bd3f2f608953f" + url: "https://pub.dev" + source: hosted + version: "0.12.0+2" + camera_android_camerax: + dependency: transitive + description: + name: camera_android_camerax + sha256: "4fb17b62dc25f97ec752596b749a31e80ccc9829ef95412badfdf0bc6c43a166" + url: "https://pub.dev" + source: hosted + version: "0.7.4+2" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: "866e9cd8370f8055d005c0413937a52dc1d7a472687f0ee3ce02392955aababa" + url: "https://pub.dev" + source: hosted + version: "0.10.2" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + sha256: "4524ca6eb4176b066864036ad4fe02c3e4863e63b77eadc21a5bf56824f43498" + url: "https://pub.dev" + source: hosted + version: "2.13.1" + camera_web: + dependency: transitive + description: + name: camera_web + sha256: "1245a480a113437f8d46d19c0fb90cea9db921436d9cf2ba5fb11854a1312693" + url: "https://pub.dev" + source: hosted + version: "0.3.5+4" + characters: + dependency: transitive + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.dev" + source: hosted + version: "1.4.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" + url: "https://pub.dev" + source: hosted + version: "2.0.4" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + code_assets: + dependency: transitive + description: + name: code_assets + sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + connectivity_plus: + dependency: "direct main" + description: + name: connectivity_plus + sha256: "762c99f890ca8bf87f7337236f99edd42793843bc6c3631da294a76653a54bd0" + url: "https://pub.dev" + source: hosted + version: "7.3.1" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "92c9c43c383bfa1c32079d3bc492d55d6d4318044b7b47edaff8971cbb555c51" + url: "https://pub.dev" + source: hosted + version: "0.3.5+4" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" + url: "https://pub.dev" + source: hosted + version: "1.0.9" + dbus: + dependency: transitive + description: + name: dbus + sha256: "0ce9b0a839e6dee59a37a623d2fc26a35bbbe6404213e419b0d6411023d62645" + url: "https://pub.dev" + source: hosted + version: "0.7.14" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + sha256: b4fed1b2835da9d670d7bed7db79ae2a94b0f5ad6312268158a9b5479abbacdd + url: "https://pub.dev" + source: hosted + version: "12.4.0" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + url: "https://pub.dev" + source: hosted + version: "7.0.3" + dio: + dependency: "direct main" + description: + name: dio + sha256: "0df44ebba85e503958eb75d07eedd3c86275a58c1d3eda2f2ce8f0a2c3abbb3c" + url: "https://pub.dev" + source: hosted + version: "5.11.0" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "0786d0b7295a373de356fc0af4f6f1d0ab2844ed31b19dfc5e7556b70e24212c" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + equatable: + dependency: transitive + description: + name: equatable + sha256: "3bce007a596ff8b3119c45d68aaef631272537c03d30e5d4534dd24bf4c5eaa2" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + file_picker: + dependency: "direct main" + description: + name: file_picker + sha256: f13a03000d942e476bc1ff0a736d2e9de711d2f89a95cd4c1d88f861c3348387 + url: "https://pub.dev" + source: hosted + version: "11.0.2" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + fl_chart: + dependency: "direct main" + description: + name: fl_chart + sha256: b938f77d042cbcd822936a7a359a7235bad8bd72070de1f827efc2cc297ac888 + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_animate: + dependency: "direct main" + description: + name: flutter_animate + sha256: "7befe2d3252728afb77aecaaea1dec88a89d35b9b1d2eea6d04479e8af9117b5" + url: "https://pub.dev" + source: hosted + version: "4.5.2" + flutter_dotenv: + dependency: "direct main" + description: + name: flutter_dotenv + sha256: d41da11fb497314fbf89811ec30af02d1d898b47980a129f0a8c0a1720460ba2 + url: "https://pub.dev" + source: hosted + version: "6.0.1" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7" + url: "https://pub.dev" + source: hosted + version: "0.14.4" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: "0d9035862236fe38250fe1644d7ed3b8254e34a21b2c837c9f539fbb3bba5ef1" + url: "https://pub.dev" + source: hosted + version: "21.0.0" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: e0f25e243c6c44c825bbbc6b2b2e76f7d9222362adcfe9fd780bf01923c840bd + url: "https://pub.dev" + source: hosted + version: "8.0.0" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: e7db3d5b49c2b7ecc68deba4aaaa67a348f92ee0fef34c8e4b4459dbef0d7307 + url: "https://pub.dev" + source: hosted + version: "11.0.0" + flutter_local_notifications_windows: + dependency: transitive + description: + name: flutter_local_notifications_windows + sha256: "3a2654ba104fbb52c618ebed9def24ef270228470718c43b3a6afcd5c81bef0c" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + flutter_native_splash: + dependency: "direct dev" + description: + name: flutter_native_splash + sha256: "9db4b80b044e9af17cc4b1272137fc7ace0054d879ef8210a76adc34aaf4cdff" + url: "https://pub.dev" + source: hosted + version: "2.4.8" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785" + url: "https://pub.dev" + source: hosted + version: "2.0.35" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "7686b1d6a29985dcbb808c59518226e603e3bfa7c0ddfd1a0d00e4cda77c868e" + url: "https://pub.dev" + source: hosted + version: "10.3.1" + flutter_secure_storage_darwin: + dependency: transitive + description: + name: flutter_secure_storage_darwin + sha256: "82329fa5cdf343773b1b6897dea959105a29f092454259edff92f9f6637e8149" + url: "https://pub.dev" + source: hosted + version: "0.3.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: a5f35ddab43cf5c8215d2feb4ce1957851f28c5c37e6f04335066a0602087bf5 + url: "https://pub.dev" + source: hosted + version: "3.0.1" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: "06686df417f34fe9f963ed217b7fdce250e2f637ddd6c374d7eb054549770633" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: "073a62b3aeb866ab4ce795f960413948e51e5a42a9b0c8333b6daf5bb3208a1c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: "3b7c8e068875dfd46719ff57c90d8c459c87f2302ed6b00ff006b3c9fcad1613" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + flutter_shaders: + dependency: transitive + description: + name: flutter_shaders + sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2" + url: "https://pub.dev" + source: hosted + version: "0.1.3" + flutter_spinkit: + dependency: "direct main" + description: + name: flutter_spinkit + sha256: "77850df57c00dc218bfe96071d576a8babec24cf58b2ed121c83cca4a2fdce7f" + url: "https://pub.dev" + source: hosted + version: "5.2.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + geoclue: + dependency: transitive + description: + name: geoclue + sha256: c2a998c77474fc57aa00c6baa2928e58f4b267649057a1c76738656e9dbd2a7f + url: "https://pub.dev" + source: hosted + version: "0.1.1" + geolocator: + dependency: "direct main" + description: + name: geolocator + sha256: "79939537046c9025be47ec645f35c8090ecadb6fe98eba146a0d25e8c1357516" + url: "https://pub.dev" + source: hosted + version: "14.0.2" + geolocator_android: + dependency: transitive + description: + name: geolocator_android + sha256: "86ea1654e4f61ff51466848e91c116b422d6010ea269fda0fbe1af7e9e742ce1" + url: "https://pub.dev" + source: hosted + version: "5.0.3" + geolocator_apple: + dependency: transitive + description: + name: geolocator_apple + sha256: "853803d6bb1713c094e935b4a5ae5f19c0308acf81da13fa9ff84fb4c70c0b73" + url: "https://pub.dev" + source: hosted + version: "2.3.14" + geolocator_linux: + dependency: transitive + description: + name: geolocator_linux + sha256: d64112a205931926f4363bb6bd48f14cb38e7326833041d170615586cd143797 + url: "https://pub.dev" + source: hosted + version: "0.2.4" + geolocator_platform_interface: + dependency: transitive + description: + name: geolocator_platform_interface + sha256: cdb082e4f048b69da244117b7914cc60d2a8897546ffaa4f2529c786ded7aee2 + url: "https://pub.dev" + source: hosted + version: "4.2.8" + geolocator_web: + dependency: transitive + description: + name: geolocator_web + sha256: "19e485a0f8d6a88abcf9c53cba3a4105e14b7435ed8ac1c108c067b938fe8429" + url: "https://pub.dev" + source: hosted + version: "4.1.4" + geolocator_windows: + dependency: transitive + description: + name: geolocator_windows + sha256: "175435404d20278ffd220de83c2ca293b73db95eafbdc8131fe8609be1421eb6" + url: "https://pub.dev" + source: hosted + version: "0.2.5" + get_it: + dependency: transitive + description: + name: get_it + sha256: ae78de7c3f2304b8d81f2bb6e320833e5e81de942188542328f074978cc0efa9 + url: "https://pub.dev" + source: hosted + version: "8.3.0" + group_button: + dependency: "direct main" + description: + name: group_button + sha256: "0610fcf28ed122bfb4b410fce161a390f7f2531d55d1d65c5375982001415940" + url: "https://pub.dev" + source: hosted + version: "5.3.4" + gsettings: + dependency: transitive + description: + name: gsettings + sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c" + url: "https://pub.dev" + source: hosted + version: "0.2.8" + hooks: + dependency: transitive + description: + name: hooks + sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" + http: + dependency: transitive + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + image: + dependency: "direct main" + description: + name: image + sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce + url: "https://pub.dev" + source: hosted + version: "4.8.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" + url: "https://pub.dev" + source: hosted + version: "0.20.3" + jni: + dependency: transitive + description: + name: jni + sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f + url: "https://pub.dev" + source: hosted + version: "1.0.0" + jni_flutter: + dependency: transitive + description: + name: jni_flutter + sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80" + url: "https://pub.dev" + source: hosted + version: "4.12.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + local_notifier: + dependency: "direct main" + description: + name: local_notifier + sha256: f6cfc933c6fbc961f4e52b5c880f68e41b2d3cd29aad557cc654fd211093a025 + url: "https://pub.dev" + source: hosted + version: "0.1.6" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + url: "https://pub.dev" + source: hosted + version: "0.12.19" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + url: "https://pub.dev" + source: hosted + version: "0.13.0" + meta: + dependency: transitive + description: + name: meta + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + url: "https://pub.dev" + source: hosted + version: "1.18.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + modal_bottom_sheet: + dependency: "direct main" + description: + name: modal_bottom_sheet + sha256: eac66ef8cb0461bf069a38c5eb0fa728cee525a531a8304bd3f7b2185407c67e + url: "https://pub.dev" + source: hosted + version: "3.0.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed" + url: "https://pub.dev" + source: hosted + version: "9.4.1" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + package_info_plus: + dependency: transitive + description: + name: package_info_plus + sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20" + url: "https://pub.dev" + source: hosted + version: "9.0.1" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + page_transition: + dependency: "direct main" + description: + name: page_transition + sha256: "61dac670e80ebdd7da847c21841e1dfd8f9ab483b3a0c5fddc877ad8d23b69ee" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825 + url: "https://pub.dev" + source: hosted + version: "2.1.6" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" + url: "https://pub.dev" + source: hosted + version: "2.6.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: fe54465bcc62a4564c6e4db337bbaded6c0c0fa6e10487414436d163114784f6 + url: "https://pub.dev" + source: hosted + version: "12.0.3" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" + url: "https://pub.dev" + source: hosted + version: "13.0.1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: "79dfa1df734798aa3cfdad166d3a3698c206d8813de13516ea1071b5d7e2f420" + url: "https://pub.dev" + source: hosted + version: "9.4.10" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + url: "https://pub.dev" + source: hosted + version: "0.1.3+5" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 + url: "https://pub.dev" + source: hosted + version: "4.3.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" + url: "https://pub.dev" + source: hosted + version: "7.0.2" + photo_view: + dependency: "direct main" + description: + name: photo_view + sha256: "1fc3d970a91295fbd1364296575f854c9863f225505c28c46e0a03e48960c75e" + url: "https://pub.dev" + source: hosted + version: "0.15.0" + pin_code_fields: + dependency: "direct main" + description: + name: pin_code_fields + sha256: "0ae83c636a3c1ae00bc09bb496bc554ea290b01dfe396e3c77e12d3a3f9207e9" + url: "https://pub.dev" + source: hosted + version: "9.4.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + posix: + dependency: transitive + description: + name: posix + sha256: bc1bad54ad2b735816e31f8d4600cfde6c7839975085ddfbca48b6c9f7c4044e + url: "https://pub.dev" + source: hosted + version: "6.5.2" + provider: + dependency: transitive + description: + name: provider + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + url: "https://pub.dev" + source: hosted + version: "6.1.5+1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + record_use: + dependency: transitive + description: + name: record_use + sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf + url: "https://pub.dev" + source: hosted + version: "2.5.5" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "0634e64bd719f89c012f392938e173521f535d3ecaf66558fa94a056d22b5cc7" + url: "https://pub.dev" + source: hosted + version: "2.4.27" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + smooth_page_indicator: + dependency: "direct main" + description: + name: smooth_page_indicator + sha256: "4b497e9898d095de40d246db943371183fa7482492a88391cfa8415ef94d57ba" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stacked: + dependency: "direct main" + description: + name: stacked + sha256: "5f4a6ba6cfa43c5854690de0f946eef1694250cf46ecf7859519d90bf764b1e4" + url: "https://pub.dev" + source: hosted + version: "3.5.0" + stacked_shared: + dependency: transitive + description: + name: stacked_shared + sha256: "3d69b34d87422b78a7e5123681d3f4bcdd79757170454933f68795c54812d003" + url: "https://pub.dev" + source: hosted + version: "1.4.2" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + url: "https://pub.dev" + source: hosted + version: "0.7.11" + timezone: + dependency: transitive + description: + name: timezone + sha256: "981d1020d6ef8fe1e7b3de5054e5b25579ae7c403d7734adc508ffc47668e9cb" + url: "https://pub.dev" + source: hosted + version: "0.11.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + url: "https://pub.dev" + source: hosted + version: "6.3.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: b413d49b73867ac08dd2f9890efd3cc11f2a0e577618d50843440a1fb3776c32 + url: "https://pub.dev" + source: hosted + version: "6.3.32" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0" + url: "https://pub.dev" + source: hosted + version: "6.4.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" + url: "https://pub.dev" + source: hosted + version: "3.2.5" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" + url: "https://pub.dev" + source: hosted + version: "4.6.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vibration: + dependency: "direct main" + description: + name: vibration + sha256: c6c25eb7acadfd231925a60004736443cbf63f1eb5740d6dd83a1bc09cf00f6d + url: "https://pub.dev" + source: hosted + version: "3.2.0" + vibration_platform_interface: + dependency: transitive + description: + name: vibration_platform_interface + sha256: "258c273268f8aa40c88d29741137c536874a738779b92ddb8aa32ed093721ec5" + url: "https://pub.dev" + source: hosted + version: "0.1.2" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" + url: "https://pub.dev" + source: hosted + version: "15.2.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + win32: + dependency: transitive + description: + name: win32 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e + url: "https://pub.dev" + source: hosted + version: "5.15.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + url: "https://pub.dev" + source: hosted + version: "6.6.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml new file mode 100644 index 0000000..ae103ed --- /dev/null +++ b/frontend/pubspec.yaml @@ -0,0 +1,132 @@ +name: Grounded +description: Grounded — a to-do app that doesn't believe you. + +publish_to: 'none' + +version: 1.0.0+1 + +environment: + sdk: ^3.6.2 + +dependencies: + flutter: + sdk: flutter + + cupertino_icons: ^1.0.2 + + # ── State management (MVVM) ───────────────────────────────────────────── + stacked: ^3.5.0 + + # ── HTTP / env ────────────────────────────────────────────────────────── + dio: ^5.7.0 + flutter_dotenv: ^6.0.1 + + # ── Secure storage ────────────────────────────────────────────────────── + flutter_secure_storage: ^10.3.1 + shared_preferences: ^2.3.2 + + # ── Navigation ────────────────────────────────────────────────────────── + page_transition: ^2.0.5 + + # ── Connectivity / device ─────────────────────────────────────────────── + connectivity_plus: ^7.1.1 + device_info_plus: ^12.4.0 + permission_handler: ^12.0.3 + path_provider: ^2.0.11 + uuid: ^4.5.1 + + # ── Proof capture (photo / timer / location) ──────────────────────────── + camera: ^0.12.0+1 + file_picker: ^11.0.2 + image: ^4.2.0 + geolocator: ^14.0.2 + + # ── Report card / charts ──────────────────────────────────────────────── + fl_chart: ^1.2.0 + + # ── Notifications ─────────────────────────────────────────────────────── + flutter_local_notifications: ^21.0.0 + local_notifier: ^0.1.6 + + # ── UI ────────────────────────────────────────────────────────────────── + flutter_spinkit: ^5.1.0 + modal_bottom_sheet: ^3.0.0-pre + smooth_page_indicator: ^2.0.1 + group_button: ^5.3.4 + pin_code_fields: ^9.3.0 + calendar_date_picker2: ^3.0.0 + photo_view: ^0.15.0 + flutter_animate: ^4.5.0 + vibration: ^3.1.2 + url_launcher: ^6.1.11 + intl: ^0.20.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + flutter_lints: ^6.0.0 + + flutter_launcher_icons: ^0.14.4 + + flutter_native_splash: ^2.4.6 + +flutter: + + uses-material-design: true + + assets: + - assets/ + - assets/icons/ + - assets/images/ + - .env + + # General Sans (Fontshare, FFL) — see fonts/LICENSE-GeneralSans.txt + fonts: + + - family: GroundedBold + fonts: + - asset: fonts/bold.ttf + + - family: GroundedLight + fonts: + - asset: fonts/light.ttf + + - family: GroundedRegular + fonts: + - asset: fonts/regular.ttf + + - family: GroundedMedium + fonts: + - asset: fonts/medium.ttf + +# ── Launcher icon ───────────────────────────────────────────────────────────── +flutter_launcher_icons: + image_path: "assets/icons/icon.png" + android: "ic_launcher" + adaptive_icon_background: "#141414" + adaptive_icon_foreground: "assets/icons/icon_foreground.png" + min_sdk_android: 21 + ios: true + remove_alpha_ios: true + web: + generate: true + background_color: "#141414" + theme_color: "#141414" + windows: + generate: true + icon_size: 256 + +# ── Native splash ───────────────────────────────────────────────────────────── +# The native splash matches the Flutter splash exactly, so the handover from +# the OS screen to the app is invisible. +flutter_native_splash: + color: "#141414" + image: "assets/icons/splash.png" + android_12: + color: "#141414" + image: "assets/icons/icon_foreground.png" + icon_background_color: "#141414" + android: true + ios: true + web: true diff --git a/frontend/web/favicon.png b/frontend/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..39918270d4412e46b14c921f38924156cc133d71 GIT binary patch literal 390 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 + + + + + + + + + + + + + + + + + grounded + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/web/manifest.json b/frontend/web/manifest.json new file mode 100644 index 0000000..56eb30b --- /dev/null +++ b/frontend/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "grounded", + "short_name": "grounded", + "start_url": ".", + "display": "standalone", + "background_color": "#141414", + "theme_color": "#141414", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} \ No newline at end of file diff --git a/frontend/web/splash/img/dark-1x.png b/frontend/web/splash/img/dark-1x.png new file mode 100644 index 0000000000000000000000000000000000000000..92ef668aaf65fe585d80082cf2edf4bf20abec72 GIT binary patch literal 7513 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

ojY5t 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 KER#2T_x}I@wh~kT literal 0 HcmV?d00001 diff --git a/frontend/web/splash/img/dark-4x.png b/frontend/web/splash/img/dark-4x.png new file mode 100644 index 0000000000000000000000000000000000000000..82f7f01952010b44079517d86b75d2be91110caf GIT binary patch literal 39722 zcmeFZc|6qZ7eD;5MV6=(*;QgBBwO~BZHSs=XULYFvNOhAwyZ6KBou~Z%N}BsP}UG* zi765qOxD2|^IX&YeO}Ms&tJdaANTw*Gu`HMu5+F1tnYKK`@uC+11@#}b_jyFj0~?J zAcz@!Wro-efDg3r+4~UmY}M$>CG*hdOH&6ir!7(Ql!#L&$t|x-ZI3u!U}HY6D|X^E z6L*<%Ml5yuzLUSo8e5ig^6?tBADeAVr4b*u|3KXQG36b52{HWp<)Y5Npa1>juN?l93tbHVDZ_ut z0R86~=t}T^vBxylheEUDr>FMB1nfPvD6Ml>4IHR1L%1uP$g`I@K2b3_>HKW81o2z0 zgqV*zg%$U#Yw{&(Z;_7d-p#nl>vVf;*XzLj)`a{&)9>3Zs^uIoEivEsEfsd~7duwv1nYPZ2wFSEe+y9`Soaj1Aqb zIQJS;HRv7ehO{ZcxU-=651eFUbh3^e$Fp8~Q8ZklR=-WyA`=~Tjge8zgFYrDJrSF8pO&(7DRNbxBKtjgr&%JRIMt(T+NWQdXhRf^Q{anz=Re( z1S9VBoNIXM<>l41b!fik*Gbs+)f<|pQfKGSO5{qqZX_d0su407ZpdPe0GMRW7Bvbd zYV7gxvuAWzEDklbU-mXryKI-RySEe~%-bul<&M4(D~`8oRW9yF*>~hQUiit>%jX4BKKiRBZ&v=C>}_U=apM0> zro7yw2B@Doz0F6sCU-1h)+*#^!eU?(ikq?EnpBy&bb8(ePU8GM$CGjGk5-OsS3B|N z$tQn6%ca8&9=~v&UUl>(2GiGO_{+gqy%Kf3kK(3B@?X$0fbAS1gwV*U7Ve4y=Psl& zf#qXKlq#NGyhW8<&}I%V)nnrLk*AZQ_$j7KJiV70gxSh0r04kf^?$JB&hHbPRJCR( zR2&9@GKcRpgwrmqe(z%*zX0_6Xfjwc`lyS;w&VqB&G>#4j>g-gu%D)yiad#3eGbP; zpLz0#KdtLSK=_aRV|!g^4<1;q1%)9FVXyfU~ltx(yqdH z`gZUOrfl~BX;UHkSqG0rT0}T(i(b4_xN%f_^=Z;QvF#x7nI!3SeHf?YEqVS|tD!;? zEVz0_?aZ`;%wKOS4&egD97l^cwsX>tH%YD}$1kE1B9!uD< zmRIJlmrta{=MzHa%a=P9`Zlz6i~5;1;_g)tJHtz^U=d;P5_3%+z54|ILsadw{MCp; zOvqm$1e;6~q9N!{f^bbI+G1N!|A$KYkxX;xxqK3Lj8Dj>FHK>*Ty(NG^WXxZb2gwX z^B`|hhvtaqjshXg;Rl7v-4XCdH1#0k*eP&U1_^+{=y)TTxL@<~n=BzOzM$r*n(n#s z4>+6hV-%EuYHGQ*yFi?iNLNFzX3nDpF&#gV1=U~|{^~LBdUPL|Y8=QTFQU!EETk1V z*7W^e%OlJ^F*?bA{t~{_K=G@~CvlNl$kS8H!PKCDvZxS;?a5%dp>=BQj<$l4W%RKw z-SJ>K<*Uxenc03I6$HZkLY}{mFw@-p8npTX4^CW&d-^#9`3u!IPze6*T(QQojoS$E zWVEF$UUZm5UXk0&S|yx7^gm*uTZv6Ab+3JJY%Sp}^FKk-HeFf>cg(A&~p zCiU(-imD*yB4wVT&1LcZWa_@R0osOY_MG(|gcT|lJjNc|_0_Xan=*oI`MH)P*Sful z=pIO7#py%P_js^3@NVcvb@!r@-mUkKmK3ivTCfFRJ#1pbewdZgKA%Slys0zLQF%S` zd!CPdrl$SJ_uUAR=I(JAwiGi|sdq&Mkl;>HIR`-+F}A*qu37~`YNq7;TU8smaBO%S zaRRuPjyR`37Zh36#T@1TY5O%o{vG%p{Y zzHD5a2T~G(l41a*=qp2Om6BPP@lzT&tFpa-MmtS>KvPZ5FAp+OV^`@!ihd2gc*+5l zgIQ~=Xx{N`QHj=)(7~0-90UP@!_NhRLjRn()4l$!^y+YAyzhF$a^J&XT1QdH!LK+X z{^r^m}u415&s2)^3m>*ipf$Vm>k~e+S){y2NGDeBoqB%zF)D^ zKicEZ4cw)@b?|fer8=dT;XV%%|%!pnH=Uk}zvRJ0o6V#65)zvca<9^x2DagbKWgPR12Bo*h zq%SSJ9hYrWK00u1&$*dZH#$9UksP5?Z?mRg3S7C&g=uC6D8m%7myPI_A77^344+cz z&`!m6#mtU5HMi_aU-0d*l(-r3n?Y9tU|fc&2s)zl!BXcNlBn=!X+y^mGEGMzPp zl~qWTRLmNhu6h}C6>x+}VISAiD7hBp%6=j#aZTHNyfjq@BeoYCV>OvIUNyo z?u7022|O#*R-gx1`}EFbSbI*@w5$4OyiAXs@FD6adCm*VUYF|)VJVA&0_i<-Be$y7 z#Jd8bEZBbB(GY_iPtZ*&qTa@+?gVO+lp|7{M zA{_f6K5#OaKd#D&-+MP+_$41T-bpH{($E5C=+pC^N9g*N)q6JO ztDg8}Js{lc8)HMxWrMd{C+b!9OEGH~gt*wCPba5k7$61LsGoVh)CL)1oNB81(Bqsf ztkZ+V^F>|HMD7X-3eK~MM`nPNtBwPkc&HS9wElMKCJxo)<98*_sd`n^R0@W5VCM-^ z^C|}HAmA;$I%(=n^bKDEZ=aIGCWCN<2!}?yt2Q*)e!!iDDrF@57ZTYw0}+*h1)Gx* z2fuUq@NN_7*Y>MP6LqE;WQPKAkG8Pl1{+T&1tQ>S@7Fgk%dmi7vCw_MS+{apYR#%( z`Dn#mpNJcNdtFP~rqU?sXcBk6sXHFH@-V&TmMyKnT;Bu4-~4$zP8$n&p6Ze46@OW7 z)T?^9wW4fr`6&0Ms=AbxhAo95w94anMUc?EHix(noq1~=@b>B6PA3{)Z&NBfnJY{& zfd242K6QA;XS3BL@rJb&uSoS5?t3~C$YPw1L|F8`+I|1;$RViW2e{m$*~9 zoDavnEGI_TPf8-yC?w#FVVONOx&+0f8Z~7$#`9Sw8~6ozanpMcig1wmF%}A!$>>Qx zYZo%xqMLBa$ni)q$_Ik>=fbt(8n!@&}pLT50#b{xB>UtPO7&%r3-K8h((5pyS8s zhVLYNfkIOlxO-QVlGnRNJmFU0P&tqoHE{P7L3F-N= zx3-g$S`=hAaS1nZgpWPLx>Chts$V~)A&uQ>pB&u0v+3M=<;;%or`mK#)UXF4l_Fys&w`^ z@rbp{Ckm!mJdgzd*m1hoIgU_xBCt44(-qIFp{;+wS;;(W>LZZjryb?D-*|Lq|K1!W z_)zYyXGEr#;qfec?c)zu#A)7ZsQF9xWY>beyg;|yRSnG3SCub>hC3#J6zI*LX{p|^=~;>fARz+f^3b-1f) zKhj}~?JbVOQQ-v6h;5Z!8O{vJNYf?z!6>ynNrTj?2|woWI}AnDkxiuSl;LFeiiaK5 z1i1VXPVS8A7gXg&PJdnA`^-s8<)doDk0|!K)%!Kh9ls$b_z>}1XXeFx7+OJqi(n_k zTPby%qazBJm+cEtH8%N`u4-D4DE6?qK^M-_+Da}cdYSG)lWr^b2r-Wy%P}p;=-rp2 zCf^b2s?zGL9K4-%|NZwHK#eC$OG`r%%0Cd`Zqs}-Wjj;~d7#31`hghsD?`W4DPMA@ zR3c$~vCQFT*vbVIao3A$5H-tx5=D~9lTAfnRCCeZ5f4_GaKZ~B!Xggx#Zi5 zmaoLm4W?U1!?(tS4#lHqDFv1*(=C)wkt#cSS*x^wUlbPa<`pu?64Ai+-rD^TK>cYNueaJ=YxEm9M{l-jj& zR62dDLG=g^^oE|sK}lEmVnHF?@@~r4D%Ck2(T$~8W0Pob>@6t?@2;v!JNqeAPkr6K z@eP^Z^N(W$nl@>1z9CZ;=CARV(&_!T6>YkCbVLbOr6V$30S^23)3|%P0w?e+Q1oB! zpBk2^C)StCW|^Eh3Cqpsez(o1JJCvVsI3p4Shn`vp#{`0bG^u$Jtrtgjx2WYLwP>9 z3|&sM$9|R2=I=p^#jp*v0z$v&ta6BFNu=6^tbg;YTZ>r@<&~SX326BRyuf1hmJ z+&%~3Uhc>f35MOq>DH=%0X;QlspH1wVozm9h-G<6BF*8PWJ>Y#Nzcy%eOOs zHS^y3__UL&Lx__65OeO#5_$9R^KYF#ro%@jzS-d-2Xgm#X?J%U`!7cq7?2tx@fB0? z`BI#-q4np#4+(LtXT*%-|d7IDXBqNfj| zTQ5XaEJnQWnR-$HB1a-p_IT2W7~9v!ORQp>R-?`~Y^%YkjKDF13=-0Ly>W4M_uY0% z3Mtn@%4(i7{&JNJk+$guwlnuCRFRkm#KZbYwrsq|s&{N*Z=;`%$y=e^gi}6nUbvDZxJij%>KX(0c z)?D^tf`0M=of7l;yoz3(c9}t+a5GHz5V)g|6^GET%+k(SQ#x1e;&El;*=LvCv?33F zfV{A}M8OhAro%KB~1}@E_&9P~L**i~A zfQH9O@K)$if2L{JL!+aby%_*nnZC*S!71@)QsH@h#>(jGl$cdVQA zX`Y;V>+9-@SF$GXT(jK0#PhMv#>cYiwx%B2Dt?Ixf((z+<#e@Xd}=i`+_c%APj@>Y zLmm26L!-9VG*%-b{S+u>RUv!%?J-OZ)*u>`9A|jjL|aMeE+Yk z6un|C;M9%BfqDjYHtp5R(_Mv2fz}k-pVRhG5vJU?vSFvnt!7iYYcbXujmz(<7n%JW zZ+!j>12m3V6nzCn_YMrEtq zLsVos;`mVO3xm&JoNeJdftsVSJTj~dP~qQHK5@tGHe9%bt!nMb@S+U#$vdJJA^jRp zbi)y7L*3mger#2B@0}*_=}Mm;Kp>nG1zVfN@Qaae6BH2rX#jvC61HB1*I1 zA3p@A|LmICSS*fPBM-djdMiToK(az&gnzen>=V3Wz9T?PLF=q1^Lth0t2^UAXHED@ zIc^1WZ)%Q^l}o3@50=oM`rH+B8LUOmCK1?7s(T}oPM)>9bp}AU?P2jqW09h^h>ge5 zx3}vC4MQ_r>o)WsDHPn-Lk7n+yamDdAlP@CQE}R(Vcb*@-u@W&qw_tnVPWXCovhKo zI&OGTC_r__$K6}_a7ou^pHv_1W%Uuy`bB<@b4f5RD)`OAb;INkK_Lwcba|bUIh8-#?dc!hWm)W#SjTs6> zW)a<8%GvoRu|g8!`~b(H=c{5$aJ?!X=})&U$<)HHN+r9~d+iuG_pH-o1j>&n;+`oo z3op@0NSG6cqQCx?fb%mwhMO@8L9C^%Y({^Srjw*?KV|drtOn%a}IXy|*^y z^=FO-6!FYdifq%sCMCK5vWI~8$a7}RS>HubXIDeZ%mWp}q-q9S8TwYRtqr>(f^pXo z(#;K)02ui-+oP008P~dZt{GLwrAXeJbSZl7oOg@iP`taxur$8HRndrKgDqFC(7M+X@08 zVUnmW6s>r==0!KxJuwX8nFsRAsbgJLQoQUIJSJy7kUiB3oCW!$)liMp8Ug;rH->3J zn!ldJFMwsA2h!J8>`ly5T@gKzmJ;>SvD{@lG6XA$Es%=3$1{U2LY?n1Buu)@Xg|@b zLBNl(vs@H0+l6nY^@^JAR723kD`0nqDNa^RuX}C{;&K!?sk7!<;ybFu0ry8hOSGJT zmNehaK&UWUW!ZlKwK~0Prjxjlj1I;JN){p>U6YQwvldX4^p$=9f0cylua~xI65I$d zA-XMpFMNXdU2)rfo9xpZQE2{J%UF)#)9rsG?>kBtD)wvL`DtjxcK#~mUWQ>Asf2#$ z?tziFUzSl?{u6`oZ`RVlk-DV?3UvMf5sL zdm*|>H@<6zuSYCU2)0Ql*8>2li;!XG+sdxUBT)>H{}4TfB8rxlMuW(rEc{9WeXr+Z z2af1QYPZM!KAAPuzF50fre9#Q`7R1GH+-@_H&uiP&ORPI_3*eRhrn6a$Ugb9OqaJz zIyZ_u4q|_<$MT3uKx2n%B3{v1w-yx*KYCen^huh#v-X@9BlMGpuH-l4UV3BQ>aJxZ zxjCx|6PKqPUVRnDNz(J!7ZN^KQVi!h>`s@ELmR5%W#i^S>irlge8Gj*Bl4 z+vLYPYwoD_OaTy)^AsRf=xV{7RkXWxTT$R?k#!P?2TGeB$gxu|F^N5MiAn+~4$?8( zb{R}5Eb`4E*H1gx$Xfmk)kv%sfEH$DO@=YH&UUDXegE!E7U&aG2@)lIxsou@!_O&w@ zG$MC%7rE9pG{2JXsBY9u?~7)_CGM)_h#bC^IdJ$9Fo(aHQeo1|FZInx<-QNJdgcoS zudhixg+Cx*5K;OiW!gD`3;wV0liPi|FCAqrNyaMl-HWn7j>h5;-7~@(vhXb$P^_uaDjqm~$BN*}#Z=I@EIQOUjfkmZb!72)zr=A$= z)}OEM4Bi|0>HI+q==t9`_UtOfte9vJqdK>ODA{=X+2!$~5&nXAu_A_8 z{59%V21l3BFRz7VwZ`u{6rt?X;uELN@i_YWgyj^uRRf-~M)2CwI8L~A&srPwxJFnNS=7zUeJu~Ud(%khEY!SMW z+%@aBG=6t9S`4`1zaf9Zjo6A+)RM&RaP-Jumc}Y-4kQVr{E}1c{}qeqw!L`xdwe!Q z5LM|}*D1FEG6|6B)G=YA1j4-~MFjyY3Kc$}3#{Jyy`Oa_EjeFD=Dikl0S-G6_fmm# z-Aq|oxqq-r_;*(z{_+IA-f%;65a$D+-HnzZvP`3y2Xewxe&gw=v--|cJaEQz+(c)> z?Lz*gFO*xnB@uA!kVp6lozb&8Q>|J8Tm=BPT)gyVRT(zt_4w5H54Dv(?-#~tZvbp( zTQI0KRj-oiG^;J_E!DD5AX6h|#&YwZwk2&~!*s!!OHiPNlV6sD@qTid*>aP8JUte0MO&vmnEkfv*5c?7)=G}M9i(_>B2^c2EE z>ji1>NZr1qi$c*809hNJ0K#)T+?(jsoFmc&JBgb+5*)E?V*Uc_HAd~ zPxHP?3G*S^(K}6O`=e)F(Pao2$vQuU=C*M-&er+jKJn*EJ60_yx<66o8|b0C6i`ZK=nH;_6W&1~R3AtvvU!(#Z{5 zO#b&QyymL=`cgorniYd!X`ulf34Uyi=zcLdrTWD?SoZFe!wYq-q1IQzMKYzQIooLo z7*=QOA*(<@^f-M|!SQ-8dm+4I_bn;HPl?mo>hVp8JX{QIP=c*0@s(A}D^sAk7RdI3 zW2Uxp+2yG=UQ~nJ1A1yoyABHx)po@Fps`a|3FM-18G6dg1RZCj6BX~xWDrvI|884} zrh4JN-BuhBU#DrOxk#XeHEv1#1ox9^>+n^Opc@SbLJ!0A9D>qa#(_^(qED)CIQ9N*eUnlLy z&mUNJAo+0+k|8VpCKiW=c4JuD#MUol?PLxzKuF!LGtbiKm6MvO%IyYA zT?s@tdSX$6!HA<(HE`i!l^+4Ps#5`XuBz{l7e~qdC=}Hw9lmoJ_%-X@MmUVaIAh+M zZ-jWYCtwM;5NLYwS+Xmz?zRtBO)BP4^qiyhuaqaBf4Vk3G@;m-p0?+i+;SVct(fP^ z-$@9|UAKYF_09ZqBG6t(tL#H0(XfL;vVG^nwVNiR|D49#aUkV(%p-)KYVFj3TK`em38?}@G(Mdm&L(uWq9rXoDnRZ8eOiSU9K4q z|5fdCAXKJZUS@#U)p0AJ&ZeBoM0b1b$(qG#RJQosv=nTcgbaxy zPlZ^TsV~aW_#3{aL5 z@vsW#EGo`vPum08`}qZ52N<8a*OoH5+}{CzFrTJ>UxhZS9kqTxoV@2#vrgulS+}70 z)a=#d;|QcY&?V`=Asr6baW}ZWUHHDOJhLY@6%+Sr6Nr$2 z-o@gQF7LG;Llt*H%ZZMWI38X+#W05=qvjrB)_j0O$$@J2O?^LSAIY?Vl}r@G#=u04!+i$PA+r!5*otoE7RG|MIOlY;#YF9o=MCk=1k{Wmd=J z%#D{S!e1FK;|P{r)1A8SJ*_ju;nVVTOwHo17w+j&v$L`TPqZ$24Zj*WY9XW*go*ROZRWY;QPzpZ2sK4ZutfNQ`o^=y9fWx}~{)(!poJlYtle-uq1e-mn9N+a<6zJH^ zl)YU#@_QWYO}IQ`JIBFqy(Jv`52@MGytMAZrUPSBjZQpknDk!9eK%T;Z?jKJ<~SOl zkMulMrjRw2*&9p~r4TJFg6*I5%Fe%TQ{?nmxc6=S-FWhyIvbQ{xdr7E7UR2^H?TpZ zU^!^Q)W!;&`w^*cq6Ze{_;XB&HZ3NOYB_B9pi_?m&;1OTYR;urjy!V%8_29W!ieIbg8!3kX-o0AW3pXgaFXv@%E#Ows29!%jB*ao%hRPi}e5 z;_dv98p8qEGb=(XvcC`ZB*fPdMJ{`$2mh#xiG zQKoyXvL9SWA+a0v=U77XW&@E!-t`XOn+_Q>`0Fl?(kx1S8huF@yu2c&4OY29Kb_A0 zaS3CFZ)eXu`J9>9aZ8DfF@y1Y%$NJ+Rx*j7U1knCIb0$FL6_16oweSEh~CPPVQDks z8S(5Z+(ZxL(8X1qr~-$gO(t4-fpd}bt()LeOYK8IOHxz21m6&ut|~w5COFX``mxJo zpJUbUZOYSif%^L6D{lB+?X-Hj6~qAjxlQ*s3TAw6O*$7i!`kYb-RmYxh`X{x^{X$~~T6uHhz&Rq6C)-W;V%(fAU)M1@&#V!7UDlDq;!OVZq(#tR~8&h7GJ zM@?F($W>TG+|oc@w}ksINa5B_!)b9BJtRs!o+8L$|__}JU|e4Fwx+bSy= z)=O=lq3uT-skDw;*VFP}lE`v4w(863-n*=jl`>uI*lM2}ZIcl#1(m*a7W)KMkt^4E zVKA%-W%o^JAThz?*=t0}vx>X2)U1FEEBP<4?yayFm^E!M%F$s_ikkIbh`-L8 z6$6dQ!uAV1>)bA;qIQ1&&ZQXwZL+fXyHM>p=C>u2Q}P!7c3pnDi`EYdpGwgmrZ;FR zDppz_%mC`Wp+}h^E1OB(XwXpU17+vPSupv>CeE(N_%j;;ryZ}IEM=KonSRsM6wK5C zBEmSm8qJuBG>blK-{21Zn%U?p2M-RBWcegMwN|JNUPHjaEcXwBu-Q@rARvT)o z{_6(oeulP%y+=3BZtQv11#jL~OfHZ*66eIj_|s6RYf^cyhA`z@`R-C0m;D~G>habl z060$mUCiMKW5Q3X29>d3G7+4uK0)+KK$#ia&xAIOFJL@r24_*4ojY$oy;$~JiB{Cl zBDD|3&FDw;DYmd}I0p)+(yrFG$bvCWlv?yt(_%0@69XB`q|Uwt0dK_f>RVsZnyGvJ zk312MOZ1Le+h<++!^iCZX!@(QHQAP!S5}UA3KPhu+U0v|E4kI>QfFUTlxAJ$ovhcK zT&88XaMBg=4nR4ehw782tZ!YNXLCtkiREc30x35%2ROLezq`*Kc`&Q$tteo@uTS2M zqIGzA6;Jj10#EU7=IS9xEVDiqYN)KFt(#d&=cI*z86(%p?3b9KpE1$iH*CRtN~RM3 zhM#E45+7-rx*Y+d?~$IcekwoB{H0^R1x(z?$@jf)R?o4o44ldx^6DJt6U%;#H?5y6 z9lfm>N(?);#fjiK)xk6u96Xsfmd2CCA5vE8Xehp=76!o0`6RESk zjEtHE&$ohR;*(~r7V%ZXi%$y%e;V?eI*fZ)?59L62U07wGq;o&|1G#75!Ex-B)mS- ze)X%9G)P}>n1#7FPlesQWBy`VFgh=r!w_~X11@1au$QJks|}`SDN&S-VY3ix5;xR# ze^vhg6zx;L#qzcTvleK{7LYZ6F%LA)xItFvk?L$?_^RbAP0su^!mWjk|AQDDH7{Y#!1 z#pbSOeLEV9sArFT3`UW&<1bShMn|L8a(&Lx%E+wHcYa`ZV3Kv`addk;w{6oKUfsRK z`HP||eIMQjN6c~J>w=C6NL+$UAMa>_aYHav73iY6QJFQcOT;Hyqai3t6`Tna71I(y zjx$n;p4Qv{5uV9FWNrIt$kq@%R|D=rmAbb)nrS^2NS5M^j>pf1%oM9D4S5}UA|b{iz)E50UdpeHu&+>m7zvVr$k+Afs)p21z(@r45xkopjmjX zT~3K}y(2}x^r8rNN2tc}Fuwy|>HviG5OnL-sF%GQn!4wCR8*r&RQzuJ9+?T>-7t)zw?3 ztKTWSeI#KB8nX^V%`$*C&-uKLRv^VhGt}=J&RvV?L#;)6Z|e4tHgFiRe$scC3^SoT z1FvQ ztyl))$Oh=10BI z&)4rSA1&IaF**S~j0D=~e*}gN9CSZjlUtKzMj(rNJ)3zapPXB4zF{{|lXu&?1Jw2l zfoe5sRW{|tsKqhy;YC9*SE0$2I|=FVCC;u|Vc;EImZd3PLqJxiCGw?socI&pzaSaH zR#s7@^*AS#1$-wAHiw_5Z7udgRZ1cYp^VW_Vt}|z@41Zj7)~#A=7tb*Z&hs?U*c(1 zO>J*Uhu2w4pE$`@bW1HK5e$0-s@?2;cuwNfKnLx)@9jb7NVR3e-np!DQ%K@mkEgB0 zOM5V01fs;V`LIF9FSK_~{G#Bp56%dNgNbaDude}9eNL~@{O>JVR}}t9)2NKda7{-Z z)_afbxaE=NnpHFIh!5JFe{$Rg%W);Ec5lA*+Q&6`rz%M=<6)FXyS(`>9(378M%fu^ z5G5L2)rkWqINvp->QA53P2e_!`AzK>BlVhcYf5`P18+w{f|fOtmWc2Q>7+4H8+dd*6pv z$8bRkC#bEMwYxL#U&4TU136sVLI3SPPF}6ejq?{P&+;bv$)>}b?eT}fEPYyboYPb7 zeRT#f6Yc}OYL8a6=&twCoRrJ%`xP5dCd+?_6;FsD4K^6U>`@x0C9gnj{UDtLJ`X*+ zOC_Oc$?55%5z^_u^;)%g3I^rBUZ5i2`^zj3y%7Cd@1MuMphh7>E+4Zg?DO9J&9JgH9b!wXkIWfJlA1U5`9c|=w5@v>BGi{v2C$eFg;XFmYsFpo*TTC_|k_P zjK5nFHZE;2jvQZ_9eB3D{dTQg|0{C~73RYbi+FiWY|rCa*4_ST%F;S*sRf@FRDIioBZ3{`^H@vN9`K3;7qQ;;2N7T9zPZyc3iaoRD0aLOc%fBh!57jnIXD;3 zE!vt3%Ck{=x?~sgAcraYkZaR5^|hA5%=hhkmMI}o#u0gLZ?HbY}j62hK!tbpttnVWB-3T zr}l}P=`HEqzx%b5(2$jSQ#`2RFHbr&b*{&S7{e-uohuR2;HE|Nx&eI!9T>KGm$hzU z&=Lcm)9fp{;9|lOT~~aT!6JfDX3xB(bWjO1G1U%wGj0S+iNf(sY<$F)S@)qzQQ)*AIzGcD)|3I%+t&5{;5|RI0xi@PHgPs z@(>yplUxA3sru>9FD3qyf2fv-@Id`sM=c>$*4ZhVTg{gFaPPYz@{}R`^NKd) zEsx@TG3-GsfV97QH{q_8jERokgml?ngPykxaPi;2@t5NF%lBqxZk?ZP3JRt%IKm4t zC)whp+2Z^_*V$^mb~x$`^pw@a0jS-~(9nzV$iO6?QM6}6b|Sz;M#Z+y+z!>7lB6%NRGNn`+W%H}zXpse;NBy_lqsRW{-q;>)Wm^n9xwYSRz{ zW?v&XbzV5`$JuP4v&6jh8dR zmdyvN`LyiLPkTU56*N`J;nY`l72bXgwgX3XBTtItT|59m#h1V{?#j>aha7V+WHDu zy7;{BnK@O06q|UIqdY%bw=sib@WLfE&h}U4(#M(Sux{(JPtbzyoJVNkDD zrzfP!K|g8^n1r(g)T;{1!ZWsw%U@$`TkL2y`hC|)^{cjg(Vhkr-E~8)_$E9`cK@OQ z1N2>yu6i;uJ%O0SA}T)=J#6JOlK*YhtawiqlCPovaYO$Y)b2zU)aCaXR$O=Dmwv{` z*kAW8YS_Di?l}6!aV7(jzme~)?7kIFrC`5Jr(T})pWtSJYUuNd{oY@xX7Ek)E>*S} zScNSHQ*_9KCcb}B(+2v^N#77>A?6VR&b~S#rC|`=Z2Vq&53=+Ae>%alaD0O-ucL(^kA+F zUxFtF03UxX#5Zt-pxA-jptLl=KLa$#ExNia8B)PhBnYnn+otIx(%@`m-)5@(IC75yVCZ!&MNSDo5883E3q386wHqQm^Z3N!>QZHw? zE?h6e&Mr|p_zE2v5I|9wqA`5`Mz@s42c=N!&UO4Ta0#57v zrI@o_UMgfC(Gj|<-NMd#jL@l&+Wx>-H}ZulJi?e(gN>J2?)hL=b^`OM!+uv6~a zn4H;{QQT*WzDqa6c>_yE!vWjY5wyY{}Pm zxuJP_0oi{elDF+KV*elFXoy8=l3d7h8%O>F&eBjB6KVx_P0FS`gT~dq(E4Zy4Z@;{ z1;h6b$-XRqG)O_q!Ry^1RXNp4C=`XDvh%1F?Hto2c~12fhX``UxlARC>}t3`6=Pc% zT`f7}TaUf4DOXGBbgoqB`&xXr@j1AF3=#r*G#J(4)$6X70hi#ec6ofOb?uSh{&>vl zeQh2W^y@GXq1}rIwhXOrtJfAbJ8sSC$YwS)PsyeO5BO-N1C1HrGrAR)3l*Gnn@ZE*?Huh)J%3{24Z~(8G#YhdoLwWfz=LL(Mj8bW+z(UP$(F z?!#PO#>jW((mrHV9-z>FgICWIP7O_-ciUcWqPwgrbi}S+M;+^?Tatj_ghoY26)U29 z(0VkKwTCjDMzE;F)9e!Z<+6)5*TBWA6*47%ayeDYG@hjtm(mSE*{O*GWTOAqPc8~^ zD=P%wh7a!@x5B)_&zJ<)8)Vz#Bo90|B9pi8ZN2hwjCYX-R|_M2_SZ zrM>k23-ZSM7qnzhwShv0eYh6x#D9%Vp1gs#|II~u?!-S{=od+3xy7ruyK92pYs}iZ z@Z;92Z7=B$V*FaXdij}sZlt3hLQm)Pqd*fUGU!eSQZy}bV8>wfycE=SKn$27m-6+) zV&F=q-_k^#9=!1aMuFxd9uq}b*mmB$b)D@|kiDhR_-Tfx&T&rJ*MG(!GNQ`OU++Jg zS7L;@o2oCS#D_f z(WeKjQ186}9{K&9?F5~@qM50x3Z8uRDaj5SPeo5*8>q|zl9Gt4r=E777`Ru5?WzXX zo_g$?gHv#Wr*w-TgdZ)KAHBd@vgi4@;n2i zk&Ea~M)MoNMlhnR|4)1G{ZIA({(&E(D8egyWTfmFA?p;OWR$%}Ldf3poXRK}m7Ntr zwkVq;LiQfvI5^oW<2an-ob!Ene}DUa|AkLKxOH=!=Xi|kab5T8e%&vK(|KieR!3Vn zen8w3h#?f(9K~FxqewR+Z-4Q&XVq(Y4V>(IaYguN5UEfQmGBlN-#YR@*!)WrOu~il z1mKJDgk9`IVd#DEVXcF*z_hCtsVc1PXwXUmo5+Z+uUx!^md^I(gn-X)Wasd5EBgJO z|K^~@w#O155T|%;&!X^e|Lo4{yRnUkmD< z%rmvO6);EtV~!XxZiN4mOn-@w+*@o><*2p(S|l0dD#-2JU5*>Z=qJBP5O~vSbLjHq zS&L9?e%eD9tIYlt4Frn0!o=cTa)PEnDC^q@ZPAg{hLh z>j>2MU^5YaTyH}i*ftBauj~P_0cT~8aY@MCkH95*5cyUE@upv0>t;T9e}m4)(^2+E zhxS4NtA}?)siQ+it3q0Hh-|H$&nSOB7GZ9+62@?|fDn)IdTZ^T!jH5qX^X^3q=)>b zpJE>np-Ahub~E2_vlJ(}SERzEQaN)Yl!>Nv;W2b$&&Guz z?cHw!_xXp*YzumvR5W|gb#%d6Jo%_%qetec#zQN&B&BJ$Zg%Fuz9WyJ!jZV3dZm$` zrcaEpB4*PI3-Cr+5syBd-FvB!dn33yfuHXNx z<~r@1n*K%f+w8nY8pBIa`?@*t6zvSjLjpfSIMQqhWW{HK_Q_Qa2>g~&Qnp1ss}1t~ z!o8Cp;ZNA4xad;0n)2vkYyS)sHd-xJwA$=dNQOK)qyZc&iOx5w?DF)=0gO6a0t^?y zJ;AFFR2;gm77s@SG+V2b$Y4rIVMNOiav)Ih+ST8?*PpX0)u2sKg7wPOjwCfifw?uE zo~xL+2~_3&HenY1CM^ZubwVDIY!T4M1R^h!TE=PE(#D40F|xwsuzt>+q}utICn5i* zZOE6TZSf!9L>}(8A#0f#|4Vf`m2j0um@PA7&JVWc> z6u6UyMMti20)p3T5Fo6tz6`PVbgcd3Cf?svHyQgWI|24wA&yvMB>^+i-q2U38l)f_ z02+M|U;Bzrr`B^<_POCKtUsc{RiaowDMCDXFxiFH|me zuySU?(z6*SgIdrB)Hgwtuvn4}$f}}uj%+{0*5LMs=FsU6OuE$Jeb@j08S_o}IXv>QRG=cnlT8ZaN_;KyX zfMo9al0octx?X-W*j$R++LR6q2-!KmO-n{euPl7fMK1Hynv3va zCqU`@P8W|47si){=0LvzSfZ(}uZIAT!QYvygZ=j2T+%!WyQ42lPbtOfc$Y^~d;Rn7%&}VG^cI0$c%>4=_MbSzRG5LailMu9D`0n?U6weKv`Buv;@vm*v}5r+WF4NCV4?*>?eNxmL1%) zFlpLWz_cZId|Ckua{+fK*J#@n$TXHM5sezsxQvOy~iH}N(x!^)oayHact}Dk+@rR=VS8PMxsFPq! zC&->1!Yazkl?|y!SI-0>SqN`5A|)l|Bex-D=2PVKe~nGE@?#s{M_)8)MM)9&PSU88 z$jaHlE_M@GC>d#%!-RyddGv!=f>}@n0;&pGi8`h67 z6fyQkBL)^Nn*UUqefUn%W{l>g9$3R6|@Ctt0ZtWmwic0}?G6XX5wh;s6f z!@k6*t=HRx$RoUhk`4wSnY;ucFe?Qpx)o*~rGNU|g>a(182&-el4rE4a6Ezyk_k8? z@Y{u~%}rsoX39tO3BHqQ2d5q5O~@z3==+ppxIJ!-Jb;A1#tF3(33w$gAf!+`?yv&> zA0rowH5xU+Dr~D0>;s962*n!~rpYqy}jsUh9 z=^?12eG^WnsF)q_e9p3xjn*W>8M})-zpk$X(7*^a!r6*#j*(JEqy}#65VbONhBmSI zz|HB$HcF6kDlm{+_C^;*TvY;0%T|cfMj}5Hxab&b;|m1K%e2U}fdW)wxQ=#j1wg$n zqQ^)UJKwgBW*RMUxmjHZ3I7L$22N!wQ?vzvrt{PvrfDH=AQ{vCiBq0JETv9Tp5sU2 zY{031<<^aS_Qmv^mMbwY`<#3OPl*o03~FLqyF&(j&(9N1wf7U_L$GsLo;d->T?v%{ zfs!LA3(IAVe($VA39%Qp0Dfqa#1S~54p0k=1|hwX5kTv){~ zSpy{&1`UxXxfBoutud)*f1Bc)foPQ6wExGc6VIbhd6^{k=#zpVfRIiBoEGl?pFD-E zNJ*MyQJDojJ<7-)aov&T8+Efw+n@1K71u(X^-aAeX3kVs00tP5}#X$Ft*m!;H4QD+i_m8`|yPq-f)pygpx!8bc=d{^bx}kq2e4yuHiP zQeIj7Y0kBU%^0?l9_`Kt6aAeho6v$kM{Ncr-|4$oNG);@GkJhIObs@$e@;&G2k5z{ z^Q{c}K*b#)`}pjmCy+aAwD|_Y&FR|A$BmVEa=u7b`9_2B2vx|;1GLO;<<@Mf<94l> z+mo2ix0Zw+BuOSCZgj&h!V+OJc1GVG0@Bpg3;C0D!?vw+hkvVBFGulg`h7IL@L8~~ z4MqDRbi;Mn*CWlKO$~+nl6)DI9^cStIhigbsZYDl+Xxv|24q<*4-wu4VF4JtZX<2` zZKoTFo?|Eii9@;iUsviNawb+6>>dO=q7FF_j^-ta>%qg{bKN+OD5w3mI>F?v#CP|F z7AAX+I@?7t98o~tGlj&2e(h=3?uuySX1vn*03>6J?wgxvKlpIB+5%@1Hi$GQV1h`V=&EAsmow@q@YsJK$AWk@rK(O5S zG3U#q0aAEZ+(VbtwGve~!J1Ixp&Ds^IvxQgh#oDyyFRLAo7bZnE?&+}!2w0%^@PijL_SxFpBh80QMh-w(Yfvr1cZ-R!u*$l7vXNaZ1w`?nXTZa*y8u zh;1*tLNFqS%)~8!b3X^QZv#+UJ!E^_!R~5V`>$_9q&wqSq*kf%Y3TVwe>iu2-=JF| zEG?P^$EZhzeOgUOk7W-^w(*}mb|ceCcbm=1xy;Oik8lc=$0oxYuxH1r4FxE#i_&Z>rrAT%}4}6 zH8|@7VIE#eNHu$0PfbdSRu#7-5tE+w?;LDjyE5(Xw?=|GW5H$47ni+&C*^ZlS44L2 zUnrODC50WW%ufoG)Q<{3X7@8=8+VtS9|~tr4l^pLK~@cwqoXQAXX?tARr_zbqp<3L z%jyE1ligtbsUzU&r-(!q9YWzQ=Yhs(w=pCzG^eHUu@LcjGctjeKf@UE^fsjw=2wG} zh_ysy_~O5%&gmXL2uK914h3y1hM7qccPKJ+a6T-gCbIHaP#Lfk96vl8fc5}cVjZBs z2*2Y0l)3$&3W#w1mYN~3#X#!ZZTTY=7Mmi$HHL!*ERd)5^h}pe-MD%lo-NzPbqAOD zO`0-AAv%C9aRF{$_0rK_shpFrrctxgOzpyhS582|L`^W{)mOpa324f?Rxco1L?0WiyBvIMMV?aB1Or`2AcNSm;>>o z2FHv!j9K?G3^R|gXRDdsqCaw5I6)dlh(Ru;oZMq?_gk;&38MNdiouPdtj%U{D6Znv z6A_5CH`nLKLN9V#uK#;U#r?oK95dPJ0#s3S#vGE8eH7X@hox>@AqlJxkydKlFNd-7Uu>)8g(bFaRv~w*PZBq}U;;_E!%&tsX1`lvx?zwa5J9(3aCuUjxle5M z%dZ_C=Wanj%I>!45v2txBfIz64#;EFCm1$^0wt?QA>ed&60Vnfenw?C3$~HYnkhiZPg-&(!$9wArqvN@YJ4UfOZUEh`XM#eSp`n3Ivxm#%1HBXcE&UqhNWILfV&P0fSedQ!=Oe=`kK>l}YLwOHsvyvC5+JJ0g zNkj7})T*9aO7mK=(gUq0&zjCD8^@fpJE_)X;e>bt?U**mVOPz7XSWtSsb$@`>9>~l ze#Ob!L!#9{{Jxp*$v@WZ!a|;tyQ;Z!6+Jh?^Jv^iJ(nI8mQFsOSZHi$hyt_bbTP>d zQhVJ(+>Nx|tUTP12RNg(NDpr&J2fkYJazbI3+wq-_c?mi;NalXA3#@9v{2K!;ryKo zMt5@!mVBz@*8vabp_dYbT_caKuAA)cQ|Nz(PnEmHBqgD>RuV*SfuztPI@iMFft&jI zk4Q98`72;oONwztOmDhz#*-ie7caU4kNF4RqAK6YZXQ|;AxGOe?8US=aTGb{?Ru}# zD%dISJzfbUYg>{J?#^3Vz}n1o28%|g3ym%#Ruo=42_(%bve8~WB~*}x?n%8d5Jsaw z%0QvE!Ot4j8x8Voswvu}3`g@i?$@-PcHF@qy*Gd&yn12Bw01D&aY=Ylax;4x*#PB5 z4~UF=9Z%Q*6Z^8E@{wKoaL zm>6(m(?O0Z?ea^ljs$Ss#y{Md;?8rU2UIMqfJ zBp=wQoX$4~RyAQvDlcd86+{8K1uSjMYxxbyrSCk?FJJH1-a=qeyYr1QBQ&~vl=hQx zGqui%-vam6V-cVKlmnCIxI3`oqtBgoHom<#!EK9^6edtfovXYKWEoz&Fp{MINjEzu zCmpbuey<$p*Od?Op|BCg+QFHN58BJwqoh}ATBZ+YZA4N^H$}M_Sv=V9NimfqpZ1=t ziM@CF6;$4UF$wR=4SeVFX-|&!4u_@CDO9EvufG9>2e;eN*_lM4Ey=9pF&#i3fD6=I z>}IH{2wy1!=*I8|;}WqJKw%@|1DXThB_BL!>E|$TRk3CahbR|rVpn}mS+B&{JjFrQ z5n+niaMGRTwapUhuc3>}r=Di1)ljO!nSe_fv{46(v$d#gf6X;?M zi;0D0f@Hf)JlVB&omf_F?ZC$Y$esv$1sxb)_RzDK&SwF-P*X+T(OmiIikQjL);X!a zFfSy|?MR&Ll`Gsl4-v%=h;(RF5}dt?Q~325EGGYVNB?*Bd70<1Q`I7r+5 z*FsiLo~6<9X`=bbJ%Oag8<~U1@r~~hq~9M3Jd+GUj3{)hzc-znZu?o>6S=wf2GzAoO4ixMK@WcOrvP89wNolC6BIs%Y%l(vZ2Vvl8Kw5K zblOc`Qe0(eE3zEobnUegJfld^U|(V>8<=k(oVua8o2x1xLXO(P939t`ny`8-w1v-M zDlK6$laO&{(|R)ln<;l$SJ=nizu3(J&dk=!c9=N7KR{WM)KFc0R2Y<&46(bcw}8Ecu*G4Oel%+Spi~Qps~NDSEa=vVqr6pzt2<&}}Cawo-!S z5p>M;l+W}@Hu5ADK6&wFqiD&+baq^*1&&w%8*vRrB=$JQafz%cAUsz8LMX zA>8^BlNE~RFw@~|7`8n1PTe!{hN66-%RoGL1;4=xTRxf4Qet7<0{A}r*OmccPi8fwSTc9mIz!KS*QGu)FhFt0 zQ~UIb7JB#SRn7Jl#)^ocY;2vyLYZ&l*-Gm|(e{PUV*TKpg1$E(^nK?Ma1kI{Q=Jpy zde?RWwO!_)e>#|{Bz@2IUPv!ucdEV9@4w|~VKGb8NH8Qz7o^2OR=e**;q>wxqQliY zg_eHBt^(C-TCg_7Z13Cc%kkQ8w^W8lwa7K&|129!y+M{^Zns^1+;?Hv+ZACV#DvA- zty|yB7?HsxQYwvD$~7HU=vi3nBT~D@y5IB@5{)f_at=AhWlN)qlJ4G(7q5vU8jJwdNjnfo&mIGfAl;7*)Za+Y< zc<=JGw=7+Nv|GIm)Wwji0foZC)*MWil|;R9uIF0Y=3y}_c~e@4ozw{9+!}q%&qI*k zutrvviITU8Hi+Bt$0d*lNoD2)x?x8Qj2M7{yqUO+wk5kpO?NpTt&RQv8`+XP`3{(p> zBwfiH3Mnv}ZL=!77VNERs|d>7d}S}RNO`XXKQc8~@4ENRtaVgsAlIdy5Ey~xjDZRj za{XF+bMs#N>^pUF^9mErAG059r3S~K=I0gs>Daxs&qk$1o0yb(F<=sMg;Q*x-i}di z8rQ@A{iGUCvn76!XN%s?{PQB9Y_0L>nP=$i@SUn|T~hMZY?~^=GH^uutsE}d+3Y`s z*hy@>O_M-aSm z?=0}4@E%LgF!LjUHaEBY-WBIfOwYpO60xwaNRKG{uln@^w$z`KIRJr57(~;y7PBFo z??{;csC-?y2K*I?qJ^>%{7DaOCpY{e7ckSLN;GV)0aCD~s3ZEISf<2jd{mdTJ6@%7 zK7i+e@9NZ0wh~4hB8+6Jwy`}WMnx5>?&<^yqm-JK#{u9K!K!*YQ#U*`=CknTtl5=XmZKrk&Z9>F7beL8a#p&V&U>;K z7wwrg#{5n%4!fPbahD{fqbC~K?XLW4e$#;sZ(V#3w2zCMt@p!Fp2QM{P82`2Stui4 z`<~)Zjj6;O&lqwprgLc^LVrtU4CiKBvDt+P3@v<$$mt@@-T{h7X@yKxeJeFCt^cNZ z%Mij#Hh(a(jtSQ zDt@1Rc~X?s5lAR3%y`t>`hViRJ0kXPd7WYLJOcZ3JT>R`W2M?i1erSM^r&rb*vkK% zjZopZi%jO=r<}EU695!Pfu~V8MUDUM0D#PwOkL!nCxcHn%za~T!=U6@-CPA#Qsa+}^vTkE_Nn_D z?`PoDxG-fiP_AF9CAMgZkxLFtn%b(wiIVM_+1L=%3`$P4>ROLB+*xatDu zjvcR5s$HngI8R8(E(^GZzbUNMcZ&+C-O1`a)EqhrrkGuK zaGF24Q%5h$v5+a*dNdZZaSU5J>L88esXLvWWZ0sPNvOI zs`ym`%u@?}8e29L;xmujVj`~Op`q}>H();%$$CfN_%S%2CWDeqy-nZ1Vx00@*6h_^ z6=c^z`Z}fe8FXStbMJXQfj%;?35bQR?C*`*@>}ofFn9&*uRXU~y2Z47B~bb7pCIYz zwJ%Jh-kS1UF(&OPuNhXPhv~0Cs!|1NHpwyl(^j4|jN1nW95yb(b=BvNdfI*Z zi{arkKUpK6qnM*B0F*5l*O|3XCv1!GWsp467drHm%hZ?FJq6WloV9=E>mm?>%EO!l zi>z=5g@m^(>mOMHF&n^#L|6!87}ef5|2ckDGEI0Q z%S0gFmGnEhZc~Ms?7eeW#E~6G30!q)-El5=y})Mo|0sW|s~xE_Qf>nN=~*D!XZ>v( zU9whg*DnuJLnf@+6Qz3CB^j}=FT#9gz!p8dcDSO`tRiv9V?T-@6JR9)a5KW&du!+D z8z>zCS-_bIYO9+p!%WB$Xd@sy&S)MkglhfuXe+x0P5fmuwxKRq0+0>| zJgeb%`Chz{VGpP={(OHCm+j9C^B$jV|Gf^&fBO2unE5X)>$qMdF`zlTB>AUk!Go4r zsflsJk{4lZh)QU2@~P+NBp@Nxrwe!xAK~XKN{d=59Bn<=t4anA(f>CWo=OTp1wvD` z3yIg0dU@Ui{zy!D#CS1lL4lf~hb1e9RyZ&k93Xsrztqu*_}xH6CgShME*&76^7Dn9 z;fs390%5qSvnm&zdVdYKfe0I*neY82bw8YB7fTqjYgtcJ|L9GM)Ip#cBNVvWuE)Mg zV|iCNW;Zf!4_U((M|MHsH^8!ZPaO%VwXe~?TRcA6Lvg+T)+2!XOg{NygY*cs^{6N4 zo2530E{t;X{3ivv-hDykTY9#pNG&pT)986Gs~P>G!6q=MA&{^ls6+m}g8BcKyTF52`YqIKtB-9(9!&E(dLEJWRS#N8NzxhpN|sAAKiV* zKt7OqGONf>t+vv%`?Wt@H8)gSAPE)=1i@T>e98r+je>g&Vb?vlf?jx+b;93XVAZ?T z$iUoFCj1K|(&7P)GkdfCJ|lwO+4}&ysE|B%g#gr;{s>*RncAZQezZtYTj}WGytgM! z_zigDbaM3x)j2Wtk~+0is6ZKc6Vm1j>gYO91_2b63_wxY%La@ExC;l*yX&VV4YutH zw2SHOchaAi|MnnWP&5ukycx!DMmI^&7Nm(VTj%z#+`2b1y$21h=P~>Ra{eag?3t|b zC{R98^iW{aP`2{&$W}!_^EKf~gJ}C_AV+n@ppVm{BA|BCmHVn%_<)o_lPIBHAmP$) z&|x71__&Yf1>o;)dsGL3HVf>iAg>pr!1hYK%U?Q-dM_EDZ>ObdE3}o#L?6`^e$j8_G`sx3q1me(W?{1;l|^4+Jp(U2dr zK4u~*9x}AG04}8_bNc=Fzn8q!n)dJrgv+PX7joehj)hh3a!8cRkCtJL54i8io1n@x zBsY01_sln0@%bg`Qo6@ntZ;?AjU_&IN$Sa8`yUO;R#Yv8Pt`i#57TrmYWX&nvQtuw z?gT__HKD!W5=0qoNg^QO*qf*BDGZ4N=WWk!pPqLE9IiKJ?^AneRRsL~2{2;){}KH4 zet(HLj_ZUF&{DL5iI>bkl=aC-BwUw)7PX0A2n463>UeVQZzmd(Smep(!=wmyTTd!hOZ295I4Ts_ljeyUgQp5yk~q$Sg9yUfx|?`0zW!$h zkUv@V2`1e32}6Z1jg1}S!I^(f5{^@YQ*j6J77D%rn7fUwn8_{({b<_u7|92!d;5T% z!Z1g|EsAnTFke%x|T9G)>jkFibnfhNz6X{>X30 z{dLJ1hxUOD2(_MomV!j|OD9xnBB*HD3N z&pxfT3SJ-=^!uH=j;j<9xj=ovY2@UL_-iic8?0m6&*vvk^im?#JyHx!l$s5RaSz*E zph0T*Zd=I#qqoxoLkY@ECOzu+_Wb5e-I2IoRoxCsz$XDY9XR@kTq;K>0&||Yc_d8i zt7DCrKLpK?zRX}UMZ14ZKk{$3CKRzo8er*uKdS*P(lXrqcp2mw!$8hlQsc?LM1xYv zmBSRO!sP7z;C~LVk^RSaw?X4I?U%xAd)s?}r*?LMTk?4FAy3+#UrSjVb_x1|ZOXSm z=xk}JIxxj!p_uio(;oJH5ggd|XQA;yqxzG>Tw}(ZAs;;FXiD^o!#!IUg7%Ygj&2BY zk2eypLY{(XOzNIK6Lx3LdGjqAfB=D3;h03CePPW%#bLL|{YF~@_$hzS&H3xQ*QC6y z?3k#(jF&hU8oK? zr}OI$(Vcngm<|V;h@k^dZXES{;=)%W76V`)kEys%=5>_`J4zuRr83idV5yp_BCETYRV&tf1Aj?44u`tIH1VOD#W~5yJ(L< z1slbX1OK*7infiL@->xcN?{eq=#7itGI{+{9ThsqClK)U5|seUDdWxKCQbyy)1JTP zgr0Dl6ob08$%v7fG1H793`MJr=l7X30~-S|7G2>-*E6p~60xO;FOTqq2hOqvu|47^ zy@#v0yGNepsYH1)l-O3&U!-HO7u(UmaJLJuUwF`RcQomDYl0!kLm~|i4%%|i z3aiGgA()Gv@_x%`m>NA9GwcmW6{8>XV#r&gL3L#Z_FD1eK^qI@&w4}Qt#^RNl{d{9 zQ3fi}^Q#gPS$T_F?0G9W7|<8~<*CO+dYIJXDqCGK?!;HG&@}i~AB_7^;CTE=H_t7) zyF=f9Pm|jEfXdH@!6FG@;Wj$$|M90fs*GZK z`%jL#gbYMe9p|BJ_f>!ka`}}Vh$@z$cv+ehOH)#3&uen&(Z9Qq&Pu0&dfe&_j~Q36 zf=ZJx(u|CbSj)u8o|j0xW&vovCqn(IxNuxkjym6sR^3CI7OiDf{|zZSxKkut3IdG> z8LT*8PRgIp^Pqsoh*tQ}26?*thH4GVcwZh5`mzFSLV3&s-wbdzBxmBJES>@`V|Q>F z;+!X#?9E>i`oF>{t07{8yQDQ*;dZ!S!$Vv6{r5fZE*#ODph{Ttab|DyCiFSvcW4}qntGj#@sn?mwF=KI|8LanHoTbK~)z4S$X)e z!V*EE&IT1{|0^K8lit9$_`9#K?@+haKhs^Zwfm4Gbf~5c-aJs`(}E zB=qR6Jri*>7VBpKTTM{Ykuti`Fs;VEqq$-n15o(l(SLqv^z;)0GvBY%RVp+pi3HA2vll-s zI%>qc&k+Bq@@EejVf0$q#DC`t7LI2KUif*Jq#|CJ&y#AvUL<_WfFqco41~9Z#Xk%> zX)r|-3^b2T5A98mhUh_q@W!eK9Xy@rj9wziRi2r2fLtb-2w} zjSCg)ufgON4$taKG>MDeRt(J@PDC*#uwc}l39=k(r#;_Fz&|+)oqPz>m);GRq8m1w zLF?oTab#mz)h+_NXbQL!?ysC+5U&N`ax5zA$Wl$Xwak4Q#1T?cKp~$=eP~W`BBfO2 za-2XyT-GhAF+{ni0Cnf&?ZNwxc})WQc zdz%ygQe6w>_84flc&vS4($$!%LhIFMM46RMi~MeU-t%KB$Xd+X^Lqb=JD+Xdn4{Yu z+T!!(6%sak!SO;i@NfTqUQjBj`W2kgo(Qv+T$DU!*BeJId!y#I7aUZ6-6W}{odqfq zUj{ULJA^HuVyj#(K(fwNjT-KTkSp%e@J0sZ?J0!^*2*eP}SBHNjO^%=`uIDQ@M4)`?UhgQ-~Mc zvW!HYyJ>|-edw3DodvTB32rm-Q=a(REG#IS1l}x!^f`GDm4V;v`)>n-|l~a zv?eiovw*tm7s@6kA~fE;u)XdI%X)(YP`(IadC1i7UM=!Zx`Fy4DMa1=Tj$<4TlCL# z8b~pN#*@cFOy#`P6e#S1xrC`Fb22dfpB!$1qRaI!{ABfVnHWUMtk2Eywz4N^9OBbGSXuWo5*zsS0`H8&{1&yZF!F@h%%(Mf3SQd+4p18TtC{%ZhpQ#{_9*BZ!GT=YFOMd_c&-%z1&%{?!qE-W zG(t~38KCFf35z$_R9W#^NolnSG`e!v6LLn{L59XT2HMZ;qxQ0Vot*+zwuf-{Ufs`p z+;bh>R;oTy?DS*xOWb z7CJ=8-aA#{W!|wM{rU6fsfUNDDqFd#0-G}Skk$6AJ{KVW_06Pu?0}0cfBWgcXGXis zA6u5{)Rvr-s?2I9J;i$2VPAw|vEpJ@Cm#bm;e*4V#$G}4wysRu@DDe_^fZ!94}ON+%BAC&NQWgW92udM-m>0f#fw^98Mzo7-z_RTU3xnn-XC zI6+x$?Je=xT?QrD!ofcmAm}7=AaTh3yJj?$g@wfisF!hQ7JP6dYrj*Fy$eFbSe4M@ zzd#)?mpi`H7No}aNYqm}if!tPDGAGkT3z)u9d%}?7!Ja1tff@X=w1a&24QgQ`6IZ3{O2^5Ic+67$UM&xe#47~D4o(01wmSg}0Lxs9y5_6FhMzB6&#(VWnK zdV1=%*+d6g|LM^}h!%AL$EaFo{TxU0nHjc-TivUz%>je3TW2eZq=4=xcfhh z*6Sffal5VbiU2!e@6W{v{jPMvaz{tUORz#fW64O!;#>`85fO^r4GRd6E3fk8swf=| z4cN%QUB!(R`0Bk-NXju{&>IR3a*H7rl{536$eH;F!M z7;6>go0_QJzT$Z2a^b?RYF>{)S0 zlg&d_smoI1qX&U3@zJM|t@yNy&!lNGDRA_1x`%nL3N4KupXB#GWfMmAoCP>(FY-BQ z9>0$bFeM=mS)ZLtJQ*Lx7+Xhp4ey@f>r)wjVB?xfIxTHa`$kWAj*7y&lm@1Dd|ZT; zn7BEIzgW;gO+1mlWC4m~keQ9A^>c%`ckguHv^MeJP_oRJ#j$vNM=!&s{f6+d^cF#xE7; zaE9%k^i7kH#!vzl%1vC~Dw7QD*B`L0KS~|yjUS%`uIYD4-=03F*rJ6Wf^BF01#9?L zn;%>qtLh{lHkhf(#30Vvo@3?@B``UPGUi{J6aFv+y|-dc+-$0}VOBGv z7F`+(aJ?&+q>m2?EMKE4UOs$nxTN9R+-Y2oN}krW4n*O7hgmhGefJj#`vbS^c*)Sl zP(hQNa2J65D4mo}hw+B`r{4CrCY<`}Cqmy3{^hnoq!e8`#kmF@gl;+0Ed-#)%~{$W z_x!v6WGHOaHxNr|A+)(dr^3Hxf@P_96NnD#*Js8=AY7>o-ywGgwjcGoZ8b&sK9{pe z@{gsdy-s7!++%2-S;}4IvSO(ey6-VwS@6bu2A|KmQ?VtW9O~Ky?y9Z(aRJ<+Fg><*H-Qd6ESiEXDt@ z0)D01kQ&mi=i}4_gB|`P-7Lt=d=YemrR!zO+dPS~$MjnMx8wZZ46gc?IqbCg>I%uD zaDlNMUwN(?ni?C1iV=p~Js>kij^p1wv6(Y*`nH2LNOSJ0gx~;wu@1>=%06g(St)Ul zh$K|S%Ri{ZSG0|w(B}V*~ni=J{q0p`hw>e9n{|> z7G#OB9#tTN82r76oBoDDu*l2n{=t>Fdz@*Z7+d_nY8jih>{Z><&Wt?4^*bGCB3TjjAGtddIEa0jq|l^FEzBgisvz znuVf~706eRCe`$FK?%D;fOZt$!B#7L!V+GJ-(om8!@v*fq0x@LCpsOBsePEjifa}CiK~DzA5t$%ow3F zKk6l!JWjQBeS9hWFdO7_n^*cJ**#xOJX`8fAD893&v}jP{r7z_c#Ug43BLqhIiL>K z0L?nSO|}LEr)E5)xpqyVVPBfv9=o^HiOi*uibxdiK@Eqj^spq-x~Gew1C2)egtmsv zvR=$W0845qE-v0>Q#D=gUXBpU?3Z+vSD97brG|W*lFb`>r%dmN37mucGYb-<;R8Fn z4vee2|Mtwc{mzJ_C|2iSxw&LxWX?<3oh@>pMW%~SnR&M4sf*W!13jwtI>ol2u(Rr1 z$9-n>Jw599Vi-i>@C-GvLqiJsOxW=``PM{Ww`&G}`Rv)ChO^Dt9E1VV_7G_$yYmP# z#tVTkUhQ|WC4+1~7!AB3bzaK_fcU$>M=$W0_k=7~jmWm~zwGs@C99jf*(1STbz0>R zq+FKC;LuF<*&Ra0EBo!O`Ba3c>L~H}agdm004;BIJb2~nHHx7{m0OCkd?7?CID)D6 z! z<48;Ee{E_*qLLnl-JmV&#={^6Q;|hON*XpEaonUcPZb=iyBK|I4tN|>d#=UG7Gxl*o4o=-2T`=6=;a`k0suR$vqqq*X5~2(QKNXYs7Esq#w^oCpHfg$ zLkp^+Z=16m_$XK)&3K4C$MmV8{gdA%FDsBPLE7*8C0=_+VA~9`Ji)wocDZx%d4a1w6!beI zYyTpv`&F^oDR$J3@KbXB@SEfhPf}=WPCcm|l*_mOiYt}}eZ(2@EPK$PgMFCAz+g+A z6_1JZEDZ!s3D#$9xiub^U%qMk9NV*j4Ot*kFaqb3@yGXunWK=_rk0hW`tBNBu6fRm zTWOIB%LvFBW}%*btyPxP+}a|Y&3K;GJ2xR!f}xb2q?d2cZH6ltq|9r`angaPM{uX5 zqHissR9GvI9hC^idFYYubIok)+HCN+mEdvL_qc)iJ_;rHfIi1xG{C~Fw7xt-7nT~D zNZkEtSqU|;k_udVUacC-7f^I=!L6rdJBM}~Gko09OT#F_H-6_fifec(9Wd8AT|SIRbjpKA_?YStn86oYmKlX5Q zzAv^IK8bDpHMe0hR}n#&co^+Tnwai|EidR-WL=t9X(mu8y#nnX+4}qYS5CxxueGio z?(<{C1DdhJZGL{f&pR?5K3H4(#pYL*jwJg(-)!=Ds&o=N^@LpN&bPPUE=fN#Ph}2{ z(@2eyGfKm-mMyIZmI4o`AP{!swxiY4v!J+1KXVW46z2`h=ExkEEC%1n&=J0qC<@!{ zE-QNytZO)3MZ;&4;~(29{f=)nqaqoS9>=DpB|oh*_M>M(5C4&DNC5biW2>Kq19&!_2%QAnTNvp#PZYx}PphcshK-dRCp6u0 zsl&|CG%`T5J;()^ki;TFey;jD@(o9dov;j%-2gO!s^Lv4q)a zT|J+d4W0!qGymq(ye{;&@PdP1Dg;KpNXE?E)7BCjK1eTaeszxcD8YIRBt>wXD07Gs z2-31RaRk@kt!dpi$WG*b2?WG1p@;PC_v3j^SXq0DXTE37=;1>kZPw=@&fB~VDo=9& z?kNGw0227*0EGf6Z}|C7$R)tG0x4+F>=yhxxAOqBr36Uex$Y5w${>)dyyrjv?@RwT n5dXI#{%>pipN;tc8Ot*mn|di_HFy61%;iH3z5A7FcCY>)IvBWT literal 0 HcmV?d00001 diff --git a/frontend/web/splash/img/light-1x.png b/frontend/web/splash/img/light-1x.png new file mode 100644 index 0000000000000000000000000000000000000000..92ef668aaf65fe585d80082cf2edf4bf20abec72 GIT binary patch literal 7513 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

ojY5t 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 KER#2T_x}I@wh~kT literal 0 HcmV?d00001 diff --git a/frontend/web/splash/img/light-4x.png b/frontend/web/splash/img/light-4x.png new file mode 100644 index 0000000000000000000000000000000000000000..82f7f01952010b44079517d86b75d2be91110caf GIT binary patch literal 39722 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 literal 0 HcmV?d00001 diff --git a/frontend/windows/.gitignore b/frontend/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/frontend/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/frontend/windows/CMakeLists.txt b/frontend/windows/CMakeLists.txt new file mode 100644 index 0000000..e54b60d --- /dev/null +++ b/frontend/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(grounded LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "grounded") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/frontend/windows/flutter/CMakeLists.txt b/frontend/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..903f489 --- /dev/null +++ b/frontend/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/frontend/windows/runner/CMakeLists.txt b/frontend/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..394917c --- /dev/null +++ b/frontend/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/frontend/windows/runner/Runner.rc b/frontend/windows/runner/Runner.rc new file mode 100644 index 0000000..feafc75 --- /dev/null +++ b/frontend/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "nya" "\0" + VALUE "FileDescription", "grounded" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "grounded" "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 nya. All rights reserved." "\0" + VALUE "OriginalFilename", "grounded.exe" "\0" + VALUE "ProductName", "grounded" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/frontend/windows/runner/flutter_window.cpp b/frontend/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..955ee30 --- /dev/null +++ b/frontend/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/frontend/windows/runner/flutter_window.h b/frontend/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/frontend/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/frontend/windows/runner/main.cpp b/frontend/windows/runner/main.cpp new file mode 100644 index 0000000..7a71383 --- /dev/null +++ b/frontend/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"grounded", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/frontend/windows/runner/resource.h b/frontend/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/frontend/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/frontend/windows/runner/resources/app_icon.ico b/frontend/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e1f2a15fd274618757beed924b5a8fdacf31a1c5 GIT binary patch literal 6730 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 D>TgIW literal 0 HcmV?d00001 diff --git a/frontend/windows/runner/runner.exe.manifest b/frontend/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..153653e --- /dev/null +++ b/frontend/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/frontend/windows/runner/utils.cpp b/frontend/windows/runner/utils.cpp new file mode 100644 index 0000000..3cb7146 --- /dev/null +++ b/frontend/windows/runner/utils.cpp @@ -0,0 +1,69 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + // First, find the length of the string with a safe upper bound (CWE-126). + // UNICODE_STRING_MAX_CHARS (32767) is the maximum length of a UNICODE_STRING. + int input_length = static_cast(wcsnlen(utf16_string, UNICODE_STRING_MAX_CHARS)); + // Now use that bounded length to determine the required buffer size. + // When an explicit length is passed, WideCharToMultiByte does not include + // the null terminator in its returned size. + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, nullptr, 0, nullptr, nullptr); + std::string utf8_string; + if (target_length == 0 || static_cast(target_length) > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/frontend/windows/runner/utils.h b/frontend/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/frontend/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/frontend/windows/runner/win32_window.cpp b/frontend/windows/runner/win32_window.cpp new file mode 100644 index 0000000..60608d0 --- /dev/null +++ b/frontend/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/frontend/windows/runner/win32_window.h b/frontend/windows/runner/win32_window.h new file mode 100644 index 0000000..e901dde --- /dev/null +++ b/frontend/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_

Y{-C|?Xx;Cn)NaD-OZ@NiMl7qww_xH|ocrf{5a-8* zohI;Yiy}?#MMM=JIJa3S+eL;|Kq5!=yp@6%T|tl}e!P<54Yg$TL+SbR=eMzhsxV?~ z!vow%?oVl%j^iEdf(|& zS8OM;7*3(Cr`G|a@!jDVj8m0?HV!fw<*8smLDRv6zn$S++8*>!2sG&wH*OOX+1&du z`hI#N>TaesPBsSCEh)v9$@DYWf`r}XLi%j7#qZymhS_vB3&!UdOm?;m@FGm46%|-L#e)MNe1fA0&Kmo@hJp4xD zDrm63m$C+o_~LsOvsM^vKEPcEz_-zAb~@y?bN%K*elF!PKwtQ=lxG+WsJ=-t8w7*` zA{vJ?Wsm@U(pVDe+eMLhLeYd8ahjbBSB zRkFD2ZTNoKsDmWyu%}U+rDF6SDHVj|n*c?r&@xQ>J{Sg{)4`A60jr)sFBt!Zo@K@b znCxys*mTy<%EOCWwn?J)L#rg@^e-7*vGNR&63g#QBvOmu7mt*(p@8WZYCdReCr@`- zs&!&~4(iLOLu+emG-tq{4!qB5dDI&B!EroZUVh3{ARTjo;e@aoJeoESJ|Ce{3QoFi zlwrX?nFpVZxiK%s&l!v+l zIroM(L_tBzk+EX}FWW2vU2;R{OAd=S#Xj0+S{>i!vcQ)A+coSV1-I(Jj-#h%007mn zXP8#n_k*>q%5zEIbTBd^;?7;Ag|0M?_Xp4tcE(8sa8+H-8?r2e93{BJ)=f{Iv&ZCu zeg}f5KmgxfUar|5-EJ(X9ZIX*Q687_vt#eeMmkNiW#t`E*vlRRNg!k#6ci-S$BeyX zesQ5=lgZo>yj$b&>_HCRuy5G`qJYmJw>K;rvAhb9RM-6JlqY<+kU4X4 zfkY^{L;`MY?F~I=@CCBb=3?x$raQ{PIRcSVQe!%Up7?=aQU%zSZ;U?)hVTO^ zHaNlJKGa0jf%#UKpbp9$fHg$8b#Ncpfn7#3tPm-k3T+yeOew$z; zBR2N%>z}?8u&-NyxB-0;4(K2JBKm#c7mwnGMU<`7?t|Gqi@ zT^+>t;Q!xS$Da^xRmR9xfE?(^5t}b~FT4_lf=wSVia0?4#|w5ja%>-Hp>8s00i1mARb6$9tvN+3LC+NmJv|$$ve%vN~GT1$5mnxxII#X#C<&(Jrks@=p zS0|ZWM_yfc>c1-s#?V~tN~e)etNn%ZpoWTwh!9in*n36tJ{DSIja)j2?*f5)?I$r2 z>mN}D=7F<7f0_BtRstsB3Dh+(_K-7`ssT9DlC&z~kt^Js3*XBf%M2mA?V8aQRc+|M z4)@kTnKQq$G?@EkD5=xtRQN4WI(W*R;XfzqvtDiuhw0q|c5#FldtraF8V@BcTQ`_% zfG*znLloRYoYZ4T87VQU@cJGnX%=*lP<+1%{6KNR7R=lv?@6F5py2$xPXsvi9Fj-M ztvhZ!r7-d6$(yM6uXG&N#`O8fGSC`CpwzX_m=5aXkAO-TtUl@R1pDo0TPYf4X__Ao z7}=`!P9$iTorZ8Uo>RxcUc0m8{^N44iF>D>fI$P=>iantbiD^Ww(=q$S?Ahujardz zPAWuWNa5s2%D;HQgm&i>d# zLzYZRCw#Q)I(&2xc4A-xeC~A2uEr2i!A7eL?(0^|=>+{fKWszk!|L^~)KF;7ww;&% z2dIgQZ&dt}E>wz4{OvmHO{bWbVwhhL!SaIPL;~d2fUI1282l1oaVv+P_7gQ3C4eW7fL$udyJ6bRHno zw#Y{A@3l%VLXREB<%ci5kFUd zhxY~DvB%QD71k*HpIfRp?pNQf0LisuqF^N_LG0haj*ir#&CNk|o@ukcZV@B5>m$YX zdDyY;6XJ|WvxuX_Xm(Pxd~gZ8{Ey&!PLLpm21%-RSA~0M20uI1!aYN7w8Up;XBQMm z{N*@?NIn6to(eaBRQvJwR)zWZ$?($COvGF85`hTF$}sldi|n@ty9uNVN_elz{vtc3 z+4y)5Werr$(uNP9?oE?ViI1g`3L#Sa!f9pP-11J5!=N(}+=Nn|Gz0HE#rgSKdMh8BjcH(M8v)R4^=;vb@h7SM zfe9RdO|oY0y{f#>OI&uTuk4!_n9~+PJioOq13EM>0IS1qsXrCY4%$O1*NUvqz)wJ} z)xA{1^Lan%_3P~g%={DT+ca!a??J9Ut&69yTaiOl$6tNDxvA<>zaxRsLY#P+$@kmU zXY-E(aaRk=A)uMyNz+|Gyl?Dx+PKFP56n9>^$_R4EFUlwOp@!hJk+z`!5;CStMl}9 zoN6S26tvCfOmm5^_ra{F$=tGiCrOA`?Ttu74o_lIyCD39@>B>uwVnI^t@3z4q*jUX zm9jpX06<1Y;ubud0N&Y0Z|d!>iCUQsmex9NE|m&r9#U!&F+%=?n#09HAi{>G?WsFc4VFRziPT5flJbVHSgZeMImVn)p3%w3<=2`;rZ;>{$0u)@DPrlpNln0E z8p(JCI1a!&8%Vl~V5h0<1Q^^zR~MuO{T_2B2o)PTCM>CE7b88i8dWt@j3^n<26^< z%^oEZ0l#7p!F-3+!at%ojjzQw~@*DHiWm&j?b=kR<3}hVOV--_Awo~@ZH%x~W z`f0}SXOJjdvINwgzvjuyRV`Ycvk9}Mb(@Rx=`L=y3ltRHG+`81k~+ja7mKSGlAok< zntyua7Eqt)|Awauf`zpko<{vgH2JaX(pR4?k@!ZMSx^Mtjs4 z5Zxx>O2KCZ^dXmKXT6yYEF90PSW`x+baB|p$#TcxN%X#DWc!Ro#pU1}RYyEvAA6Ii z^yc4r`F3rggd-}WLPz1q$I9Atm{8$X&5r9&I8=&9791Va)Jg>RI{rkZVozsmv zAlnZ-%mOiGNse6{_r?#BZgL2kqzxr(=VBqRx3x1ElJ>ZPcj6ha?HPT*U;0maK7GY9Sq*lK{%>q zAiQE7i>vcQh@ICj6i>KQ`KV_aVmC3Lo&-vV?@j__UJpN+8X>Ei3-Nw9-r9F}*%ow< zFTXC4(6{_D&cz%IJ;YGb%F40FfRZOV4bklZRAz3PU#3oO^z)j7`O#hTJ@>A9O)V`3 znqGY5T(Jl;TZB#>Bg@_eh|Ata`x5a4v$|&VAnD2Rf9zHC`IuCRm8Qv~IS6-4TVo&z zR&vDW=jFYTJs1H~0b^S>8rfn~p_J7F$hR~bZO()4v9rsRXXi5hLB(=dVK`eK$+8Q7 zLvZ5Xz$X9aTWp~LYQ6g=26n@(dx*`uS^!5CnBx`=Ktgt3Q2)_hSzm5}JtzJCl6g9I ze`Y5{;W!r6JqGX0GQ2aO(F!=2jeZ~k33k+;!kQ&4?-!$GoB@pQg!&U4(k#Hqg%uO5 zt*yZnkz{=-RfEYD0bp7|!LJm_(qO_%k+YZ8I^S&Uf3%n%cD!Se)H1ySaekHKciuhJ-waAvVF&EL zF61sjqRpGD4{f+lUAZ#5eeVnvDdNPlk|h8hfvbVg5!at@?o#gy+TTc=Rr#<9j;I4P z0lNrivFlBd;M?!{?trRVOZoiH(N@<2h;@uJ*z0)y)~xRM4VH{!Z8orx`FJgn6V-}O zhT%_LrPxscbL+pGp&Ybuc^SKVO*_BulN}wz$wU)ylXQXdW*P|E3==wSX?iM1r_*VA z@E(qvshAc93?T+7FUlC{cEEJvI0@qUO|||vwhWE-l{VMx(o21so*d++O1Od=w6)a= znx$v`_Tw_YOY`05WI>P9^#s+pDvIl(p+2D&g7UMF$T23TKNkYM2B+pVhXEa+c7r@U zAsIYJ`J_VgDO>vXnQ#_ScCe(HTU%%UV-RSs=Mc1@faBw{xlf)q8s`r+(-o8b>Gj`S z(;LsmooAB$5GOBH{not0eF`K5j}K;NzW$cu$;ZFNE0YMUk5KXGq8Q#!6{2pio#=xr zy6&EKPI_BSz2Zl?8U>=8b20jeL()0Gde&J~m#||F{6`k&+f3Of`A72?8<|DNG(mv@ zcMaZr$V-AZE<>Q?OL^WmprgMd5VVkZ3x4&tfLr?>obAcG%$^JJM-dIF_&o>*U4Jtj zPg4OF-4!oldJq%_{FgTh3+!Lip8F?Fsy^B*dtvc9$$WMvjs(GWMhkZjV%2(-2@u!Y zYW6;Wt|0b^fcy&yJbF5(_ppIt`Wfr=2+d%cX4h(ybQHdIdAJK2M{%|ik?%!%&cd3l zLuE5p7^zthjZJxl_}qIaZuXdSFD&8~%`sx>T?BA?fYrKXULfaC`NlORJ@V5(m>Ca_k*~6m2zdCE-+%U_iJaF0To{Bzr$8ycj91RMc2|}BG_26IJz%LoRe)26 zQ39vJpFv*1d3_|ATCzvN;L>jsP6QI9J%#=DBa4$FG_~Um{(B|hGU|e;bX%Sikm&MU zEs<#NgH3fF&ZEzUCyul~1_q>%w5i5JK7vuzuDrIq{6LjCT}A`5VEex$aiVP|9Nbk9AGiUP-ZaOYze$ub+l6|IW?oTS`QLk3U){`)Z}XU+ zTqo8%KftFh`k>KHBUc9_rX%hdF7s7GzMAml*jDIC86;~IBa?gfrC`_ z1ZpWda#CFapHB|XOahaQT)phK2|i3BhNU9ezzKZA&az~S<$P&$sXfP=@M zt0XSlq}Yp(rdM6oIdh3IGU4@WL6dz;_#!pW2gZTK)ar1eRsU>Jr?O`f8@13Oz2_@V~dek=9EeY$0Oi1hl zw(r-w10QHQboE!EEIDA;WruRRx3?TTuz;?Jv*C`%b0QkgJRWcSKWR`4cTY0v>gV_m z?K45E)dX4!vdo?+0n$yCch?QIv_OJ42ZCluGdD`ylFPf9x7t6>{CA zc(vO_1a)$bgxF}I!2_~AUSy4u-6m_Prek*GvlABHB`oB3Cwmpb3K6H_VvUOCK$bxF z2M?<-FK-)*`$YZ+$fn_f-U>pYySaC;+X^WllS@*CaWj!y1Bo$2KOlz z+5K%u%-`DVXfFnrO_AV-0#_a(2Z+6!9S2GKcn=8> z{JICKyD``1s4iL2!cU)c`?3z$lf#)`WbplfK3`A#{<`NI3ugVoI*aDpdi{^&?W}BrS4WMZMbv0H5C zKnd<<%=%05T%)gzm?7Fe=6{?vF_(Y2 zLLKC!FWYCHvm#o=3!(l4m{EgQ86nOdyJclOER`$~O%eE<;L+Fma3rXLkP5)%yivnluK zsA6~-jUDea52}8;fO-QNk~UBew$Bj5FC@oYG3NMdfGI{uW@hsBJQ6!|0`v|9Q2n9S zo9rM$=tEWjU2)^A@xx*$;@^lZM3B;Ef|uO!GX&ILI;HygtvG4f7Xwq%z#gS6xZRKy zW;K%4wVP1@iUYC0!-ri+B(L{A2{J;2EA5!bWj(#Scb~e?{gjz`nUQrn>YA@;Fw{}f z^F)nXe`%1oo%R99Z=N=BGEs1VQ3+bS|MXHT|LLWUv5-MOS6pmbZWZ3yM)E%NozzNh zUf$o0c8=QR@~c3a|KY_A!g0TJWdxG7Iq>Vs=*jl)NRcb-lp>{L(!tml=bRy0?k+H# zAhKLSSQ1pNXIEhn)E&&71KA)3l#$S7H2;2(woLy$HF|9bpdkOXD`?o-VYyP5YM#C@ z|GzadIg8RB4fTHtGw-FFL4ap5r04#2&3+fZmNAVoP`iw#* zQuckvjvctepr7$oAE0Fm3M}7M`TPwFGH(_E1Z03H9bBrhaoU;`Q>Ui5Np$go8$`p% zhy@ucBAP}mQaPsT8Upy{7z~Bc$zOF92x5+$q zwbu4;x2R>=Bk9z+5Pfhr#l^*etN=C_9MmAX1d;3PCq-&aDEO*DpF-SVY#P}2Yq?b+ zr=G3Eak63j>+>xL!d*Zf{f>cg3O5z$o{#-{_H(NX-Hk>eBeUN(R6(T>fM+r{`RFt% zfu6hvsCq;u%pFn3l-3j+0TObZp6XfiSx0Ps>FQV)XlAV5^2#b85Dobr3Gi=;SNtIv z7{V`}jJXd;#U5FyM8d*l?#_$-HFRV8% zdcNPo$JSpWZiQw7e7vVY3yTXG|NW2uE%m$ON+jrgCG41XqYfn97eiFw&yRdWv43GFiS!qA7Wq0ZDKg$^oi?D`tkXOPViWmv z7mypA=GEYPql8e>g4pYqETZRWoe54N5bu5M?SE1{;1NTXhrHSm;Z;pBfoNCfY)mz8 zl=Lw~Owylh%4B%}Kmk0uss;H5(-Txl3C}?yVgnfqubbLx&Tokbxv>}McuS2cUVQGLu&6Y?q{r1xZ^}$~tT#pIjz|*kiUda6)e8hWrVKr3C3L+pc)f zVI4k<=?rTHrE`}Ejg2qeiyXTkv`bHBD|q@i#23H1N{t2R-BxACsJv&4!gzW_VT^cSPSTa?6ZW?yx*nhR`)X6H31?na0|PULdx4{QUev z=l}>Ws0ZN#=x9hBn;*GO`=5%sovJYpPRQ#4`@X(b^5vfdtu2!Wh=aFCCkvCK=ljB+XToz0Xpf| z(hl%cW>u6q6FJZSx6CY)Ol#2Ve+kSeH3EL{3kck=l->j&yn=v*8b&Hsxw^ z?s}wmy*v2x^X)zIr;a$v-PRehAp=kiKUXAH3!q*D!=#h-EIaImYd6HTGEb=T#Dl-` zE46Z(?;H!L)_oww%X@ifVGnCZBPEjQv;iWvuF4OS`bO2YzBqClqIhlJY4nmifl{UD z2tF80xWL6M!?ieqRu(b}86&SQ5oSQNi^Z)qU?<>M`@3~8ltdN41Pg#2=42!$YP{X7 z<%*WF{PT@E+4R`Jw?+Q5Mu{NBo`W3eDR5T!Z?rxyGc5Y@#W0TBvJro3D+!DWT>y-T zf{5(w*;;URHBjzUgjKwh$2wxZxSfWHJ5EWEQg}M1{bVzNu|DA zB(&Ql>zkMy9f0}Jqo=ghI}Z4gf0y&ZLM0C3^h>MfM!-3j>SMVFnaLBiNR0s0*WVF< zOUlB{3_r~ad$=hvkCY}0F3QpQ`yO)&3#i?p%YuO^{g5vXFYLBX9ub9W-B&STGJI}E^*^MpreDs)|qBuKM9`%k1f3WqwQ)FVbrTa78qFPvl+ zdDPt)LPkl@RZGj@$h9rL8AoS0Yg>s_@jp)rO?#^sc}b=>^3 z#P0j&w~wrl+0X&Ifhec`UCT0jJsmLT_fxA(4cm3==LjGs3%5I@*0en#h>9JruG_m7hYor3rE z_#`{;-m}4_z^M@A^Jag;Xu`p*V_ZDfglm_3;l_QwM)M*-7Vn)Hd0c8;wo-M?T zjhMXa3F|!uKp?b7Jn>t&3>_xZzba3OEeD zzQ_2ZY6BB3Gn3`4HMV_KS)qZCxuV42SeuX-q2VGq!NNyK;t!qjm$!>a@+U~CU+a=p{RpWML zfAfO4+Fl$AOE4!(}M|sL)yJm59zbfn1yZZUHu|MH;lRtxL%O-d<@x!Tr}^88IE^~ zyaXSuI;Pf3&f@E{;~?1F!rpD^!N0~+su9QN!4J~@k_C7&fNu{jkO%RwOE-3b$h0o$ zF&N5)4Mos!7G8{{a#qLtDnuesm8(^jV=a4_iDh3m`dUD@w7D6>>k|LZ< zxWSO5p=)43#}NdToa4qp9zF!um_<26SDiJy0*)}Y4xj1pao8GspL2YyVYE<<1KwsF zjY2~^1$LHRNbuPMA~;ne7Z6Lisajo_dcdZU>khRm)L|2VukvWTgBhTMus_GND``9B zRTR)P(_j5boOx0LB7@B)%G}O0+%iCC0rEh+yk9&ND(gWe+<|Te_!OI;*^o-mqsoix z{q5SOZarmkN7XfZDky4aN;DxeqT3kA4m`(*1Lif~=q*wsY~cfJjXVFtC4X zCQ3|4VKRp}j8d41UMhlUd3E(`Z>~MPSNWa0 zCyZLiW`atW=m0Cja)#Fn01XYcK4uz#GgcuJ!f$VU&!*FF7i#z7q9U-2J6F{PIi1i# zMe1~=``QdZc4ZYtL&g)LA;C)JG}%xrI9fie#-9Me4ou7q$4Mv0dALh7&!Xwi8Gz0d z2DKUr!ZyCso81AKF(@;bwF%Apj^E7;ZFP)pLN8%*F?vUh`;<^pA;kMZ8j6x_s;7;S z(c3S-1%&HIj;RiM7WOZ0`0%LLY>s;qRCnmuKmzK@xp-c#%v+eA<$Xq&B0ofUTx2jx zq@-gPgFYVo>^=+G21Jp^$#W9SFMjX%8PVt6`~NEJjl57(2m&SCBnXt7f74I;s>XTB z-8VgNjt**07I!cxHr@eL&L{g4wb3k4M$R16N!{dWNg~gDVKMT!RP?+d(1(`h`%-bALD1XFNrz6ES&FRhziJF_|q%)~(J}5O(7B zCAVbBal4euj~@_Ipf0#oi78ooV-=7<+FLQNsxd#2GSc!IS%E(p4eEYFa>Ug8S0t<< zd3heDV3#j96a+Xb<8D&POq*$g*9v;BGX-$@yAags%nY~)gQVyy@>`1iw15&+=$@T6?47ILc$vL9C zT!4Dtw2?- zj?KpyUWQZ+2&pvPz1p*eP7Hec}q zz0;+BWcSBBjbI9$BoZj$j*KeO=Au8gX;q2y#Si<>;k@G;GnFEdN9$Rj545f@O6q_~ zz%am0HCW`qW6zaKaGrktJRtJ2x3?F-6grm8@fu{4etH|2;2`%eR}^CO-iR+5!R@!u zN=BT#oYDz7PQ%dKYkF+ zi}ibnV8uWp0iOHu<4&{4V&buu1<7Y2Uv;V~(9Y#RBf)b6Occru5DvoRtJ8EcMcJ7{ zu$yBRJ`K&^98YH|D1ak;g9;1>o&W(s2+ql9<#XNZE!%p)SORNj=JUH6zF+Ag1Alm% zK@vGA{=VP`7Cd&B2G{y@D7oV|AWEZT$YBNcSpw)XNt&C2wCi@ANn_-9dP+?FZ%5Vm zfL5jH!K+0MsWU!pB%#m#H81S<-nq5C1$wI+M!Zpn?tLp4RMl<>wYlx7s(|@KoTAiU zE(pEv;+u!JW7q>#m6TdRY)O1vD7^atCCAYk1pf9ee9Ghlmgf^~g?e6f-sl9Ekof79 zx`<42(hznz4d|)j{kNscz^e_ayDynTy$jHgoCpNK2&KDvPkP9L%iz@q|6&tF!(aopiANR+e|qfcF=6Fa6Ymz)#+Hb_qoSV&I# zzko=_u=DON`}Q8^Wt*(=bV%yRvU{t`4<0%$jU?Ondzg#@dpkSGTNHpIgdwsOEJee6 zbZpYH`@uI=h(k~6&DOx94;0Eg0B)38sUm|zX98@E9=Iqm(@;>JWR(IHhoi(6@!}?q z4i0zDO+dsr$Y*?jtBzIa6Y+q}9%g?^r1bN%v`JPyilt;##zmN`ZNZ)ZQK^;~<1yG?fn_CO?cRxBx zsY1C3pz_;;-Np&DPT=SoE4Oxl;~={?&Z=y9nKsn)4@uxq4`-z|QlrUYb#3Y;SDb2A2} zo?v=}90BZ@=}FkN#mH%|Yf&X8B*^XbVTd-<{}54zg14{tlJ{Ss4in)#p{^QdB4i66 z0r$hcOeOqQz}~7H><+^pEoNZM!X}VFP#o2R03qSdeZdz{6_2jmiRM<_8Kev&M`|ob z7Ygj1Y^JaEYcYRj!91ydqzpsXet(&f=I}Pj&5D*@{MbwjHb}Z$M+nBMf+dAvLb_X3);QYdkj2AqHiV2sQ!;8)dG zie2%l73{cp>eY#cssX0ih8(-MYA_pJUFTnZO^Cu2#EvbGRqMcOF6|||3t<7$%O9Je z|0vQo!t9n%z;O;tuy-)ssDCoG+nBAMp5yY+@&9n2eqaPU_J*{TvfD>j4}9iJ1Aze$ z65I85>j<=O)=u3SY$1kPK!f#e}VR^&nBgzPIKAodsDu=a8I&=2yK|ljcP^Tnq0)dlL zPv$Z_4j};n2N?J~x#nIwu3s2;UqYw#N${JQ+-YG%^{i&`C$UeDXE!5-4m;rxgm$bA zauiroq_Vp0D?!jM4g=Y1u&c<3%~*inM|kNVdbiW2Rkvz?UP(zQ2u*nNQ2NiJff1&> zAUWz*CQ$N3+?z2H7Od7_6p$y2H4UCipKoahGJ=V+gf7Fm`Hi78y?Gt}5(X=%Lq(ZL z-$I)j*r0(E2F)>*EMw$_MgKCbJ2aqaJof_3iBFo|_uH>@|9%wN3W2Y-rGx%|Vu@Qs zY=-7XAT)x|U9*qBms^%GTRL&4blNBDOE#H@-cz=q{|Si5Upkep!c3!{qv#Q-nWm}zNktUL*REs)Xg z_q^7v(ez{H>rz5C=xxWlaxIvf9%#NB$mIJ0;=*jchyr_f{F~UoIBw6C5s*;%)u+LB zH7h-JGmbl`wqHcd?M+PH4&YSH-K{@JW`isss_M5Z=wIwGlCbHpObI-K)9)I5GU05eY`j^k}L-8DG z!3@fnPliPo*~5erWJ1I)b(9wr0Jb7@2j*dY4lEURl2Gzs0;^uZ;z;F^u6QjHsp}tO zu>i|nHQZcJJl0xQPvG(f)J>PY6+0$oq<|we_@7V$I5w(d&q<`1(&%Ay89YSaezGux zE@GVZtNaSO+_4kKc;<_v1Z-yz1OQbi8YWRHaB^O}LEIV6fLWWjhp`SsUm(!m3!z-C z-R`{#^1?FtS8Ex+UtuS7ISVeTS3m+o%@xU=nGi4)){a-_7&cqRM$2w~bBXkCwokaE z`U|(^f~WQ@vp|Ip9}XElih9lEVxe5Gtr~arQA>X^Gp*{P?(1LYfi8HH_RLY z6AuZ4-rh$Xh8ws*?{kFqutk*7qW=-SI>yRK=<;P>nB)z3+4Wcz7$Q-C;#|uU2|j{H znh0_^4CLgA`tOP=16v`+dWmexzrVC)SSW_)Uo!eb<~A58fZLxDh0PUaK|v9HM$-(T z{^air&2vSlH>Gy^Ab%JMlM-3<^cXa#j&(1G>6k-`Lt9k3nVZr9xWAcbC;%tJAiHnD z%ffTZBw9yH>uSJ$*kv8(Ph=~^zfHbpo5W9`3aBetA1Z5HkD8Sroq-< zHuT}hCnOqjd;UYu#%K22p*17#z7-ZUslfRWwsHX4J5y!4R=k+bo`p_aofzcM+v4@U zw|9!*u!etl!@XfOGa*g%r(GL#d`PtiaoEBx@ z8Sm&smR0gSP{5EEe>@x293!SzJCy8*aSOJuIM*`5kgG!JVDP>0-d!MwLJj?EhldY~f`9IVdSwY9L2?_A{$L*K@Lyr`NuREA{;v+Y%=nzRw? z7|D10>B-599yM!$Yn4^wyX}?6{Q^N274(RY*J*4==H}7^rb%uP`b*`dL`8|8ar*`J#{-&s6kWf5(I%1#jlzNQ;PL}34DU9fCx7SF98Q2E z9;UC_Posi@8YDmVq|@$~9Y+imrPZEUpL%MJt)KKKTuL%wQ#3R)D;Zomhg%4a`?ggF zu}#LsX#A2eTwWBN?7H4*!pC9wz7~xu$JnGj{zeGkN10%RR7}91IclLVu67GygA`=K z+FEC>wha$op{5QcOu-V!zZWR|?7=k09R);VPSU(@y;*vCI<#-thK;DulA1+H7>R(s zr=}(-Bn0g}{ny(lg$G;Tw*mqJ;0gj(-BDg!og82(Ujy1lk%|YOK~9c%-L(GXJ^WM+ z4aCUv5aLZ;t)T^J5x-dK zWro5<5ha+g{8ByJk*{ANf={ugb-|<$Inbks4<7Bx?FCqT;dH1>pPvx7@4xSN-cBlwDCyBsP<#>U1TD0kOFn$Yy*VR51PUd^De zu&|f(c}lWivr;C-H>s&Iu2YTe^1I4allXi9GDfz@vTex`*1L7*Z^(EDEy%4^Z^Hi` zgZ&1y8R1u@nGtsU6voB+RihvxmO& zQOx3wpN!WSbkIpbGm<5BAc%ny_T6vnQ)`5y7jG!OA~NzWQFy{D@3sW3nyj4MO2j>t zcZlE*sj?ihNGXm09E6s58l}?&cdqnxqwMVL+M{J$&^Y2aZ59?5w_fo6Da`gAOdT8? zG>66n{JkkT=9vUvy+}!mgKWa(<)Zk8##fN|o%@S&aB1h@;5rK;B{l=OPI??Mg*8*5 z_#$$~O#4NWKkFfxFnln_#drHMH_EiX``7zrT}6B1zr|)j%g}5JyW_BR61>sbw&|6D z_4S`?YW${`!q4H1E?8%riX39I``l)e7=2~jZQeBf=+BD(?olA>FH-XUnX4RZoB1E= zT$ykxO>~@ysSi3`-+5B?&=YD)rYzuj`#Ds~DO1OINjVh+H(1IGPVf z_L7NkB2>(%a5o$o9!kd48y-(^{E*gP*k5MjS=8f#xH)zwn<3|Z|Kg$_mTnD@Rzj;S zYG)k18EuX)bzA4#*=c;iXXz-$YEBp}Ma_xIfZm_*?b|mariOwrTvdSMwz9t})rB*u zvgT%Ms7nsx_!wWta$~QH6t59>rxDzaBC{N|^+N*XvBvKA3#&cLt37KbSkFl)4CMLV zmNlTtFz&1wKN3#IxBVL3dqy81_c25k#GWh?7-qj?9dg*=1fQQ_75U)Nob)*0OxuDDHt}`?5bY9NkZlH~`qr z+2ABX_p)V=+v8R9u!K8T_jhx6c__O&|FR(*i$t#JpHcGee33qXPEhlO;|F&B40$=} z-5&>cguRjmN4f+{WW8K*Z?lwmyI93p6q3mi3*Qx&xA_yK{B{!;sJqyGjReH(uL@h1 z@@-B$xOrEqtn|`sygO2R4eL)MD`j!B^-s(J{!ufTY5i&CMEjY}!vouB^r3ZXb4Q1N zx|#2?LeGLtlpi7YXAH|_2E+#|k^81a-sjxoMP1~*9kC{ah3=KJ4&x$H$EV`_gUgRk zrVoTGy!c^% z1uh*t9G#E!szM%0PSU-~X)18}i9MIcV-|SZ-TCov6v`j>wzbHpik7(V9sj0SHgW#? zLq9y)#PFO8qeJQ)#+P^!ZOJ-%%i{Yfta39+6CV2^Yl3Tm{O6nuDc^6yCM(H|ldMvQ zf#K)MZx=tBKYK0+A$5rwmmN$$H*uH@4LXTR2;zO8Ml`+(r-<5~B!o*IQcn3ECWYun zKz#hb);+OCm1Z-}J8B3`vkNU6c;VZG&p5<7*~!xIrCp68GTB2932nGQqvMD>{JIot zd&OOZdFpZ-I8}~Frj%JF6wrGc0EhY~_l@v5*rCaV~=74h{K2O`J>N6jt$5Lw~0T}!FIcg0GA zR7;vPxcB>^E$Y6DpS5SEQPJ=bS>rwKwzrb$wfOq1+l1FKi1#Lngkx(_2j4fgKG$q= z1gC2f*sIMRIzK+DbLyD%;-ZH!1$;@}sPv9Lhpr#V;ur$r=#OHU(pBH44ub}4Q4jJG z@eAtm)T5%)-j{?0cod^uI!F(r46pWr}Zu@l(Z!8*$dUawDuF~6s zi50O<7zZrlGHE7+!&&#hYu58F*#p>6!HF+;-y0DJ=S2%<+eL{dN&hWheVrhU1z?c#i;Z% zd1vI0ieM%SD?%eBzAI_ho>y0NKKL>TTvQ9Qz0sq-%yb)sMi%qUdg*|MH)fe^@Z+2k z;ITnS;bhJEK!<}s9A`khR-y7DkhvcEHfRcw2A6+R>{-~=AMNk)TUKHcN4WaE3ut)u zGwV6b`+BWJRTj_WFix+yyf&~cda`i=emB=@PdJ$7{&zp)hweM=8Q4V;NTk#isapLc z=O##R`@$P`QP+||CNu^cLNWCz{Hkun>a=~&>Wf&v?2n`hEX4n=uUI)E?@{!3F2OMi zNrRZ}=2#EiZMq~(LVS|eZja+~yz5L;9rgWq><~UZ<#%HDMkr%WPLR~~a8PKRCQ%62 zz3Ebn39@m^^t9<&(Ki`HG)A5-=wIzg_!T>oq3=ohrWdaN+fru(IPZiNrO*?Uo9LQxyP}>e%;I^s@1Hlhl47(vxq7%ioKHWc_r+RfoQTU5*7o!B9VSgeDJZ@Z-yek%*2-{6x!v zt<9-P1}UUhUYuE(N-rh zgx67s_Z|_z;Iy+3BZF244ih)e8G$fnv)DaJA>dm~p{H;GIAr6OTypa<>mxHLNX zv4`VWaA!`~@S}%rdk<5eTi2>yzC@zH()vc+p^y;pXd&g^r-=ZwvVk%Ns$ixM=$9t< zN4+0yWbl^1=t0hMBlz+!+Hgg+hNeAoPGujLHz=wUF#6n+empqoGxN=?PEt;i_sd%oezz5$4P(R?F**7n|`}LhdDr;62f#@=5>CQX5 z0y`IE6TKeKrx@B<9~%{79+kiUX$xOaQhS3$%FU)|!qh}=JE8tIfm;#rD){utZkhe; z(EEhlvltgYB|?|9++7lcs(@vljm4*iO;ozvQKPrt|NNyNF0^T$Ob}$l)wYjNMt9cD z*1;lQ-i@fEy5Q4EtzaBs_+3##yUSqYt~p_~4CTYGTTx^tddSN_8f--q)L+rlwuoJrj0_1XR7U~ZPe7VgWiH8C}*Ns7p3#18tsFlJ1y%uE$VVHa9m@a_$t2GaLSZ+YWi z(UK@w!h=1Oti2L66FxKPYqNPI+Wu>Fw+`F3UvJO( z8Wl?P{7kOIXI*SF+$QuQ5GmS4&jhz6QM-G-FNT(O^ktUrT`66?Z|J}18ssR~S6+1e z&A#LzuN-Nx?%6VeBeU93k$jNr&c@UJp4;`}1Mkx9e`@HqCl5cWml$}d|BOT;gI=*T ze!hJ$Qe(o%aFtDd;(9<`Rd4iD7(QKr?@8n6y@XI1eK2h+a$Gmuw>0p99nL>8p$LuT z6|*bG4|OJs(uyKd2)b!_{x#4l@0&h-o;o6t@iB zUm77H|H!gag$unyFbJe4YpzetIkHeeG`iCWg-KoV(LyxNc}&mCq{))g$cDo17W|Bu qZ>19U-{1cCKK%D3_%^VyYbLY<9v}yC-zklb>oo#Jx_x0;n zVl_)jN(u@J_Vo1q`ST|sAznsC`sU4B#A<%{@Zt6AH`c6K`Tzg_yLa#1xpS8UXaD>6 zFFHDQ?b_AM%*;VSA%Fk=B}y|B6H`EdKNlAl0|NsyGZPaNlb4q#0|Ud4AK%xn--u=k zx@LKKx$f>xMn*=sNK;b-0|UeV|Nk#uymb5a9ekSa-+y3ZW6#OS!N9;UVM0F(3v*6R z9@yre&z?Oa?C0mtpF}e(~ZZ?3!=fxbgY(=M^iKHaEBK*|Yc5sZ-2g;N;|3TU-6^-TUj;uj91&_wU~c z$;tH%bzOa3U%q_3e;*W7hmIZO;o;f8|3H0xQVzk1GR@{U~iR1P(#ycDGKRm;2|yu9p3 z7-Ou{X+7~I8yf1fvvd6j3x&d@q$B{)YHM3s9w3DB^3GLO-WH4F zjvW)de{b{`92FIb5W*ODc6K3z0HC+`QCV3jlgT7W(vPssW&;3(5TDOCnM?q{<#Gc8 z0sz2nx9_Ob&fu}JajVrD6cp6d)X>-W7yuLsIYMY{ZSCdDSAK-o*Vn76G&MDMBO-*Q zr8lW0#<;p#OFayK!2ri>OYvREvwwmLF0(mly$2U7#NJS{OX(S2%mcJ})98#bGVkK8jXm7bBwq}$CN z-U|0jczBpXq3G(;|1CT0r4$Ejv340Ippt*6Hqf39D2}i6lWHNkC|y zk8oq-eVI%a9DJm(uz<b|){Tt~wYp?! zX~{NVFb^=nV!oT6Je!%WRu`qDoCf&W(uVLuL-?WY%f5W29ylHyeNkWEu(Gm3u$a!Z z?>@qZqIgHdzkW*X6NdH)1N|df?e3<*hH}Hy>u`Fzz!$fwrPV#{a=sFJ@}u-4=#vGK&XcF2mk;807*qo IM6N<$g7;6dxBvhE literal 0 HcmV?d00001 diff --git a/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..5a650458f8cb8ad8a9634953e4515d7c222e7c10 GIT binary patch literal 1418 zcmV;51$Fv~P)z*Wr?dFS|05%S;2L%OW7zb_b%)%*A8&G_j0H+_V=MV@BgnS z?K$V3>*DS04PG`}UbFxcoYIcMDeWlWOS9SRojZ2~1qC4pGCn>&Jp6;zYK0pDvb02! zlAWF9=jV$c2vK4(nd<85o0?j19ETMJrnEv)eCW_YQkQ_w=gZ|q5fKsD*|`{oK?wv+ zIxX#>y)iMbL>57i#Ke6%-DrLNc_^{qNN?J-DLeZULJ`BThK9>GZrsE$EHQE4v11>( zxGYDJ$ue78+h%8H*B=g!^zPkJUS3{Ax~{Iiy1Ir)*Vfj}&7pF65deV4S zKO7wC;NV~~-PF`#U)A1zy`UhU!{Gn`LPCPq>P;Is(yp!?B7x)h{QSIqmBnJQTCIdM z@wtBBNE-}46A1)C;^Ovpb#;?fQBiO6c)S(O)(ITx`}YUQ={$2rVK$pl6lJm4Y&NU1 z@+|oW?eFi05)O{E$z*D6ZBI`>0s!#z^lWUrY`^3Y+`iqV*BhXOgSJjrRGbYD4+{?u zBeE;D7_C-!`m_R8ENIdT3k!!2A5kcZlarI|jd2|B?d>ZpJc*(xtXMF&@%i~ba&z+@ zKK%CFxeBtdw6y%nmBtklzfRzv@@8gcR%GECzhdWU4u=yO8cOad_So-^006OA92xoM z>Si{ZO{38)ELa#sXCm$E>#J0@i9|vwjb*Y-nJjb7Dx=YuntI4!puG+0MJC}>D?IGmW6=>7W>&1Q36UOtrYr%n|Lg~E=G z&bxQ}7Z(>P_M$57INl7V16MDbtk&%%L z7cRXs%mLzv)OE9GqqZ+*XuDcBA?fM4Q(pcVEe(3oIF1( z+qS))larN`lQlUxsaB7Sj*c3QQ)6Re0DxV)-VzA>#A0zoM7T&KT5)TG#|1%gRdp{r&CB1OA5MAk)*+a(QuI-`5NT=t;Y5<{|9m2_Dk?_>NSP zo_-`DA)cIair_fjKXC7I!xgjH%<)=&8poF~8~W#lNPF%OZGPilQ``Auxbur)I>OIz z-xK8iEQg!5#+jipihXHFzZS@+twt4uC-nL4yHSc)l{BR1$EG)j1wy|9{pa$H2-pB=1OUup1HeY1Ep4b9zXC_o{|`=SN8yxq6fdRp YU%hJQ#|yAB1ONa407*qoM6N<$f>!&b6951J literal 0 HcmV?d00001 diff --git a/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..a2b12ac354916edc3031bad44b41ef392a32ba4e GIT binary patch literal 683 zcmV;c0#yBpP)0uY1!M` z`T7Pte*BnHb7Nwn1O){P3yU{y+~n@=Hf`FJ`1shCmNrVwm6DSB|NsB~{RbEr7!Djb z$iToLDJe-%7h!YHpT7_g;9px?ef;=wV`F1RM#c*lE>OYT6)RSHdwT{42L%TQL9AK4 zcqtXk{r&rQOicW&Su-3Q?2jKmk&uw|`}c23&1Ga{WME+U`}gm=ckdV&7~Z{m_vgoj#L3B%r$9u0|Ne9S{CSei{rB(R*|TTi+@PR91_p+= zZ{Hq2egcmt2%AeG7#`;S|NozZgX8DVpZGPiv$OyB@dJ{a37e~|tTbuT#IIk!MnpvY z`}Yr*p^S`-bLPwv6%{EcD7tauCSh|QK77Q=%4%X_+|kiiQ&acr*DnSJ28i3?K44{K zt*@`Owzhis@X`JI4~TN_pFe*xGqaa3UlI}$Y-(z{V8Mbtd-hG8I_=GyH*40cla!Qn zc6Lfgh*wfleE70VRQfg`S<(%S4LKlanIg=#p)bn#sB|ciCe7Z{(XBGJ$^|A!y~^@4gj5)|E~;u R#UlU!002ovPDHLkV1g$OUUUEe literal 0 HcmV?d00001 diff --git a/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..295a5b98aaf4084ea6726e2bce7d8f3190e502e7 GIT binary patch literal 1414 zcmV;11$p|3P)F7{|}O?d~WqvOul92(w{qC{0KV2!5E!070iHxQ-7r!x7%*AZ8H(!+6_dbns=M z^9>MzvLMkVz(AA%i;_hMEJVdB7|>drqrBJ_N*kcPXN#9KO=0HV_BKTA|I<0obIyH! zx%au}oaeATJUqbbBlda&(31n#o^s&YQw-yJdvD&eC(6&y4?&Q}kDqjPbv=0^XYl!q z;bIt;mzOIR$Fo>0lQAbJtGT(QsOWp0PRB?TW^fS%IeYf!h=_0tJBp$r(P!S?-U$g` z8Vm;812Kb}n0PomJdA7;1o80U2tg150o%wo9U2;P^k_zT`6=52F@Vcvv(wYl5d;AM zOifLvq@>=zF9QJh`F&7RQ|;s90{}=)PO7T9xUjIW{u-ehcT{Y@e%ot~a%DhiWF9JN~A($Z>3cx-HpseAqUjWyrjix;Y^SAShxT(mpxe-)Tcr>n1*uIUO64l>7WZf@!B?y(}o z|LLt+ch^M@TzkraYfm|F?Ws2!7eXiy2;_1(BX#1IE$_?a^vYwZxHyhSNAEvyAX+F4 z5Q*aM+_`HlU3m+S=RN+9xI^sLY~@D;CFRWPDAwC6W@6C{C}}Tg}R5 zv&+gPEEdb(|093@OdRJI6cjOuTVF3tOg!x3;sOBJy4Am;;-`#^qt;#7q9|HgDhUb- zBD>4W%hlC26z5XKotc@DNJ>jeipg$d+ z$|PZ7E0@6#!c(VCD-;TbaRC6AFEDGW0>P`FFgQ4P@??ojCZijg z4laVc7bL*AD`__eKu%M|hkv!Znk6C?>Gk@NkrCj-;`yu;r^{-cYVa{IQo1Oiip%lw zaND-o!l=+p0nHRGTmZnk8#h>VmuBV{#*|d%QpGhcEJ6YzC561*#=6Rotan z?bMCC?|8b}0JgT4)w-prIf{d6{Ax?H+ND{owN%XLjXprGyBxUolmpkEdd+bE1q{{c Urtm8MlK=n!07*qoM6N<$g3KDSb^rhX literal 0 HcmV?d00001 diff --git a/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c9d727a72f236fa3adb31bc1cf667e2e463c3d03 GIT binary patch literal 2105 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 IM6N<$g7;6dxBvhE literal 0 HcmV?d00001 diff --git a/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2a3ad338aa979405803cd078bd65ab5a4deac889 GIT binary patch literal 1866 zcmV-Q2etT#P)k0Zr6t1#}7H zr3po4I43wj1UHzoptv~QfKYU^0?H6tpit!1v$-ap@_sO|E z_nhB7wRCWBAiYVleBA-yEdtowMF5+-2w-#9YXEk3c4ji?0RVb>{*uXL5JEhA#}AlJ zr^m)dhlB(>IXP)85W@a`d3kw7US9swr%&+-11DfVKaNOrj83QP8UX+dhGSe@Olat) zef#*;)mLzc1P|bVfYmuUS(cVB?TbdUJ9R2YC`>CUIfr#Hm;<}Hxrs!_jL>QX061_U zO)BkZZoZ9aJlFz52-DI8R#sNJmK)C{QLEvK74P}`b1f_^G*x6WIV0o1+O_L3oyM>Q zc6Vp{_;_oJ6B82&2}#w}SN<7K5{Ze-mY7WjEp#Z_^6&wcsNh{JR9DEy08VVuW#V{)|sda!5*4H;+8WOg^9UUEwjZNO(UYa7AOg?!sJ1#DE;X-!{3yYSP zRu7M*y2E?_{{6o|;k%g$JloI^3+s8P0dvt zg28Ef*wFA3m-|tC{AW9Me6Cj|7MF-bSpx$D_(XzV0$^lhq^Rihw7|I05;?6mKm;&S zxwyDQMQvs>nMQml6pEoyC=?728(Tm3^z;-Liyu7r1C20I0%o(>Wo74e_eL|9Zie7x z;_*VGqhoK~YDN^@e}oQjV&WIbXlE!ZE34$>-J3RrncfsSz^<;YsI+6e3kacZwgj2b z0d8w+(@SokuGZEz(`iuy&dSQ+a8~K%*-?~Ar8;utOH`Ud2UsSPty>q!5tsWDOcocz9S$Oth{=rBaE-CCFMs z2s}16CK6>GI+Uq9q9 z{@~ysJ-bBGSWtlcIYJH$Axuo%Sz3B-@#1&2MgV}nKew)~CO0>)xLB+=jR63XlT%Kg zF47!BQ!14^ckZ5=n$m08#>OThB5do{ct=M^y{bo#IujC-OgRT)(rZbjQpLo?mXwHD zEH_=dot@pDJ-fDV-*)-(_vPgk&CR#9R4py7T&};buP>QQmPjND#fwkj0Dxu7JOTsP z2L)}U(P)OTw(AwI1!Gd?3ot@ciwui@3FRzu3j*c&z=N~?NxOsDQcXzjm z0i&?elFQ{`Vb7~ucYnj>dMGCSX*0%)*Id=U<%#Fg8JeccXP+@K{1~O2L}ZP zt~Y)vYbJtFU0Yjs_U!rb@o}rUFTPr-$0sKgBZ!+t4(#CTZo6Qbb5{+6?G44K9T27a zxf|HvXt$#8$5s-esTv`$rM>mEw73XZ&vT%@Z9mq7#=&&C1Ld;{`u0g0r3 zHlcntfw%<|x<>w35jib$7XfVUB7n_Z1hBb_05*614P&c|i6%FcVE_OC07*qoM6N<$ Ef?DKja{vGU literal 0 HcmV?d00001 diff --git a/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..83188e4073c3b48ea5bfc5c2734232bea2f634fe GIT binary patch literal 2902 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 literal 0 HcmV?d00001 diff --git a/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..8e3fae747409a7af89932460d59691e3096cf8db GIT binary patch literal 1171 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 literal 0 HcmV?d00001 diff --git a/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..bc5050b6c4cf61757bc79b465392057dd8fc9a9a GIT binary patch literal 4507 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 literal 0 HcmV?d00001 diff --git a/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/frontend/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..30279a8bb993d54c988e21cfca27d1a6704113e7 GIT binary patch literal 1760 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 LhFaAcj$!`-$>~^9 literal 0 HcmV?d00001 diff --git a/frontend/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json b/frontend/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json new file mode 100644 index 0000000..9f447e1 --- /dev/null +++ b/frontend/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "background.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/frontend/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/frontend/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0e06906ac00d1393af9a64ee309545dc07814a GIT binary patch literal 69 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1SBVv2j2ryJf1F&Ar*6y1qB6xay<-=nLV}f PKye07S3j3^P6^;?tS*MA5lB&LE$4x~W|C!ipV5Rgu(NeF_(=w{L&4N9jVe#y}=LO?)DP)Zmb z(gLGl(t97@>-`7b-@ZR>*LFSkdG7n1^NDjuX=^G|Q!-Hk06?ws{FyEQ5P@Hb0P^eL z$4j?j8vtNsS9vCfe$Zju;&XaGRGw47NT5kzYelgLO_e3)1#Qm{8`KN}&y@6?#q8vt-F2LLWqL_oVJDFF0B0Dwi| zzx#)FiZ~^Ov%kB6%WchG+p1?r`@1Wfo0}b1^J8Nkt1Eqa6I+{`P8Th8b;;@KJm+_) zwuy;;cU@duM7FiT8AUCh?f(9q*UP0s6%!jvPDk3IMc3^_87|*<1y@OYR-o&fp=;v1 z_xqDuxcuqr{xYx3<;ikFo3!U9T}fG4-QP-L@esg8o`Zwq_0M!sGaVg;Tnh3(QX1uW z9Iwi~o1}8@xi$g`Z8KLhigev%Z)ad2C9T)m){iRnjFHm^IUciB&D2B&TMrQ?H~A z^!=HxgYd%f_4DyfYU>$Cjgq_x23&4^R*G{oZFK2~A-Nv*pP2mOL@phXqS1!6HJ#8u z<3(Dz>%T@xmU4Y8^?Rb}1qm(QyYoE8lw{5Fz~jf_jS|lDEy^EJPHW;A6M6xa#QUs- z2@_4|7zc;k4++O+_ADrU{!%@;`KY&Q_qyfWRFiMjt8-OY_^==*Y@0 zgek_D`9V2;r$YdmNYi;lEswvtt?i1JDz_mD+Y!S1@S)pzrq~7?evaG}dMV`blpG-I z96TKzWuT>`_HPN&wQm>e*2^vr-vbjg5ZSu3slb`T5b%DUNg>$mS8FUBS1? zEkiVD*41g+!omHuq8{ z?&-;m@P3O)mX<| zTa`zFnGE7k_(p=YAKFXR^KyUA%^k5`X;f|Rh#*@tl3iI{#TveNaryUKxE=4&+1>;N z2c^4AGPd7-zQ$Uusp*=+xjf+Ef-B6;%ga+iZdPO{uN=)b^%vCIa93hhuCv~``(`lP zxY>CjVy>n2^2&Db1AtD!;?~y<d2YY*(`-u9r7Y>r_ z2(P|Gu}UGc7K=vBBIaM?kVaocS&2;o_KK1?&q@J|Dkm2#5T`Usz?1pT-e`V7m~Dby zRr#Oe>xIWC$9rHy>I8+MkJ1U+(|AEhnT-L;)z$SgMl5KX9gu7+F6NM1n?INt*4E2T z!Ca-k6sxMQsZFCp>rXE_n!dySv>)p47y5vvc@HJd3*DN=G`BrjC*TqnHb&Ft2}?1; zuhGGn?rAC-JmdJd-c17-ItLO0{Aq^(taq~xwiXxH90wn&79?g%T0iAm7xNjvyWk*)EUswI3XOImId9n-HJ8;(B$i#$kkRu%(oGUbx7E_{n4b#T_u8N)0E$MJ!| zy~f)589z!{=l6TRhiPbthJ$mSxWDt*8ox};h@p^1;V*>cBawzg>8auaUv5WX2Ls6d zfb79~w>hft(_+7|d+JTa4^(#{5A{Efup~LKzt>Q*bgZc#Xo5yzk_w_*qx3TtVOUFzYqYC7^r$IAIE^Yn42Q;%q%)~O(m9h4o&k$*! zk_C?Z2$-m-s1Hp|O$O9m+&nyAYU;r-_j~PaFo?q`3lw<}O+0lzc08c1cL8qXJjl^# zQqETAwi-g8l9F=xz+VZ9y3XOyIiMl^J$rTX1de7T2?84_FVrg3RyjWV1hMe-(j4~Y zA&nHJxBJ%qw|vDLTb1U7Q%qu_@PJ=>kQtQczDeDl_4vD{&jiZwkeWoc8nG zz`dcx&Io1?$!!rB1O2y71;P=2#+l68#)cOv>G6lSNwjqL6AR3yB~VtjE#l?Nm*LC3 zKLiq3&(E@=qoZx`hmnzyWa|T2we!8bsC;fMj?#(>(W=Y=Q<~|2t)85knkZ<@cE?O}Z&!+78r(p)2A%~m%%6xL#6zZ+*Q}-M8)&T!X@!{L=n?8XqhhUQaAmqI zu~g|0^}I3z1DMC#e^6Ah?z~{8ot}BNB$}csJv_ULFx#IKd!*l%KHE}mXnX1{Z(_4^ zQ=V<_FJa&U0dF|!Xpf6K?lmg+VK26l1s+EZWHk(rd{!Q!y}^8u(ZqSsFpD-soCg4@ zFnao;wOW&MJF;n7;&!lNx71;{M(SE?Q3$y zGYz}TQDx$&3F0+5;Bj7ZGM$y_T!D&;3d~zawRy7kjvORlzHVbcMJ-JTHeE|oMFpN? zwL7jH(GWO|acGE2;?=5|L4Ug@*LX0G+9jl7Lk|vI>TRvqZ28IN|qo(yWkBAiSGDFW54F!JK znS**TKHk$2R#hp&U%bVb__tvkef&zBi;9ZiTw02bidjrdOhZs zuV)*pqwPkQaw(~(vOkU5eI}LLtGd*`G`FmzOXKQWqP0$nJi~9F_XY(8!TS+Yt=i%@ zH|#YuwmCkJq-Uiyl;>4dRWXar&$r_VCT1;*@A*Q6v9z>Y2Mh8n$nz>5zeJ;~T(UyX%-p;@SzSrCvVyd& z^OhbYCSt!0hr?@p_P?!zI3|L>sQ;wv`}Co|-_kdQ2vxIhohmEAzg3{$D=sx}`#N-ujI7PTz`$#6{`fHoX^?tPY}B?I`G)nbWHxKT(bsA#C^S1Q&3kwr z^S^(y`4xKy@kNssUDbD26nkt>a|VHw^qz=VR%Qn;6vXL-7iu?V2VUQcUh>6V&P1@2 zGvG`^Yh{7&EA2#`Bax9VI*D9wi^Ttjz7VkrVhx`v7aMRQG#Da&vn5-M#y5*`#TwX5S;L#h;iWlm#14Dju z($df{J@ExmKphI*U3Z1OZ<$0y4}L5kKvY>Q(#%rchmr=xPR`kZx}j)g<+WX%$+kyW z5c1Dd?HhQuG|DNl!=lRjf#x+Lc35b~aCY*e^9jsRw^qzkB<#AL2WOPqPog}XmAp|FC^sD&K%`PLS(^- z3>_F4XwA&ZIydw1I6~oz7YovUy`f?{`Xe=7x4)W3_zq$Op5e6AKC7^h$MO-={v&o~ z1)wezl%UOTchrOne*B7W-6Wg&FL!4DJY*2n?A)A+wl?I6?*W%+hB{uTx5Vxd)33I` zKwT4)=v|ZR2Bqyoz*iPTvvd&qsd!Kv-xo` zkiYRlqx`3*r*kLqbF*KlXsKDBzO1A*N4$7(tYbXYSALGC->xVtJ8NoeoSe6oq$8DO zCM6|>n46oYZH(TII6pnkX1aU#u^F8rHOI5}Tv6m9F3W_ik=6)o!YP zulGqcd$grQAYtL@OoMao8xYA~`2QJByS5NWxWwn@C-lem=!P7-z9A2idj33pP1;&@ zb~hw6w6GYv8PMQ;hCrPgkl(!dp;A3Za&>O?hvB0-L_EUZA{Vk| zZ|3+v$D-zF>}K$q&=0)4N%>GqY%DjlrN{7lxbDN`>xRlfS@GYiVX|Vj{2= z<-LFN6z%>Ne38j&D+XhX{js0?cSM<1R`b&Wea2h2TEsXwURf9zjJG-JaYJw2y2WO( zz8Yu$hJjo-K`UAHYiivm-4c(OI}c6}{{H^zPEI>w zr)ZA=P(0>LdH>njDXgudmeba+^^D@ImICu<`6IQ(VSiuGz|Z&id(8@8P!6S!Por`{ zK>_lK`%gRhkM{^t|OGJ^2f_Uy{On&Tyj#?dto;@&$KA#BnuN=Bon`~^pw*mw8pcU*P?y~%@6 zlTGftjFLwqD;cm{sC>!T+%GR9pDJq9;x{ zeAQ#7d$jj=aq(tTs#y%r0}k*g1Co-DaIehG7Z@6x=ITp|izE9H+XO>1rwbmT_y8?( z->$?}P{LI?FUVw>!J$%KWg;MI=pu37;d-ib<&Ft+R6Zwr9SgOdJX+y{)I`JAkr2D7 z8Z4r=76u))_b)^=M_4lZssD`34hjmmEiZpI(Emg5u~kfPDXWy%uayC`7MV;TiwdB{`-R(TQQO8+MF^-u06bOouJM|SJdlTa1LE@I9>J84q z=`fU+Zk1{QlZ7MsEDOsKV@7KI-pTN%ksk*K<83eUZoV&Ys@iHa4ie=No)By?w$GGumkmM5;q z-4dJ)yb(y*{D2B?ABguTSVLFJur@fEuDvjhw%s@x85!vZCE4=5dq{i0&!Yj z*Ue{@4EokcTUYlNS1;RR7!3WT$v8Gs${Rl;nIn{{k=S%-W|)V;VBR^-)N7fy$tb6q z!llM0wvH=q^J+$pZgnygV@m_vaQn`@mp8it28$IV`_UiYmXvE=)m&9Iycw*5U*T$%x#$2r9>M$BvlFBa zou`}kTesRn^C{3{L{)zMvSMWQnaWW5NhnYS%nYXrnc^8y&oy*^{mgk70oeJrD-I10 z%Q5=-PyP@S!=Pe&qRV}K5q~q)d19a_jKhQ(etr4<`=Er&3$~k}%tgDox^nPpX4|&- z?BB!Sr~JxB9Uf&0QBaI3T_ZipDjc=Tg(y}Z&aLesJ{KIq`^K+Hwpl}fJC>~08^5e` z>%-a?te>);m_M*0H3h{kZxy_{nsZAtn{VOGLoCD2Lg4SDxn^&(*XZHdb(-W9#wnzkFujpAoHjxW}LOy<$v5uHdAlI-k$W8lHB@LYSC*MJUv8mgycRa>7@$ z5VunzJ5k42?p(8GuS>$ujHH*($&af??gVZz`plgjZ!s&AN5#aB#q{;newA_gZBgCa zjOzQqsr*$k;NsbK!$N@Czmf0CTU{aMcB>&pkW)BYQGf4%Ojj35MoZJ`cU&6q{rz`a zEv;ifB{BJd*mPlUL!QKSk+520KIE@H?*|0R8!w@LZ2^X1)-w*wk?ow)xWmZT(xSa5>nvJl!;!|HQy+G;lQ)9w)P_b5J5}CPEubq@bhPB85%}y+9=|f1md^b zRv)I`OAu)y`KiGGeT?7VXB;_z-MV=*;PbM!YM?G3ooCrg)eSKfd-0oY4rN;Gl;JT- zkg!2ygs_UPp&Vq^XeOU8F%^A%WqiT;9EU%lclKujIU9KuO^;4II5?Pg3<#-J0KP{$ zruQZU(zp5S3%;A2Oqbv*aKaZa<2m1_()_sE;fTJ+T!w54l-rN74rpa)!)~ZFdmj|?+Z#!sLDqWZ>*Zi?yQ;XO~e z4pfw}aV4{$pre#;v@$5YEttUpK&C6Yf4&4<~VGI6E1VsZPeoucF(nU`H49y9F4{{wB<9uo=!BJ zW;Hm^7!cZ1w$g@7S$=gd#CUL+-N6(M^K})X`(cK{7wEIiaC8{E@(u-&i!g2Dt=x!9 z&Su&@(+b?uEtTS@D4c^#81iC?L>}HSMH0*s9K%jd(bfCl6U>!U`sgoWAz!xGXJ8i! zzw(j1l-I%XE@{F0bc#mhfmy{jaqo_z$+JfaCj7I=XVhl$D%}EJ$zGX%yd^i)$9*K{ zOz_|2-N_0(OR8y$aYXf$erKGPG8Q=CukV8O)%$x5*;EK zuvL_RN-P}&ZT$#K)Wla@yI(kVQeuWZ!ZX{P-ATK>OG-3MIk6)x>3!e<<)3&0pNz=Y zD|$|=W$k^NBMoP&rW^*?hNG1hH6n+@CzraQ(@DR^3J_W$KqDt8pKM#l+MQIpJ^hq< zAsC+mw$32$cVI{btHXr3xlDm%LsOD%(;G(dx+eY`=TH$~m=b&_pyD+6k(rN2j*_Cp z5?}kPk)F&ixIuW-Et}hOjKq2LIEp9K(3F;Kw%bn!v7L^Ob{TN2)ZEG9Vjq^&%s(2< z3|nA7)8;S{{}u`;YhoAke<{-^^)1kmL3p4RnJQ3tQ1ymZ;-^|P*DyCPfA{@dc5UsEfH8Y%oW@7WQUANE`*`2xManII*+U3uTx zzTL6?ZwO!;TDW=Lt@_JiZeeCAeW{SZJ$^2hYaZH$!YXwX5f_f{01<`Jwyf{gZW)kS q7C@8u|1*2}ufO8^zstxgGTii2nwd7cr{I+-prWYxtU}%*D7A*}X|+-*-tNCVR4sT|!8n0YRr23wfV|K-5TA z!7nsCX9n_UfDi6>G~n|c0|f*U%D@Z0GN@C5PxZ4gYBc|Q@&8TUpo!Ap<&9JfG4S^U}bIX?o)P(MS2Pdi(_@TRUhdIEzbqGuq9eJP0aQWOIMF> zvf%+at;pH#=R5I0OEfgCD9wQ9Uj9*I0FArB5lM0hSqEP z7r~BVQLJCn5L)7&J&gB-9l9CzM%uDmH92BpqEbS6NkhH(61(w8_vQlU$$?WM(f8}q zs*{Vehj5OBu54-OFo)8GH;`K)RFE7kvy{TpT;g-7+m`4wOA~4PjX!BVlFF#^A*uD3 zCUYEJg9O4s-#L;Qd^FY2g*rr-iFV|)eYZJ3oB`LB7Ln^37kAn4JeAOrYlQ8Krws`E z>4?khrg~`-OA|(V$|g|u$mQi{$EI=T7+!HT^#wZ`2)X@a>~nz)G3b2 zcRmY1qOVXuZc&DX&+ay_m^o#7_DnT8!3R92*k%v+jBo!}bKX+%vz;nqm^HY7G^-fT z=2=obzn%VI@VUb&Zj^rZpZ^R>ci5YTQq7Ya0--o~_Rp{H_MPI8-%|M275^DUB7yMB z<_F4TkW5WTSS7ZaE9NN1!xX)i|4hnzEuZK~On7Es8)qe;H3{4hDs~D8&8@yik*3zl z|H%XsM)WeC<=yK?6%U`b1S^xJ$sX)CSzp35q1w%ooC&k(TKw+10*V&a!TY0RDX6Wi z>~+deZg3n>NRH!G|Gm>*J~`%kqKq4sNYl*?AW}omUzdI`huj#HmX@}b?nlDg4rc<} zo|`@UI0)f>O#@L2we(;5G->;u!*_fiZQZ0kTzf|%Et$qJfoxOqAusE;Hw;^=BYHZ= zCbX1^z|XwIl@^nnmwG=bi9@KUfa#$>tjHs!o7vK?V}Hoi>wm;sMPtgg+D6+WL(4;2 z^Qx*s1}5w9Dj>M4QoxcdV97WCUAyS^JC-%Etche;>=10l;2%_RBE zcp#yxytuxu4z5A7EvOFBmjj`rrl6ou3oQHc)Vgghq^nBrnmXhD2ePW<)=Exs$e*v3 z*57k0+$N5pP^cmfhohlTqX3(SQXg~FdN9c)C@UlL6mX@H2&OSv0?D_`%g}YrJH8wvv*Od9>br-3CA299PBI? zAv-Y;$YSDc5~AtZ&Y!e>`T}tA&!;EUrJJzxIcbt8|D2{Kw*XDeu9|lcZa8pLR*5ZH z+Lcqjs;bK1GVwj~h@Oq*q82o=i+!xVw)S(1cP`gg0Ok3gZdZDYho26ck>Cfz)oQkbi zE>PsNQb4+M12lWDOWxDM=!|)S)>7%UX|V> zFD7An@p$HtzIw_0SMS>rWxm;~zKR9$mpJPOtwU>a5_o|?@R9g}MRnpxD zCisH=^pcJY)lEq_3ED7_b^1%tUy7moGPwD3IDfLPfV2_1E};qGFhgc2Wu{?`IM~(| z@bLrQR8WVA?a)BlX^ifZ!Exx~;?nw&gCOdiM2b${5v6ruU|*GsFB#g}+X@Oa096Ck{zbMd3zBKNWLfE&ihMgGH;PnhkZyWGCHSJVoiz7tX zp4fmD|7-Hw^hf07sh9jz9rq$Bp5GHC>7H_oVq~DZt@z5e)z+PzC_!QKk#FO>&3zzU z?4HIoBFam>ke&;_KSgQ$e8vc2Vf~*~8p+;-eR`Mll*1O1H1yEEYQ-bQAgC)ArEJqE>ruU7n9qhESNK%aft`<>j;FA7(WJ zBpz3a(8PNX$iw&)tv@XaN!l?%qvW-Dl?ikzDIvqdDkNB2+u5aHwn+A9|8C#p$k5OM z$$uAdY-nb->STgavcA_<@0L5TzT9uI5sZ&KADYVpf$*ud;12)2H_cS?!I=&3?pJ5O zndkCpYHIShD=bWWq^FngXKCv@Ev1W#&A=CV)1u+vwy3U!J{US`k&mjx3r8e*`@{?P z5iVC=x)6~7fy93J{o7S_8EG{W4VPl`0jC&gzp5+i$g`C;kCw%SW~TaUB@(e~2!l1GoCos(UXCVf*g&TtM#nD}qKSRMBkl`-~9W&q3O^gIrr`WY9LXQJFZel+Buwl8> z-aKXB9x6GLr8P2x^G77&?Fp@dqw+HSt1%fF;i>oqihK^R3k0N3+5&vAJ;Ozvdc9p+ zZETklG^|cgRrX!M6%63)RqzEn49%O!thaq?uablhe}kKHNte^kbnUCovSov?a8g3V zf~w2Pc5coddd=)|u@DkY4oF0J@H)D+zS^b}u5V>8c)6~HQTm3e&~ z*dMFFWDP8NhKhj{+fm$~=5_GJTj3M_ADM(bNfm+Z^nCnS?ah$&-qcMDD%s+Lx6gj@ z;>DUT%oMeBjEU=Jp^(oZ1SY5emxS8+(dQE9J@%ia{b8++*syT zrmKvYS5#1tDX+iCqmsU`n=@mw72-4Ce~&>-2}B@EwFNn=08hZYnjoH8-^ecGEql24 zVqnxZ;%=HQZI`yRsY$1(*G)wyz4IlNCZEs#|`>4>I@+$a;kArCD;GV_63~d8=Dv{_A0hEO}{%`@jGG z<4v>NneNZ%d*%Px)*v5JIubT3-QUSvmPvf|Ak+1K#!9`sM{`R6nEF}@%I+7Py!u5L zV%$N_!mbCe7WO3YC;3s=uau@<;qaU4;xuskWcc{c(y=~PNwJq@l}4c%AHz zhLAZ#o44$4cgM_B>roXLTfopQx4MQQjVjpo5|HX5w3I2t?0;W^Jt9Y1atdA(mqOvE zpYpfBN+;Q(lC9VL8!<&{nO;5{*Q4GsFX~fKf#kr#J1q4;v#V?+mUTFx+tC9pW>?*K z%&wP`5-$|Nw)va(@7R`^K{14MR=b@vNbPxOHq<3w=}sStEEwrx>Ml3e6=t2B7^)=4b z%51u_in9LJ?2Pcx37@O+3WpCEB178&I_t=B4!MoqoxW|VCG?n4{X{0Ui&dzsqA~c+ zD^=CN4T?)wfv@ra>Kz;$v}KRjF2nkFExU+9&_1>79w+mx)~7gGuI^4Wm#rZ%S3b2% z9~-nW6|;s!B5!hDV3`IU5Gy&|EQV$f${6OyEjc=NmCT;V}hJFJ0J7>N|RT5x9o2PRZ?+O=S>{HK9adPGS*!d2W$)BczN*U9Loi; zg_VX5CH3&gkkaJF)el!*1m(wheres+9d0hJsgeAkY&|R`J^FBT%<_Qb14EC;itbdYe946RLc zdb(t4nauJllJ%q2axXWQ9se5?lpXiM!uN!`C85J;p9KS+FLbYh zKEMBq{BHc%SpD$zs9*SSk+&{OWWEHYpr`lmpK{b^_Ux~;rIXPkjRULqLO+1>j?bne z0$T1g@Fm}u>dz|YQQ1$0crV@i9VeT~pOihLoMn2{!yzUoNj(A;m7B4fB{ECRgu~$= zAB4`M&t&Qd05mTpnSVeJZd)r#LjxZ@@(;Lzrf|@nKI}YS{-n@6BR!oBkHgXK;HHa; z1cFD)jCGVmMD*Qy_qRt*+7!CHuvyhLh>h!SK9eBPXPcO!XYsNBh=C$JXsxR2@%HHy z+8*swhr^W}PfshuYwzOg9VF5k z|LM~*4MK_BV%rh1{+=u3=tf`lLDU*2br?Bhwl#RN*4ZiK_1M>mhQi@hzTK-YuQ0}Z z+RL_(AyYgA)hi26navn__M7>mV@07Q?PD`0vXlAkPYM!=Vr{CfV{3O(MckYv`h~Q4 zdPnoaYM4dciHGbS-cGAX@fiT5OfM-8sA-x@OD2|Ft$8-8joLLFeEzkmrUtM20@JE` zBc>7In&IZAlVORDD>f8L#BP?ukE}C?(* z>*9%P*`spi>)JxI8K0bo{T4V+ z9{yFMuj^Ug*mx1&<+|CsKM+dpe%aAw!@|;dg98b;jFHB>Laf*!BK5-?z{9rZ@*&W} z=jjLMg_iczV<}@VOvJ%uQj=qw-p{E1pNc}+q>fuGa|Z6?=4NN_n{e{>pdcpBe4jby z(`y(k)6`?Kw$KK{aqfSrq&I7=kOZC5V0q3;lGQM>m6baPEP1lS&x|=In?R7 zMgU7+X~a?fTG$roh_syZPfJRpm6LN>nHA%0i+gAf4GX*qVezMeaBGfyt8-3wM%AcV z85s?`VSHmtL*XxMR#cb2TF>rp$FXxkCHLqTL8Nr&t{JyKLGRxXxE8gPgi|O9Ksvj)Cj9nM3n5R55idK!Jpv2DsmC#%aqXusDLV6M0V{Lr9!QkZ0 z6dwfQpTlV%(^an0VDsYyzO7VgNOO=ZT*m>du50)&EqmoTZfLk&!(^S69qe0wFxXL1NXugWwQqzHM!2 zNFahER=&mwWq^GL%m691y9S9=*mnqJ+d;f|{`A{e{dy8z3pRwJ`{T!tvt@%HwD@;E zDhP(VGcd>iwg576`%r^-8%!Q(_wVPi>VZDJ-Zo3IdHDwG}+(T)eJrkW2DHwB1-0AwkI;wkMK#_J@ z3PKGIVTz>8Yx?;VZ7^_y{zgS#R1VK^ea6iD&#>0J#pGO|FgdcX`Yd*7A?iJ+`Sf=; zOj5ahRn^FF}%$4+0IpIF@>EW7x|7uVO1=U13r&c^tawIY|`8FM+FBu~Jr_wpy zSec~7UtL<`HQGTN7O_6!`j-~AjxY{Ziety$G#gqv~cbt@F=gce7V7Jj*l|qhH$lvsM%1^ z7d$eGa)bBQWxgHac{-e`nU&S!1g={WAV#`N)LqS<*l=DgI6BuE1^NGpde8s;$<(S8 zB(V}}mvDC-L|RcP^%C(<{ev4w4*$PoWq~B^iHQlbm3m0WLx|eoBmQeYrHY>``fc0* zKaB4=o>*Xt#Y@?MFTkt)a3a6frg{q~crDpc&&!V|Q?KB{2232=; zyuRput$u~WLTff|3!Pq%OL35ovQ?7zEbT;}28c#Fl{xpRlPYe70Y;OL*Giig=)SnP$IrwkB=*Dl>{Ap7VP6A^m+ zE*3+p#u`i^E-JLp^OVxKePkn8*DNR6v$5mbQXUtx9%veP!Zc-}HQMMplcl94y&Z4J z+ek>Ln&B?Vs5r~hRUk>I_$Yt8#C5D*a&U5T@>5}<{elwf8NJN?a-@c|>OJe6{wzqJ zCSg7yPiiZ&Oa@@b_XFb4U7jLJ1}U(lp_|+Or+9nnXKjHtw_Ygn-4M0%-WP88`nu$= z8^ugCSj)hR>{6^MRHrk7aE&G9cb6XiH&QrUO50Z*U`7|p%>&`qAK@gY>EbTIWN%CL z7ZUPIN~y~60V7g^qi&OXy)EJH^e+J!{Py7(+M4xHDO&X*b)Dl&CDLFzG{c<_45xw6 zunDs%pDgPB7V%aLy_#!f?8s7;RD(DBOsQrAo~cwi1L5Mw^AMH?1$%-9Cs)r5k5B%2 z%bPv)>Tl_|c3?x~%CUXzzx0Q5XO|n&km28L?W6{!PCL~bC|=(=TfNAzlp|3~Ll_X` z7NLnb7yoVE^5zR|0G@hYYvFEIHCTN^2Qwf>8=A))_wa#4qpy7~hN78?0`ex$Dt&1D z^|S!W%gC@-B8y6`YFMg&6Ng*9B_wpm+bFb64WgD)B3qPJ)YwqwvdqRaMfa30)J~vq zFZ^#-y5iv!7BJPHht+d{ZCWrG-gIni3{Z zqinImgdE_6ZzINPN}L`;KEdgLsSjt+J|@YH1naHNZ8SfM5`KxWN;h3g(T$8sD!a)^_YDVY}5@L`xC z8}oNWM>>S6xBU+Maw(p$3@)el@jGlDnV8Vt1~B&{XCv38}w}pTHxUJO>Zfm1A67QHsAk zn}v$f@sUZ zJ`1L|AtkDD8g}qj&Np`VL7q_9`t6#|OwKLfC%lhim+b>T%=Na%x&7I2qn|#R3$${y z+?V;Pi8|KE3bRwBnp=0da^Qgr6BE;ELW#skf}JL$y@a9O>a(NS8}psNQ&W~J0*g8& z2+3IY|7}(O-`u$87JDqe#3=4Ga1(kOfT?$@uBoZ{3#E(@PSm0qmLgW-9VB{u;@Y_( zIXNT!p1sLJ=KlVTTfrVdQ0U1NIIeUs7>ugOJ1x%|4RIs19~8R(ncbtAq{uGw;-qvl zkp6Q~+BLfdErE-Tw*Va7*=XlX+$4&lyspzR&+K_*VKeW%V-bD#VRUrViT&YTYG!6; z_22dL5szTlPoIVd#-!G(aX3~kL|NICD+@u?+kC;$l)S*D5qrAC_XGl@cnnwy@Y;9U7{(mXVcZMozSU zNGlR5f-2&N>oW~f8${lHUO~=Vv$NsICd0MN$|$lkC;?!_SuXC9P@`~IMPp;y-bH57P|WiwJT6OEM&4qq$8`XlO^{v zPDCDzN`FNxtw{@4tGd>^(FrY8r5E+PJ}+te4GO?V0*V{3wpKh5H2&C5*er?N|4~97 zPR-f1t~G}|Fd28Iq_mMjAZ zBi|@d^>pvhst~c%AYdu}GV>6A;bxb?o4FPf6O+yI(kNNhhu#24p#7Sf0z*K1*pyV2 zm-n_atQ%mehrec@{_(ToH3$q}kh>`4sBLYXee>o#*+RCXVJFx+rZX?wb9$yQN@HGc z>O^061pC7$un*i9?TKpBq6y1Zu=VRK=BqzT%MggN=&o{i^nQ1GHp%-G*1B22_Ef1m ziR!GsbZJ7>;tv{)7Q8@5`oVa5uC#5F7?S3ea6C^wC`f;LDbg-ag&8+#g5JutB!=-! z_;5%+&=+_Y{G*fE6vn+2+Y-SOQthBkR4#J%O)pxldd<-B0MgE*INllb{^>3`8tb3# zoR03YBXOiSw)_dY75#<~^lQPXwWX}LS*7X7L(AG}%WF~2rA3WA|3gp2+?ae3I&1|h z7)>ZtXVZ3@J0c7c8YUz62Tl`n9N}mq94aZmnLT_nO83Zbj53F%q>KDP(LDhNYx3Djz(T%z73a5t zH>14+$gbvg9ZFFQe1l+*jB-$Pz;;;JUs^g)Lu7jynOFrnKi;b3!YOoULoFfQL# z#qoSjK(JW;@v`}_l<7BahI<7!NTsgEu<|fI`{TTy52e_Kk;s$~(@61SC8=o8Ql)-y zS;*J>RXhDwV`a_}P|g z*yL1(dgi$~Jdint*(uINRRJ`3^ZT4q6>hoEqg;c8&l2xUP_5t z;{b2aT(CMaV#-trCS>B+`UCfw)s++ZMx1H@{GLw;dkAU2=j!6}(REB|CrnphTBK0v z?yRhWG>*GauFtdC5x1=dFfG-&`{Wr3AGhT|2i;41q(X+tjT)57AvNF_{z<<1pGl0A zH#Ro1f9RQ)d>>} zU1SPpY^5m4#nfc@6eU zJVR(R`1*Hfb&sf9bAWLXjin4?Bm|NNpG%ESPkUPU9WMB*szd`t$$JMIahon31U}ew zxE95=l~U7e;5X^5AT5$u(pXtnS0Rz8)%?pFvLlqJ^&I%%l{cX4f^p_|wf4D=z=v6^ zhCuVL=PXwMVtxO@hLPLH!o;Ls@P(qJT~j6ptu)#Nlje8V^84Hy(6oQYV8_J+InIfO z`}mB$_E|4={l>6M;>8dXIv)+O%Y1FCXbSY=Ne|@(@PMZ;o^tJP?H~9GG6c#qM(MDM zz<;p<>Wm-t^^RQK5uudQ=WPM|V`^#|XN3NOJfIb)l+o%xEl1h#OFW})fnPdL0lEJX zM)$25fMY>7MafqE`#HD^YyLB_1VYBf3+Kk;4wonh52P_CnjFh2$%`{LLR<30klUx= zYjIgwK7DD#F8!+zG5)eHTeC5p`ijbuiV9~#kBvLV76r-3O;=`N^V+$dei8leT9lsF z>f_sUw?YG)KZq0_l9T{LR~Q>@hPxZ_U;BiUlm!wPV;vof+>9_@5c%Iq zoVGyEGbGRXRULz!JH-71azRcKC_@y?EifWd{n{vL*;#qEJB5tu&uFJ~=l(?D42-<*GAs^Wne39zE&%Ty zj|&ep-|C+{@LhpVBYo%^UYHSsymlW2kXd9vuG5fcgryhxzb}fSFC{*Q6j2iBk{8Ak zUpGH{pk%clg7T{TBIP_ZqqupK$%xY2%L}vI8N=M8uKea|Oy?pBPU?e(%lV2}dUi5R zD$e?^y<>O(++awv@T#k-E{Bo)CKo;^@B4yq1vt^$UQB@jBF1ZsQb?03TwuvYAhL+x z-HHXN{w>S@)$tkCOGTqazf4Xw?{xqfTWOZft>r<;D5M+O^{447JSnLNCT9V99aN@PEslLg zDCXV9hg;xY^QtjaaMZ3tW|6ZgBOz_|Tq%1XILD*9$}z64uA)_6m-Rqn2V=)^S>Kgy zX!UgVYX#KO5tH^*MU%p=>hsfThgDM@oxU9(AS)Wj&$hwIJ^lS-@Z(ll;iMia=kB&_ zz*=E@6uaUxx0C@-z(xV#1F*0M=)@3a-(UWa>hheqUcfopo*Y^Xh{E92p`tDYg^v~S zyU}cyzs0Oy$hd6LCV9y}6@kz0UAPr0T4-r%Za!x60;}r~cpAU^=mlY~6g@hcc6rqs zNv<2CC_i->=}pk!Mb$QlHWh)&vLrD2d^kAtB8^&{houfx_x|E^1{61^G=W6rSI#ty znv+R4b#!E;_H&WHzpE>r`EDQXDz8v_Mhv#%EzqP%a8|lM2k=iUNgEp^tae~f>+R=< zm2OmeEb{b;`t_fTfa;W$K^!5m{>7kAG-pDRMeWW|bH3jCsKsC3#-drm5JUx8{AIK0 zJ&ly+(&NX4;E?fGYuVT5DDR!%2Ov}G1niyv1BYdVPN;tV{I?HnR<-Q1XM@YqyL5ovJ`-;C*#Nb@Eq>$i*agp?g= z@*3y``^L-V&HHt}$}$K7sGGIUXaewXoDAw7y8PEFvaoC*xMuTc-(VYuc7HimROq1a zICFT@W}vkM)XPttioz!g@WGBOlX240bwDK3qfH}rsRO?{@e=dh^ZLfEh&-vMN5Qjq zD$B~6TkGqeS%IkOd)5YS|1MLtf}$cjmw&;+8;nPr@)nl0GX@g+1$laaIgJr*Jxb9k zz)}g7t1J+$Y{t$thu@}gF}GQZW&K14z<^4su7HIS>NLeUJ@_6y<%FP^yHWNkAi{jR zSQ{C{{_@3jXJy4%XDT-AJsf^P>utbx_aqCO0?k(0mn?FYK_a}K>c1z_7vLkfqV`U( z<5deDxkr3x-~xGRz3K7qTez~@>vFU7zlfA15fkp9S+qpj9>&FGz5z66*qe|Fsq_~p z5{2u--LbQI^@^>aPx|C_)>BR73L;>|Hl8BG~azoA2jzd z<7QXsUa($^tj)Jba-&4A?ChPpXPShUZMGUApFnHll@m51@OP|GjYp%S|G(DUHrtn< ztxqTG*AMSn)bFtg)sgYYjqxAtM*QLMm&r>LplzaKS{d?scC5mJZkqoc6$Qk<#s~jI z-hJx3DM*)2Ky_(pahFG}SzekO82uDFtb+ObZEkq*dF^hODyat@JPTJ_=i{c7QqGx7 z)Z$+}+SVik`CH2Akk9(z6B83_F;G_zdrB1hXZd|c;BgORe;x*#qW7l)DWMrD`Tc+yRsc0pQ1B{C!nUZVmpDK zU!((`G+pCSJ9)tHOwc3=>_CKf&esqPaSXxH(z^M+iG4& z&!Ar3R8&+9mJSL5LgWoZNy)hK(QKIUR%eECaAV-1yW7XyAuPNuiN|Bf<36Lq6hHj^|1vxei@8wf%@=QvitH*c z18ZY0^X1*RgfK_eF7fdE66i}ur|P;Vx$s#04VXO^`yQzmyjdjwz+|_&%D(S5^WE>O z=g5H}1KUc6%DCwZ!NI{h{Z~oz2*Noj)obE1b+xtdlHy{lW3(!56u4zW*>gv4FPbcgFDr6X)(tJY)LW>miw ztI|f3Aay+nh?r5JE)_u`4nT7uhX!)%``R!?U}fVnG6wXFTg{)yEK4Hru|*keGIG$I z8*@M#V2M^%a?T#E)v|Wqo%zAZ`h!z&E&_g0l{{T6C{r#3~8=LNC zxM%Losh_V8H>RtbEh|Bd}2Krn#d-;Pp?ThI_X+sY<;(+*;HdUT3!KAozU z6uc0OpSya%Jj_-0A^OeRyy%rdb(;Nr^YzJw=LTUTKKDytq>-t{ z_1;`&Lp>e!pF`L56Mrz1pw6R5hm1RkdUK$P2Az{Q$waL+@mOY6V>AB==Ic?D2fDxG z;}3-1-3nTnY>lPh>th%CmQA0)A z2giEYpQVgt2Vj`jfsX3xO%ll30qC#(i@O%}O53~p_$JmJO#>cCPRY%!k~x&D+vHHq z%YV=YV(Urw^Jz)QkEGyw>lLK0hmHCeQkLEA1Sg(N)|nA&ip1t}%*y3VM*D_iHC7U)w8jwt<#- z*I6+Sln}|uli+N#Ud7?u#V^@&ff0&egq5$9O{Fc%Zc-W> z+|pP2c2!9^6i{6F`FZnWfL7*f?8AH^TGfYqU{K*&t(vtI+v(8nxQteg1XF83SWBYn z*CpyqmG42Q4a2R`K4yl7L+H^u(g7f*L|2hjO~u9U|AoYwQ$b?Q!-gr(s9B`@UGo;g zzfUnYq-XSja)z~whi0z-Cl?TzIZC8?08C>p9&++76^Zwn4yGT2z7J*(cr}-s{s6wG-Na=24P@r6xvJ39`in z2mW{)-Vm~?(?I&SMjxUdc+{dQd?bw#<@u%I89=UUEZ)^F;uf0+<-Gw@vGH@eHGUqF z)2u;Z#Fvm?m~y?Ff%}->G4P#h>0bh&k*Nt)okWC!^^3n&BaaC$?GBq}y@vdrwY444 z-XEU*9pj;{888G7?ze~ek(Sf2zFficp4q{(CfL5zaN(K5$zyd(lesbH^jo`mrI`sH zvpnxePuG&8ncoR%Y#z+myt^Jt3w#wB9Zl;7e-JK1`qhETmOGAg4MtTCc?<76+flmV*{~U=w4`5S$|z;#x!-3^ zOtHJgUtUG0)OVpW#ST{jgU}gr-^$|O(#GbzN%$lhF1XZ`P%7_p+6t=-`Pcnw+ZyG@ zLuIo9ykEU={i5)xOtJ6Ji<+dLey6wY;NDCPN#zm|rV^y@(d4N+;gVHf*W7Q=*p-0G zKLQ{9Lz2Dz*lsGETFrV2k-%$9sPxU_S3e8*Hed4*ej9=Apx*w$!8OX4-Dd-|O)Ycf zH@vJ95q?yiT-}u@Lc($X&+~mXhtu^cp`f z?Wx(8FvGNYP&cm=hy9sSVZPRz2_e63L9PHH+55GL^?JE~AN}A@2(6qYSYqgh#4OR@ zHv{)RF;AX*RQVhlsi_s zm?Ib8k+R;~^j^KyOhWx@@{Q8YcxOWep-zB=-Y<5(U_@5?P=exr`TUITt!n+Oi?7!E zOQfb$5BHpqHCVe5NIT8Q$aFT_MrKpEO6iIJs*Y$N*7g?UR`V>!1p16S1>EWey_{TOFg3u z{W&;7cR85+rkl%mJlvfw8oXfo-W~h6YI8}aP2MVS^}=kbV>5~V7ES4DdXRsIN2|nf zcaL2kLKsLcqA!7qb)n~sPrl-pWCs)%?$|H29;;siFUx&s6@{-6uV;@}JJQ*E$~l1M zjrIiTbSQXnyD-mvhx%Re=JmJGH=#j^>I9_i33$mS8~Jc;N^`sG1Bg?8m%hO$JG)8$ zk=Cv5-b1%Njzg9oZ`HW5K(m#X7z`h;Bfs5?sodXKbzE1ChyYA0rp7hlF7uM8O4Dk1 zZ&DFgVB212VECph6$Ll71KA;Cyl!hbxq%kXjL() z;9$*O>;df+C=GSsWF_rau$LhhDYl6;SfXxex!nE zPlHQ*7!(UZ-iMFw-_?JfzB)mdo^B9K-tx*^NJ+^U$*6xxEr@vRJiyBQz|B)U+U$Dd zu+s}fVVQw*`vCQa`kVflXOeD2hSH*}h|#XD7zd2&Ic5p==skF$VKqM@Mt|;uji2wj zc=t~F&PGZ`%cWkG-*$EjL6apz4f`KDXIC|S=YzU;o1=;+SBQp@;^b0F z``^odm*Brv@c+gUYLYhy*J&vol|rV&XlDPu<`&LcCb%^ceaoy`&Pw8|QE=|x9+VIx z2PFjE(rHh;&C=Z5Y^Ulz>Am9W7Osq!WP#vV#AEf#XqKqVu!lLkzMoO5`QNr; z+>ddnmHo5bJIf6l8~*aOQ(r%MRgY5~J5oRdI^gxJ?*`vAIP=cjW&T-h=H})poSU0_ z8U^p}w1KNR-43a;DB)c4U-v?LTVB9@dd5-37vK6h0p9tDyuQ3w6PfKW8f5NF{IXCT zhbtpxGH>~br-Zq)osHPw_U^f>JVM-Xz+@Qm>zLhvLL**)NW)nyxg;I$iqkwoqsk7C z78f0Abdu$?oPAI$yonJ{y+5q9y@2O);XD-HYvChhVpHP-yEVYo=aY_3pcV-`$jkiu z{`EUHJm3+6rIc0u4Vr(%J6odVgcKDqG>jru?ie=ILAx`6QaN z|E-$O{d3aVCaKn)b`k3 zz(HqJR2lc9rJ#YG^GqdX)qj@O)&?{ZZ@tIBuTfG!-ppVZy(1^u0xMDFvQZ`ac3L{H zA_G`Pd?(|#y#NFKLW3Rq`OZ5+c-;%cA0HaVy`spDqca<=6Uq`4JoL?cjk(zMsF{O9 z)yM_D^HD>VA^ut~pB80**-{k9kT0%TE@1bp9w)!avgGVBy9|YPsX$OD<43HNB+;t4 zE>B-DQ|Kkv`#-XG5@3;@)i<^e^S?+gxhGO#z3MQfPkz95+U{O@#ry8Jw;VArU;haI z3&%Gpai?tk6+fKJi#~Q+vG+D5u#U=lL#156d`4}mwB)UVf)u=doWW)FIp|xKpaLFT zXldj@zXLW&rz>8#@K8<3euMS2Hx}XAP+d%@yMMj!nS?cTl^xg_ls4}+uF|Xe13(*nP zk1MsD|A_26G>)`pa+Hij>y!+At}fRVPSU=7={KWg$l=9oxByZ&|JSLJ)w}pK!c$LA zcvpfv1;j`Xwt+*XHtFa>bZzWAv90&sH4B&Y{^{LfwZeIf`^I@7ogyw(85Rn7*?z35 z+Aw(fgTE5c6=)9E(^} zzlMtM)|OsbsCC_Ll&^6AZpQgVzis}k+VvDoj9Fs{al5y-OdDe=Og2Zh}7%asTr!*LLSdZ*WIJF&yh_p)E8`l%f;sJObrz)(&;)=RX9j2 z!v%=!#X=%A|K1b|3noP)@nv!K$J$dnnfRBv&dj!VAyFhP1c_>E#sn5-Dhnr^4RLpu z;!mQ>MqvER^Sc|KaV+tpeUuu7x@?X}2W#%gmFUUid1=e*=;Hzby400OYG}9-JfSoT zV_c@nz${D29xudTEP8Nxh*s?FZcJ56u( zcMw?~%qLU*ttqY*qYYQqg$4KEy^%J8c?-e)O8?c>;PLeCQuZ=3SIe*Lm_=4|3{BoKHgE0g9+kn78HQ%*>V;;(XZi_2(;I;||3gWf!lO}=} zNv?glw+pd@Wb1viQV|HsybMfT;hq;e+^D2sc92B+NkS%dze-v|4a4CK(nt7nMJwSLTreCnT*%U?Ck8} z{70;B938}Z{^Sjhb64$MTzm?2o*$dH+W1;K&tfD|rODEBDy#qJHjB#Tj=5puHEfy`L!D`B_51D64Vp&;+nk0FRdW(2P#U8L_2V}MaK zs;;~%SzuyhoInU(G$^FfseK)h_e3=m-u9R`eqnX|aCiyc7ASj*9?HCO;sB*CBVu=C zWtZN@#P~Nf{!N7EUlKxd93A^f?HnXG4<8X|l*Gj=c9u(m*V2p*8%l;R&_j)p;6;I# zxVYT*kGdE9h#9DLN5t!*V-XIme0>-r!)-<-@?jv?Xr*Jj@|>qEEDU-g4Fc8kv+{mQ zTa#g758Pe$j@$#X5y=d2>-Hotv~tW52(@ z-+;8UYfBex&z(A@4qFnum?f)Nzq5>W9x5jMATJk)rNK_pEW;bO9`G><6?unDS_Qo9ud=IXLfgE`zj69 z(lS4+aj|4__LUEqwY1t1x=RKGha526@8Q2rZm47j@3505y@?jCnPw78X=p4SC|KiS z+-K>l!CI6QL`6k0++{v?EKCr>+@&xEJJJ+@aP|ud+O6ix@cyA}AZWWxs*S8JV!^?9 z&qq~R1}xnqYr_sV=AE>KlX5s{L$Sd2fsso+AO$p^Cc9v;I3ZHO+p>x}-QKajKF0LBly9)lZ58U0SbR&NB;~Mv| z?sYAA;yY%j$q?+s;1w<|sT~Wjek?=k0>~h8bE5r-i?DF>Ujq{D7BXKjv zxLmD=Fc$jMss4h5jZ~c{A*^26J3T$w(vx1@3thwIbIY|})g0{M6vk0trii=DkpqPy zK~W2Pb$s;fYg}C<8{T8PoWgh*I^A1tjrQ*w8MjCThBuo(HFGU;q$uP=>`d+vfzUTE zQd7*=5v*G>)QNAw&`(!3_}>+$C^5eEWp4$SM0ai7=AI~I3Sy9Ps|$5KelqY6j?=FL zXEx-)&Tx};IUH~HNLwtWKXxY7Gs)`u`(wKcl3a&)WaGw|X_Xr>Z04J|>SEUY_XToV z)qNLqWB)D3o zgi+lK%ky5H^mll8G$u{TT+y|y>8tD5;JODz6U74%8qrs2)8D_x+GmCwIjU)EYq!Lv z$cl&tB*JbeRORvAUf%nagdNOkST4BDJyGe{7dz3=ongA(NZcRmbi_55E)$!mm?RjL zk2h6U`d)Vy92gk626$$veA1E8bS|sO%Uj4$R8;h|v&%JsS2EuV6$ta>SdbnpGV#Ko zJke*uLBW#4!weNNgB8j%I6B7`-Piu6CFNB0)=3!juF<`{nheD4%V9SkUW`JZAAM z@YIh|;Xg~eX?AwLW0b&7*vSz#vCSQF?{~3N_GN z;P#^J@9*!@;KT)YsUnC*uzbzjp)<{kUNH~7O{CZuk`kXJR>GX;Q(ZrOy4SRC9`Vi}e8K`EMPYK6 z^4-nY%#_PzVzD`~Sw&sCu$B>*#xaL}<&4h##=FOLs3jB3?nbnsX|G+>JJj~5)g5l$ z^R4Zid|eN9zg=C|W1(E$OQj9hh-Fm`DjT?-P!HT#1uS4fYwM%K?Idph2S_9t;1}_N zKBJudjVd!mqlDY5FMItd6 zX;?V05X-VSOc-1>|6*9^At<@|>nd%rc-oHzSSIvw?8Jqdi;_!BrKO&)mZ@37Z?SgJ zV{_V(nytWryv>Gcc@kiaT}5zE*;@*>I_3=;Yigx1hEIzK*Lx3C0bg|&NO+^|GyP2M zJ-kNu0++H}&2j|1jXPPi?-Bks)WHUpDNRQAbL{Hc+TQlzk~(aptUJtZ--U7W4?>d= zB0B0cmoMiNJ}O8jcJnUsBn0&>dCz}EU{aTN6ct$joCPQ8Ra=EaI6J=5o#kp^HI! ze7Ds)Ru+~Xac_jt~FR*S8yAq=lkQw+C^OeYxCKWvU-Zzd0Vx$IR7NWj8oyt%F zs-oBIF;3(8g=7qgR5ti8i{>%CxdJP1DNh`NL6vFMhRZ+cWo z@Ury{OYfdtZ|_gcRxgDh$ff7KNGK%AWQF0@N`U>PTRr7Oa%iF88Zk(Ko8~ojbPQTYrb7o*+ z9nUqTKe|W}vdhXCLcjF5cQV3O?@er2Ms?mzV_2^Opz5zb_eN1sz=`U_@L+h z4|zd)`JIn~80`5Uhd-vL-H)HG3}P&VljNsqf;wqfC=H9vWW72ijpA-w1Z~C~9t#gF zf9-pe4ntpmsAkmoeEpp`<0-11lho#Nl_hG`KR9?KEG1=0Oa8cBQ^J6^Pv}KcW1V>( zp(_uqk+ghMgRxp5YwErFjSoZjbwA4sP$!d_bT2$u^4`CQ5%J)^6k2=|noeN`*b|oP z{ILSW%N!h@dAYt5D41=-XpB(z{3<2LdZ_IqfSp;<{k^d>-Uy?s`T9Y|D>KD`^w1MO zFgx>3l$Tk+653H9V+u2nn&BX4H)kT@+|6lni`BQ<%`=#p+OB+j{aR;|o#W$A?;3eh zG`ZMceOfOSx}(K`i$)&xWeF$3*LQ;()41uNL{NV(`f3R#7>^DORedwQl}sXYR&2W@ zwkV36lhfM$dfT)(YrXPWxpImSdv4uQp(Sx!xo+7YFZQR$cn<1(#(ByP8MC34Ry*-# z)ktkmLDBzhBL9r*hkep^NLpz zgwdLnfeN+J)?Q)i4ISP#4B=0{b2XiY0`CD6#m zH7Vkzc7257fcjic+)}a-luM_DF?2n~A-~}>d=fbe$zC0kqh)2Y^P+F31|J#ORt{cI zTkA}OEvsMq+3iv-NY$hSwr--Rx~2%b!=6j3)8kE>?OigUtI+QhLx;Fc_!ev_%v{uJ#|f#LR`sp>>bSw3**(D5tMu89q51^g(s;sDWTYbn(i5i^upK_OMRg zH7&u*RH4KZltYsV(B^~#;w;pv%t^bW!iM`D9e zm|`sByT>;^SYQmJ<^O)V!v`hl1CB+Ply}UP2_eoXBEj z)`UmL%)cPqzxvD2Efw(E&Qul|fq_^?LGe2_)n@%sFY$x%+9Ceagpg}V?Iw3d%)Yv$ zR8uk!IE`5{zv|VHN8fUeg}>ia*kX=HLKAO6giDj;^YGvx#jVuD3e+#XH2i2s|Mb$s z7cZg{#{)N#;>POy+Xu~r*|33TG(P~ZV%=6QTs@mj*XJn0iPIh9j+34D$k%T}-&ZB6 z)#50j9RPU1GpRyXemxL~|3e+>%j6)fm7{yHp>Z4kk{SOsP?YA~pMXMXMND_hOsO?5 zIeQ>e^36Y(Wmf9^S?GU)Hdd33&FgEQx!=SgQM_B>p2U@yZ+e;dj@GIKn4}gja%b-@ z^Z1irj#9q>t+37l29$ks4wS_9e9q0vt~#fW({z3 zan9e_44yjKpA&>Qp>^gHcgav;!NA2Y@#;f+;}3K02&2s$DOGA|B)Vr`F???|{p#{a zTYKXT2+&^`nmk;-KKF77A>DO<@*J>? z#g%qaFTmUG7DytIoB8?>NS<^RM3>JOvs)0j;M;chX%wINoTD(Ok7N%Cf!iAYg6u zy)^B$V#%yDhK@C^A<}v3uy30`=qh4oNN6}`nk$FE-u@uY+-r)eNe?Vo>5q#>2bNbZ zk8EbO+WbrAUoL7f*ZTlybP&I4Y;0^gmC=3mV@bD7bE{4u<0nODyFH|pRgeQhFy^99ITni2{vwm+&#d+{~=$pOK6h&1Mx%^#4DDxb+D zcNm0Pm3;{a_=B798QL-mnG^b6GqNsA=dQO2d`n!eX5XONY%Z1GNuerUPGqeMMYgFR zc6D@g;1WfYT}E9D-SElKlLnu0x~otJOGdkE9}kZcNIyv{jOIq}YcxG20Ryc(B|&pr z${*k%zd2wD?Q^UhKlPn6E^%`DFa3&PuNrh3P!|~Zl%U*6Fa@aSA#eP!f^2pDJ+cwh zL^^@&rz*;w4f{6gb}E=MJ$@~f>kIzMKzy*`MT0hri&Wss?yj&0aZmbqPnDlW%vSj> zbMlp=QZwPxxvOddbH1yqmb9Sq^scuc4-zK*W|&4nH7uh8wyS8nMMNuPeV{}M`&Z_V z9?Wk!x8KgW2cG}sL+j7me3{RGL=P6%Kbrvj)(`NTs>|3{%uIM_HEgRQi#Tjj<@RqC zfe{;Y=#SHDvnlGTL$<62yClE(H<&DRi#7cxJ@gp(=b)$&MbH%TGXfLj^z7N>C;FUB0ab|B%54mG{y;{tOc;)7u`vGx zv+eJrr7lk}_-A#8JP$fBPXhw%XJxdkxRL!-oq%1hpLhX^h2O){|fc86T_gUdpstDLycDJt>nZE((gmV99#)U+?75h

ojY5t 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