From c6e9a05c2ac1020c8b4894ecd42076a9a27a097e Mon Sep 17 00:00:00 2001 From: Lucas SAUDON Date: Mon, 27 May 2024 21:14:34 +0200 Subject: [PATCH] feat: Ajouter l'authentification --- .gitignore | 5 + .vscode/launch.json | 8 +- app/env.template.json | 3 + app/ios/Flutter/Debug.xcconfig | 1 + app/ios/Flutter/Release.xcconfig | 1 + app/ios/Podfile | 44 ++++ app/ios/Podfile.lock | 29 +++ app/ios/Runner.xcodeproj/project.pbxproj | 172 +++++++++++++-- .../contents.xcworkspacedata | 3 + app/lib/main.dart | 41 +++- app/lib/src/app.dart | 57 ++++- .../domain/authentification_statut.dart | 5 + .../authentification_statut_manager.dart | 21 ++ .../domain/information_de_connexion.dart | 14 ++ .../ports/authentification_repository.dart | 8 + .../infrastructure/adapters/api_url.dart | 10 + .../authentification_api_adapter.dart | 41 ++++ .../adapters/authentification_api_client.dart | 61 ++++++ .../authentification_token_storage.dart | 52 +++++ .../se_connecter/bloc/se_connecter_bloc.dart | 45 ++++ .../se_connecter/bloc/se_connecter_event.dart | 30 +++ .../se_connecter/bloc/se_connecter_state.dart | 26 +++ app/lib/src/l10n/l10n.dart | 8 +- app/lib/src/pages/accueil/accueil_page.dart | 42 ++++ .../authentification/se_connecter_page.dart | 76 +++++++ .../pre_onboarding_carrousel_page.dart | 17 +- .../pre_onboarding/pre_onboarding_page.dart | 17 +- app/lib/src/router/app_router.dart | 39 +++- .../src/router/go_router_refresh_stream.dart | 18 ++ app/pubspec.lock | 196 +++++++++++++++++- app/pubspec.yaml | 17 +- app/test/authentification_api_test.dart | 5 + .../authentification_repository_mock.dart | 24 +++ app/test/main_test.dart | 36 +++- app/test/scenario_context.dart | 12 ++ app/test/set_up_widgets.dart | 9 + app/test/steps/iel_appuie_sur.dart | 7 + app/test/steps/iel_appuie_sur_commencer.dart | 2 +- app/test/steps/iel_est_connecte.dart | 9 + app/test/steps/iel_lance_lapplication.dart | 19 +- app/test/steps/steps.dart | 2 + packages/agir_lints/lib/analysis_options.yaml | 1 + .../dsfr.dart/example/lib/buttons_page.dart | 2 +- .../dsfr.dart/example/lib/inputs_page.dart | 25 +++ .../dsfr.dart/example/lib/links_page.dart | 2 +- packages/dsfr.dart/example/lib/main.dart | 2 + .../lib/src/composants/composants.dart | 1 + .../dsfr.dart/lib/src/composants/input.dart | 113 ++++++++++ .../lib/src/fondamentaux/colors.g.dart | 45 ++-- .../lib/src/fondamentaux/spacing.g.dart | 2 +- .../style-dictionary/tokens/colors/grey.json | 63 +++--- packages/dsfr.dart/test/input_test.dart | 44 ++++ 52 files changed, 1417 insertions(+), 115 deletions(-) create mode 100644 app/env.template.json create mode 100644 app/ios/Podfile create mode 100644 app/ios/Podfile.lock create mode 100644 app/lib/src/fonctionnalites/authentification/domain/authentification_statut.dart create mode 100644 app/lib/src/fonctionnalites/authentification/domain/authentification_statut_manager.dart create mode 100644 app/lib/src/fonctionnalites/authentification/domain/information_de_connexion.dart create mode 100644 app/lib/src/fonctionnalites/authentification/domain/ports/authentification_repository.dart create mode 100644 app/lib/src/fonctionnalites/authentification/infrastructure/adapters/api_url.dart create mode 100644 app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_api_adapter.dart create mode 100644 app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_api_client.dart create mode 100644 app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_token_storage.dart create mode 100644 app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_bloc.dart create mode 100644 app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_event.dart create mode 100644 app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_state.dart create mode 100644 app/lib/src/pages/accueil/accueil_page.dart create mode 100644 app/lib/src/pages/authentification/se_connecter_page.dart create mode 100644 app/lib/src/router/go_router_refresh_stream.dart create mode 100644 app/test/authentification_api_test.dart create mode 100644 app/test/authentification_repository_mock.dart create mode 100644 app/test/scenario_context.dart create mode 100644 app/test/set_up_widgets.dart create mode 100644 app/test/steps/iel_appuie_sur.dart create mode 100644 app/test/steps/iel_est_connecte.dart create mode 100755 packages/dsfr.dart/example/lib/inputs_page.dart create mode 100644 packages/dsfr.dart/lib/src/composants/input.dart create mode 100644 packages/dsfr.dart/test/input_test.dart diff --git a/.gitignore b/.gitignore index 329c1351..fedec784 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,8 @@ app.*.map.json # Example **/example/pubspec.lock + +# Env +**/env.*.json +**/env.json +!**/env.template.json \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index b28a6c01..127740df 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,9 @@ "type": "dart", "args": [ "--flavor", - "development" + "development", + "--dart-define-from-file", + "env.development.json" ] }, { @@ -18,7 +20,9 @@ "type": "dart", "args": [ "--flavor", - "production" + "production", + "--dart-define-from-file", + "env.json" ] }, { diff --git a/app/env.template.json b/app/env.template.json new file mode 100644 index 00000000..2f4802b1 --- /dev/null +++ b/app/env.template.json @@ -0,0 +1,3 @@ +{ + "API_URL": "X" +} \ No newline at end of file diff --git a/app/ios/Flutter/Debug.xcconfig b/app/ios/Flutter/Debug.xcconfig index 592ceee8..ec97fc6f 100644 --- a/app/ios/Flutter/Debug.xcconfig +++ b/app/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/app/ios/Flutter/Release.xcconfig b/app/ios/Flutter/Release.xcconfig index 592ceee8..c4855bfe 100644 --- a/app/ios/Flutter/Release.xcconfig +++ b/app/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/app/ios/Podfile b/app/ios/Podfile new file mode 100644 index 00000000..d97f17e2 --- /dev/null +++ b/app/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock new file mode 100644 index 00000000..1604b92e --- /dev/null +++ b/app/ios/Podfile.lock @@ -0,0 +1,29 @@ +PODS: + - Flutter (1.0.0) + - flutter_secure_storage (6.0.0): + - Flutter + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + +DEPENDENCIES: + - Flutter (from `Flutter`) + - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + flutter_secure_storage: + :path: ".symlinks/plugins/flutter_secure_storage/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + +SPEC CHECKSUMS: + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + +PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 + +COCOAPODS: 1.15.2 diff --git a/app/ios/Runner.xcodeproj/project.pbxproj b/app/ios/Runner.xcodeproj/project.pbxproj index 4f9baeb4..a761fa11 100644 --- a/app/ios/Runner.xcodeproj/project.pbxproj +++ b/app/ios/Runner.xcodeproj/project.pbxproj @@ -14,6 +14,8 @@ 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 */; }; + A3196C1BAECA0EFBC1B9C235 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3FE653E553199EA0D8E60C65 /* Pods_RunnerTests.framework */; }; + DDA609B743CFD8479EF06A83 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47B835E56B6C85A06586C789 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -40,14 +42,27 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 056B113D6A8EF1504D406DA1 /* Pods-Runner.profile-development.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-development.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-development.xcconfig"; sourceTree = ""; }; + 0AC4C333702A3046F2E982C0 /* Pods-Runner.debug-development.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-development.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-development.xcconfig"; sourceTree = ""; }; + 10BA0A289E3094318D1225F9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 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 = ""; }; + 2ACA48AA689B89B91FE5B1B1 /* Pods-RunnerTests.profile-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile-production.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile-production.xcconfig"; 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 = ""; }; + 3FE653E553199EA0D8E60C65 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 47B835E56B6C85A06586C789 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 518E4FF8BD465ABD7690FF5B /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 681531C5085E58B853F5A065 /* Pods-RunnerTests.profile-development.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile-development.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile-development.xcconfig"; 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 = ""; }; + 7AF14BBA9415C4B802EAB2D9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 87E654F1915BBD2B9717400A /* Pods-Runner.release-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-production.xcconfig"; sourceTree = ""; }; + 93600FC6F396502C8666800E /* Pods-RunnerTests.release-development.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release-development.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release-development.xcconfig"; sourceTree = ""; }; + 957C0E65F7ABC725A142E321 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 9678FF7D89509267972EFD0F /* Pods-Runner.release-development.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-development.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-development.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; }; @@ -55,6 +70,13 @@ 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 = ""; }; + 9B2E5048FCB2391063CDFA81 /* Pods-Runner.profile-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile-production.xcconfig"; sourceTree = ""; }; + BD84FB5C135CF7DB74B0EDCC /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + BF40B846D76B93DF3D209BDE /* Pods-RunnerTests.debug-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug-production.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug-production.xcconfig"; sourceTree = ""; }; + C28C76E653078CF2B933EE82 /* Pods-RunnerTests.release-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release-production.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release-production.xcconfig"; sourceTree = ""; }; + E2E9EEE1F59D1DCA87B597D7 /* Pods-Runner.debug-production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-production.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-production.xcconfig"; sourceTree = ""; }; + EB097862E46FC7C70F615A1A /* Pods-RunnerTests.debug-development.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug-development.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug-development.xcconfig"; sourceTree = ""; }; + EEF68D5A5EAE6181C6248DF9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,6 +84,15 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + DDA609B743CFD8479EF06A83 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CFD4550FD4C92567D5103DF9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A3196C1BAECA0EFBC1B9C235 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -76,6 +107,32 @@ path = RunnerTests; sourceTree = ""; }; + 6986E75ADAEF4AC1389F0CF1 /* Pods */ = { + isa = PBXGroup; + children = ( + EEF68D5A5EAE6181C6248DF9 /* Pods-Runner.debug.xcconfig */, + 0AC4C333702A3046F2E982C0 /* Pods-Runner.debug-development.xcconfig */, + E2E9EEE1F59D1DCA87B597D7 /* Pods-Runner.debug-production.xcconfig */, + 10BA0A289E3094318D1225F9 /* Pods-Runner.release.xcconfig */, + 9678FF7D89509267972EFD0F /* Pods-Runner.release-development.xcconfig */, + 87E654F1915BBD2B9717400A /* Pods-Runner.release-production.xcconfig */, + 957C0E65F7ABC725A142E321 /* Pods-Runner.profile.xcconfig */, + 056B113D6A8EF1504D406DA1 /* Pods-Runner.profile-development.xcconfig */, + 9B2E5048FCB2391063CDFA81 /* Pods-Runner.profile-production.xcconfig */, + 7AF14BBA9415C4B802EAB2D9 /* Pods-RunnerTests.debug.xcconfig */, + EB097862E46FC7C70F615A1A /* Pods-RunnerTests.debug-development.xcconfig */, + BF40B846D76B93DF3D209BDE /* Pods-RunnerTests.debug-production.xcconfig */, + BD84FB5C135CF7DB74B0EDCC /* Pods-RunnerTests.release.xcconfig */, + 93600FC6F396502C8666800E /* Pods-RunnerTests.release-development.xcconfig */, + C28C76E653078CF2B933EE82 /* Pods-RunnerTests.release-production.xcconfig */, + 518E4FF8BD465ABD7690FF5B /* Pods-RunnerTests.profile.xcconfig */, + 681531C5085E58B853F5A065 /* Pods-RunnerTests.profile-development.xcconfig */, + 2ACA48AA689B89B91FE5B1B1 /* Pods-RunnerTests.profile-production.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -94,6 +151,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, + 6986E75ADAEF4AC1389F0CF1 /* Pods */, + B90CBB918408E0CE83E1D375 /* Frameworks */, ); sourceTree = ""; }; @@ -121,6 +180,15 @@ path = Runner; sourceTree = ""; }; + B90CBB918408E0CE83E1D375 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 47B835E56B6C85A06586C789 /* Pods_Runner.framework */, + 3FE653E553199EA0D8E60C65 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -128,8 +196,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 39AD47168F0C39F491EBECF5 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, + CFD4550FD4C92567D5103DF9 /* Frameworks */, ); buildRules = ( ); @@ -145,12 +215,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 924448706E821FCC976724DD /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 0F115A18356B2469A1D88B1F /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -222,6 +294,45 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 0F115A18356B2469A1D88B1F /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 39AD47168F0C39F491EBECF5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -238,6 +349,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 924448706E821FCC976724DD /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -359,7 +492,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-production; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-production"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; @@ -379,6 +512,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 7AF14BBA9415C4B802EAB2D9 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -396,6 +530,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = BD84FB5C135CF7DB74B0EDCC /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -411,6 +546,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 518E4FF8BD465ABD7690FF5B /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -428,7 +564,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon-production; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = "AppIcon-production"; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -485,7 +621,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon-production; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = "AppIcon-production"; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -539,7 +675,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-production; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-production"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; @@ -562,7 +698,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-production; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-production"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; @@ -584,7 +720,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon-development; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = "AppIcon-development"; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -641,7 +777,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-development; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-development"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; @@ -662,6 +798,7 @@ }; D2DC2FCF2BFDF3EC0088C2BF /* Debug-development */ = { isa = XCBuildConfiguration; + baseConfigurationReference = EB097862E46FC7C70F615A1A /* Pods-RunnerTests.debug-development.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -681,7 +818,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon-development; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = "AppIcon-development"; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -735,7 +872,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-development; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-development"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; @@ -755,6 +892,7 @@ }; D2DC2FD22BFDF3FB0088C2BF /* Release-development */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 93600FC6F396502C8666800E /* Pods-RunnerTests.release-development.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -824,7 +962,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-development; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-development"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; @@ -844,6 +982,7 @@ }; D2DC2FD52BFDF4000088C2BF /* Profile-development */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 681531C5085E58B853F5A065 /* Pods-RunnerTests.profile-development.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -861,7 +1000,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon-production; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = "AppIcon-production"; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -918,7 +1057,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-production; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-production"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; @@ -939,6 +1078,7 @@ }; D2DC2FD82BFE08F80088C2BF /* Debug-production */ = { isa = XCBuildConfiguration; + baseConfigurationReference = BF40B846D76B93DF3D209BDE /* Pods-RunnerTests.debug-production.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -958,7 +1098,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon-production; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = "AppIcon-production"; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -1012,7 +1152,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-production; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-production"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; @@ -1032,6 +1172,7 @@ }; D2DC2FDB2BFE09000088C2BF /* Release-production */ = { isa = XCBuildConfiguration; + baseConfigurationReference = C28C76E653078CF2B933EE82 /* Pods-RunnerTests.release-production.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -1101,7 +1242,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-production; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-production"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; @@ -1121,6 +1262,7 @@ }; D2DC2FDE2BFE09060088C2BF /* Profile-production */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 2ACA48AA689B89B91FE5B1B1 /* Pods-RunnerTests.profile-production.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; diff --git a/app/ios/Runner.xcworkspace/contents.xcworkspacedata b/app/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16..21a3cc14 100644 --- a/app/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/app/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/app/lib/main.dart b/app/lib/main.dart index 72f53686..df3db893 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,15 +1,50 @@ +// ignore_for_file: do_not_use_environment + import 'dart:ui'; -import 'package:agir/src/app.dart'; +import 'package:app/src/app.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut_manager.dart'; +import 'package:app/src/fonctionnalites/authentification/infrastructure/adapters/api_url.dart'; +import 'package:app/src/fonctionnalites/authentification/infrastructure/adapters/authentification_api_adapter.dart'; +import 'package:app/src/fonctionnalites/authentification/infrastructure/adapters/authentification_api_client.dart'; +import 'package:app/src/fonctionnalites/authentification/infrastructure/adapters/authentification_token_storage.dart'; import 'package:dsfr/dsfr.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -void main() { +Future main() async { WidgetsFlutterBinding.ensureInitialized(); _registerErrorHandlers(); - runApp(const App()); + const apiUrlKey = 'API_URL'; + const apiUrl = String.fromEnvironment(apiUrlKey); + if (apiUrl.isEmpty) { + throw Exception(apiUrlKey); + } + + final authentificationStatusManager = AuthentificationStatutManager(); + + final authentificationTokenStorage = AuthentificationTokenStorage( + secureStorage: const FlutterSecureStorage( + aOptions: AndroidOptions(encryptedSharedPreferences: true), + ), + authentificationStatusManager: authentificationStatusManager, + ); + + await authentificationTokenStorage.initialise(); + + runApp( + App( + authentificationRepository: AuthentificationApiAdapter( + apiClient: AuthentificationApiClient( + apiUrl: ApiUrl(Uri.parse(apiUrl)), + authentificationTokenStorage: authentificationTokenStorage, + ), + ), + authentificationStatusManager: authentificationStatusManager, + ), + ); } void _registerErrorHandlers() { diff --git a/app/lib/src/app.dart b/app/lib/src/app.dart index b0f04ac4..0246035a 100644 --- a/app/lib/src/app.dart +++ b/app/lib/src/app.dart @@ -1,16 +1,57 @@ -import 'package:agir/src/router/app_router.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut_manager.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/ports/authentification_repository.dart'; +import 'package:app/src/fonctionnalites/se_connecter/bloc/se_connecter_bloc.dart'; +import 'package:app/src/router/app_router.dart'; import 'package:dsfr/dsfr.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; -class App extends StatelessWidget { - const App({super.key}); +class App extends StatefulWidget { + const App({ + required this.authentificationStatusManager, + required this.authentificationRepository, + super.key, + }); + + final AuthentificationStatutManager authentificationStatusManager; + final AuthentificationRepository authentificationRepository; + + @override + State createState() => _AppState(); +} + +class _AppState extends State { + late final GoRouter _goRouter; + + @override + void initState() { + super.initState(); + _goRouter = goRouter( + authentificationStatusManager: widget.authentificationStatusManager, + ); + } @override - Widget build(final BuildContext context) => MaterialApp.router( - routerConfig: goRouter(), - theme: ThemeData( - colorSchemeSeed: DsfrColors.blueFranceSun113, - scaffoldBackgroundColor: Colors.white, + Widget build(final BuildContext context) => + RepositoryProvider.value( + value: widget.authentificationRepository, + child: MultiBlocProvider( + providers: [ + BlocProvider( + create: (final context) => SeConnecterBloc( + authentificationRepository: widget.authentificationRepository, + ), + ), + ], + child: MaterialApp.router( + routerConfig: _goRouter, + theme: ThemeData( + colorSchemeSeed: DsfrColors.blueFranceSun113, + scaffoldBackgroundColor: Colors.white, + appBarTheme: const AppBarTheme(backgroundColor: Colors.white), + ), + ), ), ); } diff --git a/app/lib/src/fonctionnalites/authentification/domain/authentification_statut.dart b/app/lib/src/fonctionnalites/authentification/domain/authentification_statut.dart new file mode 100644 index 00000000..14fed131 --- /dev/null +++ b/app/lib/src/fonctionnalites/authentification/domain/authentification_statut.dart @@ -0,0 +1,5 @@ +enum AuthentificationStatut { + inconnu, + connecte, + pasConnecte, +} diff --git a/app/lib/src/fonctionnalites/authentification/domain/authentification_statut_manager.dart b/app/lib/src/fonctionnalites/authentification/domain/authentification_statut_manager.dart new file mode 100644 index 00000000..69144ff0 --- /dev/null +++ b/app/lib/src/fonctionnalites/authentification/domain/authentification_statut_manager.dart @@ -0,0 +1,21 @@ +import 'dart:async'; +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut.dart'; + +class AuthentificationStatutManager { + AuthentificationStatut _statut = AuthentificationStatut.inconnu; + + final _controller = StreamController(); + + void gererAuthentificationStatut(final AuthentificationStatut statut) { + _statut = statut; + _controller.add(_statut); + } + + AuthentificationStatut statutActuel() => _statut; + + Stream statutModifie() => _controller.stream; + + Future dispose() async { + await _controller.close(); + } +} diff --git a/app/lib/src/fonctionnalites/authentification/domain/information_de_connexion.dart b/app/lib/src/fonctionnalites/authentification/domain/information_de_connexion.dart new file mode 100644 index 00000000..fdf9c2de --- /dev/null +++ b/app/lib/src/fonctionnalites/authentification/domain/information_de_connexion.dart @@ -0,0 +1,14 @@ +import 'package:equatable/equatable.dart'; + +class InformationDeConnexion extends Equatable { + const InformationDeConnexion({ + required this.adresseMail, + required this.motDePasse, + }); + + final String adresseMail; + final String motDePasse; + + @override + List get props => [adresseMail, motDePasse]; +} diff --git a/app/lib/src/fonctionnalites/authentification/domain/ports/authentification_repository.dart b/app/lib/src/fonctionnalites/authentification/domain/ports/authentification_repository.dart new file mode 100644 index 00000000..1d453d0d --- /dev/null +++ b/app/lib/src/fonctionnalites/authentification/domain/ports/authentification_repository.dart @@ -0,0 +1,8 @@ +import 'package:app/src/fonctionnalites/authentification/domain/information_de_connexion.dart'; + +abstract interface class AuthentificationRepository { + Future connectionDemandee( + final InformationDeConnexion informationDeConnexion, + ); + Future deconnectionDemandee(); +} diff --git a/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/api_url.dart b/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/api_url.dart new file mode 100644 index 00000000..44ce4dc4 --- /dev/null +++ b/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/api_url.dart @@ -0,0 +1,10 @@ +import 'package:equatable/equatable.dart'; + +class ApiUrl extends Equatable { + const ApiUrl(this.valeur); + + final Uri valeur; + + @override + List get props => [valeur]; +} diff --git a/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_api_adapter.dart b/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_api_adapter.dart new file mode 100644 index 00000000..01a1f9fd --- /dev/null +++ b/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_api_adapter.dart @@ -0,0 +1,41 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:app/src/fonctionnalites/authentification/domain/information_de_connexion.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/ports/authentification_repository.dart'; +import 'package:app/src/fonctionnalites/authentification/infrastructure/adapters/authentification_api_client.dart'; + +class AuthentificationApiAdapter implements AuthentificationRepository { + AuthentificationApiAdapter({ + required final AuthentificationApiClient apiClient, + }) : _apiClient = apiClient; + + final AuthentificationApiClient _apiClient; + + @override + Future connectionDemandee( + final InformationDeConnexion informationDeConnexion, + ) async { + final response = await _apiClient.post( + Uri.parse('/utilisateurs/login'), + body: { + 'email': informationDeConnexion.adresseMail, + 'mot_de_passe': informationDeConnexion.motDePasse, + }, + ); + if (response.statusCode == 201) { + final json = jsonDecode(response.body) as Map; + final token = json['token'] as String; + final utilisateur = json['utilisateur'] as Map; + final utilisateurId = utilisateur['id'] as String; + await _apiClient.sauvegarderTokenEtUtilisateurId(token, utilisateurId); + } else { + return; + } + } + + @override + Future deconnectionDemandee() async { + await _apiClient.supprimerTokenEtUtilisateurId(); + } +} diff --git a/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_api_client.dart b/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_api_client.dart new file mode 100644 index 00000000..e304e4f7 --- /dev/null +++ b/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_api_client.dart @@ -0,0 +1,61 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:app/src/fonctionnalites/authentification/infrastructure/adapters/api_url.dart'; +import 'package:app/src/fonctionnalites/authentification/infrastructure/adapters/authentification_token_storage.dart'; +import 'package:http/http.dart'; + +class AuthentificationApiClient extends BaseClient { + AuthentificationApiClient({ + required this.apiUrl, + required final AuthentificationTokenStorage authentificationTokenStorage, + final Client? inner, + }) : _inner = inner ?? Client(), + _authentificationTokenStorage = authentificationTokenStorage; + + final Client _inner; + final ApiUrl apiUrl; + final AuthentificationTokenStorage _authentificationTokenStorage; + + Future sauvegarderTokenEtUtilisateurId( + final String token, + final String utilisateurId, + ) async => + _authentificationTokenStorage.sauvegarderTokenEtUtilisateurId( + token, + utilisateurId, + ); + + Future supprimerTokenEtUtilisateurId() async => + _authentificationTokenStorage.supprimerTokenEtUtilisateurId(); + + Future recupererUtilisateurId() async => + _authentificationTokenStorage.recupererUtilisateurId(); + + @override + Future post( + final Uri url, { + final Map? headers, + final Object? body, + final Encoding? encoding, + }) async { + final response = await super.post( + Uri.parse( + '${apiUrl.valeur.scheme}://${apiUrl.valeur.host}${apiUrl.valeur.path}/${url.path}', + ), + headers: headers, + body: body, + encoding: encoding, + ); + return response; + } + + @override + Future send(final BaseRequest request) async { + final token = await _authentificationTokenStorage.recupererToken(); + if (token != null) { + request.headers[HttpHeaders.authorizationHeader] = 'Bearer $token'; + } + return _inner.send(request); + } +} diff --git a/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_token_storage.dart b/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_token_storage.dart new file mode 100644 index 00000000..dfe7a468 --- /dev/null +++ b/app/lib/src/fonctionnalites/authentification/infrastructure/adapters/authentification_token_storage.dart @@ -0,0 +1,52 @@ +import 'dart:async'; + +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut_manager.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +class AuthentificationTokenStorage { + AuthentificationTokenStorage({ + required final FlutterSecureStorage secureStorage, + required final AuthentificationStatutManager authentificationStatusManager, + }) : _secureStorage = secureStorage, + _authentificationStatusManager = authentificationStatusManager; + + final FlutterSecureStorage _secureStorage; + final AuthentificationStatutManager _authentificationStatusManager; + + Future initialise() async { + final token = await recupererToken(); + if (token != null) { + _authentificationStatusManager + .gererAuthentificationStatut(AuthentificationStatut.connecte); + } else { + _authentificationStatusManager + .gererAuthentificationStatut(AuthentificationStatut.pasConnecte); + } + return true; + } + + final _tokenKey = 'token'; + Future recupererToken() async => _secureStorage.read(key: _tokenKey); + + final _utilisateurIdKey = 'utilisateurId'; + Future recupererUtilisateurId() async => + _secureStorage.read(key: _utilisateurIdKey); + + Future sauvegarderTokenEtUtilisateurId( + final String token, + final String utilisateurId, + ) async { + _authentificationStatusManager + .gererAuthentificationStatut(AuthentificationStatut.connecte); + await _secureStorage.write(key: _tokenKey, value: token); + await _secureStorage.write(key: _utilisateurIdKey, value: utilisateurId); + } + + Future supprimerTokenEtUtilisateurId() async { + _authentificationStatusManager + .gererAuthentificationStatut(AuthentificationStatut.pasConnecte); + await _secureStorage.delete(key: _tokenKey); + await _secureStorage.delete(key: _utilisateurIdKey); + } +} diff --git a/app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_bloc.dart b/app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_bloc.dart new file mode 100644 index 00000000..fbe07838 --- /dev/null +++ b/app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_bloc.dart @@ -0,0 +1,45 @@ +import 'dart:async'; +import 'package:app/src/fonctionnalites/authentification/domain/information_de_connexion.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/ports/authentification_repository.dart'; +import 'package:app/src/fonctionnalites/se_connecter/bloc/se_connecter_event.dart'; +import 'package:app/src/fonctionnalites/se_connecter/bloc/se_connecter_state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class SeConnecterBloc extends Bloc { + SeConnecterBloc({ + required final AuthentificationRepository authentificationRepository, + }) : _authentificationRepository = authentificationRepository, + super(const SeConnecterState()) { + on(_onAdresseMailAChange); + on(_onMotDePasseAChange); + on(_onConnexionDemandee); + } + + final AuthentificationRepository _authentificationRepository; + + Future _onAdresseMailAChange( + final SeConnecterAdresseMailAChange event, + final Emitter emit, + ) async { + emit(state.copyWith(adresseMail: event.valeur)); + } + + Future _onMotDePasseAChange( + final SeConnecterMotDePasseAChange event, + final Emitter emit, + ) async { + emit(state.copyWith(motDePasse: event.valeur)); + } + + Future _onConnexionDemandee( + final SeConnecterConnexionDemandee event, + final Emitter emit, + ) async { + final informationDeConnexion = InformationDeConnexion( + adresseMail: state.adresseMail, + motDePasse: state.motDePasse, + ); + await _authentificationRepository + .connectionDemandee(informationDeConnexion); + } +} diff --git a/app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_event.dart b/app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_event.dart new file mode 100644 index 00000000..1c9ef0e3 --- /dev/null +++ b/app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_event.dart @@ -0,0 +1,30 @@ +import 'package:equatable/equatable.dart'; + +sealed class SeConnecterEvent extends Equatable { + const SeConnecterEvent(); + + @override + List get props => []; +} + +final class SeConnecterAdresseMailAChange extends SeConnecterEvent { + const SeConnecterAdresseMailAChange(this.valeur); + + final String valeur; + + @override + List get props => [valeur]; +} + +final class SeConnecterMotDePasseAChange extends SeConnecterEvent { + const SeConnecterMotDePasseAChange(this.valeur); + + final String valeur; + + @override + List get props => [valeur]; +} + +final class SeConnecterConnexionDemandee extends SeConnecterEvent { + const SeConnecterConnexionDemandee(); +} diff --git a/app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_state.dart b/app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_state.dart new file mode 100644 index 00000000..aeb52b15 --- /dev/null +++ b/app/lib/src/fonctionnalites/se_connecter/bloc/se_connecter_state.dart @@ -0,0 +1,26 @@ +import 'package:equatable/equatable.dart'; + +final class SeConnecterState extends Equatable { + const SeConnecterState({ + this.adresseMail = '', + this.motDePasse = '', + }); + + final String adresseMail; + final String motDePasse; + + SeConnecterState copyWith({ + final String? adresseMail, + final String? motDePasse, + }) => + SeConnecterState( + adresseMail: adresseMail ?? this.adresseMail, + motDePasse: motDePasse ?? this.motDePasse, + ); + + @override + List get props => [ + adresseMail, + motDePasse, + ]; +} diff --git a/app/lib/src/l10n/l10n.dart b/app/lib/src/l10n/l10n.dart index 91d3548e..c77ea14a 100644 --- a/app/lib/src/l10n/l10n.dart +++ b/app/lib/src/l10n/l10n.dart @@ -13,5 +13,11 @@ class Localisation { static const String preOnboardingFinSousTitre = 'Les questions suivantes nous aideront à calculer une approximation de votre empreinte carbone et vous proposer des conseils personnalisés'; static const String suivant = 'Suivant'; - static const String seConnecter = "J'ai déjà un compte"; + static const String jaiDejaUnCompte = "J'ai déjà un compte"; + static const String seConnecterAvecSonCompte = 'Se connecter avec son compte'; + static const String seConnecter = 'Se connecter'; + static const String adresseElectronique = 'Adresse électronique'; + static const String motDePasse = 'Mot de passe'; + static const String motDePasseOublie = 'Mot de passe oublié ?'; + static const String bonjour = 'Bonjour,'; } diff --git a/app/lib/src/pages/accueil/accueil_page.dart b/app/lib/src/pages/accueil/accueil_page.dart new file mode 100644 index 00000000..6d515e7c --- /dev/null +++ b/app/lib/src/pages/accueil/accueil_page.dart @@ -0,0 +1,42 @@ +import 'package:app/src/fonctionnalites/authentification/domain/ports/authentification_repository.dart'; +import 'package:app/src/l10n/l10n.dart'; +import 'package:dsfr/dsfr.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; + +class AccueilPage extends StatelessWidget { + const AccueilPage({super.key}); + + static const name = 'accueil'; + static const path = '/$name'; + + static GoRoute route() => GoRoute( + name: name, + path: path, + builder: (final context, final state) => const AccueilPage(), + ); + + @override + Widget build(final BuildContext context) => Scaffold( + appBar: AppBar(), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.all(DsfrSpacings.s3w), + child: Column( + children: [ + const Text(Localisation.bonjour), + DsfrButton.lg( + label: 'Se déconnecter', + onTap: () async { + await context + .read() + .deconnectionDemandee(); + }, + ), + ], + ), + ), + ), + ); +} diff --git a/app/lib/src/pages/authentification/se_connecter_page.dart b/app/lib/src/pages/authentification/se_connecter_page.dart new file mode 100644 index 00000000..d1797d77 --- /dev/null +++ b/app/lib/src/pages/authentification/se_connecter_page.dart @@ -0,0 +1,76 @@ +import 'package:app/src/fonctionnalites/se_connecter/bloc/se_connecter_bloc.dart'; +import 'package:app/src/fonctionnalites/se_connecter/bloc/se_connecter_event.dart'; +import 'package:app/src/l10n/l10n.dart'; +import 'package:dsfr/dsfr.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; + +class SeConnecterPage extends StatelessWidget { + const SeConnecterPage({super.key}); + + static const name = 'authentification'; + static const path = '/$name'; + + static GoRoute route() => GoRoute( + name: name, + path: path, + builder: (final context, final state) => const SeConnecterPage(), + ); + + @override + Widget build(final BuildContext context) => Scaffold( + resizeToAvoidBottomInset: true, + appBar: AppBar(), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.only( + left: DsfrSpacings.s3w, + bottom: DsfrSpacings.s3w, + right: DsfrSpacings.s3w, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + Localisation.seConnecterAvecSonCompte, + style: DsfrFonts.headline1, + ), + const SizedBox(height: DsfrSpacings.s3w), + DsfrInput( + label: Localisation.adresseElectronique, + keyboardType: TextInputType.emailAddress, + onChanged: (final value) { + context + .read() + .add(SeConnecterAdresseMailAChange(value)); + }, + ), + const SizedBox(height: DsfrSpacings.s3v), + DsfrInput( + label: Localisation.motDePasse, + keyboardType: TextInputType.visiblePassword, + passwordMode: true, + onChanged: (final value) { + context + .read() + .add(SeConnecterMotDePasseAChange(value)); + }, + ), + const SizedBox(height: DsfrSpacings.s3v), + DsfrLink.md(label: Localisation.motDePasseOublie, onTap: () {}), + const Spacer(), + DsfrButton.lg( + label: Localisation.seConnecter, + onTap: () { + context + .read() + .add(const SeConnecterConnexionDemandee()); + }, + ), + ], + ), + ), + ), + ); +} diff --git a/app/lib/src/pages/pre_onboarding/pre_onboarding_carrousel_page.dart b/app/lib/src/pages/pre_onboarding/pre_onboarding_carrousel_page.dart index 5958b2db..720902cb 100644 --- a/app/lib/src/pages/pre_onboarding/pre_onboarding_carrousel_page.dart +++ b/app/lib/src/pages/pre_onboarding/pre_onboarding_carrousel_page.dart @@ -1,5 +1,6 @@ -import 'package:agir/src/assets/images.dart'; -import 'package:agir/src/l10n/l10n.dart'; +import 'package:app/src/assets/images.dart'; +import 'package:app/src/l10n/l10n.dart'; +import 'package:app/src/pages/authentification/se_connecter_page.dart'; import 'package:dsfr/dsfr.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; @@ -63,7 +64,7 @@ class _PreOnboardingCarrouselPageState extends State child: SafeArea( child: Padding( padding: const EdgeInsets.symmetric( - horizontal: DsfrSpacings.s2w, + horizontal: DsfrSpacings.s3w, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -92,8 +93,10 @@ class _PreOnboardingCarrouselPageState extends State const SizedBox(height: DsfrSpacings.s3w), Center( child: DsfrLink.md( - label: Localisation.seConnecter, - onTap: () {}, + label: Localisation.jaiDejaUnCompte, + onTap: () async { + await context.pushNamed(SeConnecterPage.name); + }, ), ), const SizedBox(height: DsfrSpacings.s7w), @@ -106,7 +109,7 @@ class _PreOnboardingCarrouselPageState extends State ), SafeArea( child: Padding( - padding: const EdgeInsets.only(bottom: DsfrSpacings.s2w), + padding: const EdgeInsets.only(bottom: DsfrSpacings.s3w), child: TabPageSelector( controller: _tabController, indicatorSize: 8, @@ -138,7 +141,7 @@ class Polaroid extends StatelessWidget { return SafeArea( child: Padding( - padding: const EdgeInsets.all(DsfrSpacings.s2w), + padding: const EdgeInsets.all(DsfrSpacings.s3w), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/app/lib/src/pages/pre_onboarding/pre_onboarding_page.dart b/app/lib/src/pages/pre_onboarding/pre_onboarding_page.dart index 139c5d52..135706e0 100644 --- a/app/lib/src/pages/pre_onboarding/pre_onboarding_page.dart +++ b/app/lib/src/pages/pre_onboarding/pre_onboarding_page.dart @@ -1,7 +1,7 @@ -import 'package:agir/src/assets/images.dart'; -import 'package:agir/src/assets/svgs.dart'; -import 'package:agir/src/l10n/l10n.dart'; -import 'package:agir/src/pages/pre_onboarding/pre_onboarding_carrousel_page.dart'; +import 'package:app/src/assets/images.dart'; +import 'package:app/src/assets/svgs.dart'; +import 'package:app/src/l10n/l10n.dart'; +import 'package:app/src/pages/pre_onboarding/pre_onboarding_carrousel_page.dart'; import 'package:dsfr/dsfr.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; @@ -23,7 +23,7 @@ class PreOnboardingPage extends StatelessWidget { Widget build(final BuildContext context) => Scaffold( body: SafeArea( child: Padding( - padding: const EdgeInsets.all(DsfrSpacings.s2w), + padding: const EdgeInsets.all(DsfrSpacings.s3w), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, @@ -33,7 +33,7 @@ class PreOnboardingPage extends StatelessWidget { Localisation.preOnboardingTitre, style: DsfrFonts.displayXs, ), - const SizedBox(height: DsfrSpacings.s2w), + const SizedBox(height: DsfrSpacings.s3w), Row( children: [ SvgPicture.asset( @@ -49,9 +49,8 @@ class PreOnboardingPage extends StatelessWidget { const Spacer(), DsfrButton.lg( label: Localisation.commencer, - onTap: () async { - await context.pushNamed(PreOnboardingCarrouselPage.name); - }, + onTap: () async => + context.pushNamed(PreOnboardingCarrouselPage.name), ), ], ), diff --git a/app/lib/src/router/app_router.dart b/app/lib/src/router/app_router.dart index 262bf501..73c879a9 100644 --- a/app/lib/src/router/app_router.dart +++ b/app/lib/src/router/app_router.dart @@ -1,11 +1,44 @@ -import 'package:agir/src/pages/pre_onboarding/pre_onboarding_carrousel_page.dart'; -import 'package:agir/src/pages/pre_onboarding/pre_onboarding_page.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut_manager.dart'; +import 'package:app/src/pages/accueil/accueil_page.dart'; +import 'package:app/src/pages/authentification/se_connecter_page.dart'; +import 'package:app/src/pages/pre_onboarding/pre_onboarding_carrousel_page.dart'; +import 'package:app/src/pages/pre_onboarding/pre_onboarding_page.dart'; +import 'package:app/src/router/go_router_refresh_stream.dart'; import 'package:go_router/go_router.dart'; -GoRouter goRouter() => GoRouter( +GoRouter goRouter({ + required final AuthentificationStatutManager authentificationStatusManager, +}) => + GoRouter( + debugLogDiagnostics: true, initialLocation: PreOnboardingPage.path, + redirect: (final context, final state) { + final path = state.uri.path; + + final statutActuel = authentificationStatusManager.statutActuel(); + switch (statutActuel) { + case AuthentificationStatut.inconnu: + return PreOnboardingPage.path; + case AuthentificationStatut.connecte: + if (path.startsWith(PreOnboardingPage.path) || + path.startsWith(PreOnboardingCarrouselPage.path) || + path.startsWith(SeConnecterPage.path)) { + return AccueilPage.path; + } + case AuthentificationStatut.pasConnecte: + if (path.startsWith(AccueilPage.path)) { + return PreOnboardingPage.path; + } + } + return null; + }, + refreshListenable: + GoRouterRefreshStream(authentificationStatusManager.statutModifie()), routes: [ PreOnboardingPage.route(), PreOnboardingCarrouselPage.route(), + SeConnecterPage.route(), + AccueilPage.route(), ], ); diff --git a/app/lib/src/router/go_router_refresh_stream.dart b/app/lib/src/router/go_router_refresh_stream.dart new file mode 100644 index 00000000..e10617f4 --- /dev/null +++ b/app/lib/src/router/go_router_refresh_stream.dart @@ -0,0 +1,18 @@ +import 'dart:async'; +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut.dart'; +import 'package:flutter/material.dart'; + +class GoRouterRefreshStream extends ChangeNotifier { + GoRouterRefreshStream(final Stream stream) { + notifyListeners(); + _subscription = stream.listen((final _) => notifyListeners()); + } + + late final StreamSubscription _subscription; + + @override + Future dispose() async { + await _subscription.cancel(); + super.dispose(); + } +} diff --git a/app/pubspec.lock b/app/pubspec.lock index cc288c85..797587e3 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -32,6 +32,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + bloc: + dependency: transitive + description: + name: bloc + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" + url: "https://pub.dev" + source: hosted + version: "8.1.4" boolean_selector: dependency: transitive description: @@ -95,6 +103,14 @@ packages: relative: true source: path version: "0.0.1" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: @@ -103,19 +119,27 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" - flutter_carousel_widget: + flutter_bloc: dependency: "direct main" description: - name: flutter_carousel_widget - sha256: "37b9e55e4cafffe358152b016db24153e756152aa07c4214cfe6ee902cd69a01" + name: flutter_bloc + sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "8.1.5" flutter_launcher_icons: dependency: "direct dev" description: @@ -132,6 +156,54 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.1" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" + url: "https://pub.dev" + source: hosted + version: "9.2.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_svg: dependency: "direct main" description: @@ -159,7 +231,7 @@ packages: source: hosted version: "14.1.3" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" @@ -182,6 +254,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.7" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" json_annotation: dependency: transitive description: @@ -247,13 +327,29 @@ packages: source: hosted version: "0.8.0" meta: - dependency: transitive + dependency: "direct main" description: name: meta sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted version: "1.12.0" + mocktail: + dependency: "direct dev" + description: + name: mocktail + sha256: c4b5007d91ca4f67256e720cb1b6d704e79a510183a12fa551021f652577dce6 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -270,6 +366,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + url: "https://pub.dev" + source: hosted + version: "2.2.4" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" + source: hosted + version: "2.4.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" petitparser: dependency: transitive description: @@ -278,6 +422,30 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.2" + platform: + dependency: transitive + description: + name: platform + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + provider: + dependency: transitive + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" sky_engine: dependency: transitive description: flutter @@ -387,6 +555,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" + win32: + dependency: transitive + description: + name: win32 + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + url: "https://pub.dev" + source: hosted + version: "5.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" xml: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index eeb4f610..2315e610 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -1,4 +1,4 @@ -name: agir +name: app description: "" publish_to: 'none' version: 0.1.0 @@ -10,19 +10,24 @@ environment: dependencies: dsfr: path: ../packages/dsfr.dart + equatable: ^2.0.5 flutter: sdk: flutter - flutter_carousel_widget: ^2.2.0 - flutter_markdown: ^0.7.1 - flutter_svg: ^2.0.10+1 - go_router: ^14.1.3 + flutter_bloc: 8.1.5 + flutter_markdown: 0.7.1 + flutter_secure_storage: 9.2.2 + flutter_svg: 2.0.10+1 + go_router: 14.1.3 + http: 1.2.1 + meta: 1.12.0 dev_dependencies: agir_lints: path: ../packages/agir_lints - flutter_launcher_icons: ^0.13.1 + flutter_launcher_icons: 0.13.1 flutter_test: sdk: flutter + mocktail: ^1.0.3 flutter: uses-material-design: true diff --git a/app/test/authentification_api_test.dart b/app/test/authentification_api_test.dart new file mode 100644 index 00000000..179c2d84 --- /dev/null +++ b/app/test/authentification_api_test.dart @@ -0,0 +1,5 @@ +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('', (final tester) async {}); +} diff --git a/app/test/authentification_repository_mock.dart b/app/test/authentification_repository_mock.dart new file mode 100644 index 00000000..c2d57bc3 --- /dev/null +++ b/app/test/authentification_repository_mock.dart @@ -0,0 +1,24 @@ +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut_manager.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/information_de_connexion.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/ports/authentification_repository.dart'; + +class AuthentificationRepositoryMock implements AuthentificationRepository { + AuthentificationRepositoryMock(this.authentificationStatusManager); + + final AuthentificationStatutManager authentificationStatusManager; + + @override + Future connectionDemandee( + final InformationDeConnexion informationDeConnexion, + ) async { + authentificationStatusManager + .gererAuthentificationStatut(AuthentificationStatut.connecte); + } + + @override + Future deconnectionDemandee() async { + authentificationStatusManager + .gererAuthentificationStatut(AuthentificationStatut.pasConnecte); + } +} diff --git a/app/test/main_test.dart b/app/test/main_test.dart index 1f6041c9..edb0bd72 100644 --- a/app/test/main_test.dart +++ b/app/test/main_test.dart @@ -1,14 +1,26 @@ -import 'package:agir/src/l10n/l10n.dart'; +import 'package:app/src/l10n/l10n.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'device_info.dart'; +import 'set_up_widgets.dart'; import 'steps/steps.dart'; void main() { + testWidgets("Iel n'est pas connecté", (final tester) async { + setUpWidgets(tester); + await ielLanceLapplication(tester); + await ielVoitLeTexte(tester, Localisation.preOnboardingTitre); + }); + + testWidgets('Iel est connecté', (final tester) async { + setUpWidgets(tester); + await ielEstConnecte(tester); + await ielLanceLapplication(tester); + await ielVoitLeTexte(tester, Localisation.bonjour); + }); + testWidgets("Iel lance l'application pour la première fois", (final tester) async { - DeviceInfo.setup(tester); - + setUpWidgets(tester); await ielLanceLapplication(tester); await ielVoitLeTexte(tester, Localisation.preOnboardingTitre); await ielAppuieSurCommencer(tester); @@ -20,4 +32,20 @@ void main() { await ielGlisseDeLaDroiteVersLaGauche(tester); await ielVoitLeTexte(tester, Localisation.preOnboardingFinTitre); }); + + testWidgets("Iel lance l'application pour la première fois et se connecte", + (final tester) async { + setUpWidgets(tester); + await ielLanceLapplication(tester); + await ielVoitLeTexte(tester, Localisation.preOnboardingTitre); + await ielAppuieSurCommencer(tester); + await ielVoitLeTexteMarkdown(tester, Localisation.preOnboarding1); + await ielGlisseDeLaDroiteVersLaGauche(tester); + await ielVoitLeTexteMarkdown(tester, Localisation.preOnboarding2); + await ielGlisseDeLaDroiteVersLaGauche(tester); + await ielVoitLeTexteMarkdown(tester, Localisation.preOnboarding3); + await ielGlisseDeLaDroiteVersLaGauche(tester); + await ielAppuieSur(tester, Localisation.jaiDejaUnCompte); + await ielVoitLeTexte(tester, Localisation.seConnecterAvecSonCompte); + }); } diff --git a/app/test/scenario_context.dart b/app/test/scenario_context.dart new file mode 100644 index 00000000..4be4adfe --- /dev/null +++ b/app/test/scenario_context.dart @@ -0,0 +1,12 @@ +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut.dart'; + +class ScenarioContext { + factory ScenarioContext() => _instance ??= ScenarioContext._(); + ScenarioContext._(); + static ScenarioContext? _instance; + + void dispose() => _instance = null; + + AuthentificationStatut authentificationStatut = + AuthentificationStatut.pasConnecte; +} diff --git a/app/test/set_up_widgets.dart b/app/test/set_up_widgets.dart new file mode 100644 index 00000000..8bb26ddc --- /dev/null +++ b/app/test/set_up_widgets.dart @@ -0,0 +1,9 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'device_info.dart'; +import 'scenario_context.dart'; + +void setUpWidgets(final WidgetTester tester) { + ScenarioContext().dispose(); + DeviceInfo.setup(tester); +} diff --git a/app/test/steps/iel_appuie_sur.dart b/app/test/steps/iel_appuie_sur.dart new file mode 100644 index 00000000..caa2e880 --- /dev/null +++ b/app/test/steps/iel_appuie_sur.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Iel appuie sur commencer +Future ielAppuieSur(final WidgetTester tester, final String text) async { + await tester.tap(find.text(text)); + await tester.pumpAndSettle(); +} diff --git a/app/test/steps/iel_appuie_sur_commencer.dart b/app/test/steps/iel_appuie_sur_commencer.dart index d6a14e91..6dc8d448 100644 --- a/app/test/steps/iel_appuie_sur_commencer.dart +++ b/app/test/steps/iel_appuie_sur_commencer.dart @@ -1,4 +1,4 @@ -import 'package:agir/src/l10n/l10n.dart'; +import 'package:app/src/l10n/l10n.dart'; import 'package:flutter_test/flutter_test.dart'; /// Iel appuie sur commencer diff --git a/app/test/steps/iel_est_connecte.dart b/app/test/steps/iel_est_connecte.dart new file mode 100644 index 00000000..54338189 --- /dev/null +++ b/app/test/steps/iel_est_connecte.dart @@ -0,0 +1,9 @@ +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../scenario_context.dart'; + +/// Iel est connecté +Future ielEstConnecte(final WidgetTester tester) async { + ScenarioContext().authentificationStatut = AuthentificationStatut.connecte; +} diff --git a/app/test/steps/iel_lance_lapplication.dart b/app/test/steps/iel_lance_lapplication.dart index d76e7afb..c22aa546 100644 --- a/app/test/steps/iel_lance_lapplication.dart +++ b/app/test/steps/iel_lance_lapplication.dart @@ -1,7 +1,22 @@ -import 'package:agir/src/app.dart'; +import 'package:app/src/app.dart'; +import 'package:app/src/fonctionnalites/authentification/domain/authentification_statut_manager.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import '../authentification_repository_mock.dart'; +import '../scenario_context.dart'; + /// Iel lance l'application Future ielLanceLapplication(final WidgetTester tester) async { - await tester.pumpWidget(const App()); + final authentificationStatusManager = AuthentificationStatutManager() + ..gererAuthentificationStatut(ScenarioContext().authentificationStatut); + final authentificationRepositoryMock = + AuthentificationRepositoryMock(authentificationStatusManager); + await tester.pumpFrames( + App( + authentificationRepository: authentificationRepositoryMock, + authentificationStatusManager: authentificationStatusManager, + ), + Durations.short1, + ); } diff --git a/app/test/steps/steps.dart b/app/test/steps/steps.dart index cd3ad935..3475e3fe 100644 --- a/app/test/steps/steps.dart +++ b/app/test/steps/steps.dart @@ -1,4 +1,6 @@ +export 'iel_appuie_sur.dart'; export 'iel_appuie_sur_commencer.dart'; +export 'iel_est_connecte.dart'; export 'iel_glisse_de_la_droite_vers_la_gauche.dart'; export 'iel_lance_lapplication.dart'; export 'iel_voit_le_texte.dart'; diff --git a/packages/agir_lints/lib/analysis_options.yaml b/packages/agir_lints/lib/analysis_options.yaml index 3f13cb8c..1b60473a 100644 --- a/packages/agir_lints/lib/analysis_options.yaml +++ b/packages/agir_lints/lib/analysis_options.yaml @@ -23,6 +23,7 @@ linter: avoid_final_parameters: false # incompatible with `prefer_final_parameters`. diagnostic_describe_all_properties: false lines_longer_than_80_chars: false + one_member_abstracts: false prefer_double_quotes: false # incompatible with `prefer_single_quotes`. prefer_relative_imports: false # incompatible with `always_use_package_imports`. public_member_api_docs: false diff --git a/packages/dsfr.dart/example/lib/buttons_page.dart b/packages/dsfr.dart/example/lib/buttons_page.dart index 6ad48279..0cbc3b5f 100755 --- a/packages/dsfr.dart/example/lib/buttons_page.dart +++ b/packages/dsfr.dart/example/lib/buttons_page.dart @@ -17,7 +17,7 @@ class ButtonsPage extends StatelessWidget { void onTap() {} return SingleChildScrollView( - padding: const EdgeInsets.all(16), + padding: const EdgeInsets.all(24), child: Column( children: [ const DsfrButton.lg(label: label), diff --git a/packages/dsfr.dart/example/lib/inputs_page.dart b/packages/dsfr.dart/example/lib/inputs_page.dart new file mode 100755 index 00000000..e2ae4183 --- /dev/null +++ b/packages/dsfr.dart/example/lib/inputs_page.dart @@ -0,0 +1,25 @@ +import 'package:dsfr/dsfr.dart'; +import 'package:dsfr_example/page_item.dart'; +import 'package:flutter/material.dart'; + +class InputsPage extends StatelessWidget { + const InputsPage({super.key}); + + static final model = PageItem( + title: 'Champ de saisie', + pageBuilder: (final context) => const InputsPage(), + ); + + @override + Widget build(final BuildContext context) => SingleChildScrollView( + padding: const EdgeInsets.all(24), + child: Column( + children: [ + DsfrInput( + label: 'Label', + onChanged: (final String value) {}, + ), + ], + ), + ); +} diff --git a/packages/dsfr.dart/example/lib/links_page.dart b/packages/dsfr.dart/example/lib/links_page.dart index 89c4b9ca..4a40a521 100755 --- a/packages/dsfr.dart/example/lib/links_page.dart +++ b/packages/dsfr.dart/example/lib/links_page.dart @@ -17,7 +17,7 @@ class LinksPage extends StatelessWidget { void onTap() {} return SingleChildScrollView( - padding: const EdgeInsets.all(16), + padding: const EdgeInsets.all(24), child: Column( children: [ const DsfrLink.md(label: label), diff --git a/packages/dsfr.dart/example/lib/main.dart b/packages/dsfr.dart/example/lib/main.dart index b12564c5..b6ab7656 100755 --- a/packages/dsfr.dart/example/lib/main.dart +++ b/packages/dsfr.dart/example/lib/main.dart @@ -2,6 +2,7 @@ import 'package:dsfr_example/buttons_page.dart'; import 'package:dsfr_example/colors_page.dart'; import 'package:dsfr_example/fonts_page.dart'; import 'package:dsfr_example/icons_page.dart'; +import 'package:dsfr_example/inputs_page.dart'; import 'package:dsfr_example/links_page.dart'; import 'package:dsfr_example/master_page.dart'; import 'package:flutter/material.dart'; @@ -22,6 +23,7 @@ class MyApp extends StatelessWidget { ColorsPage.model, FontsPage.model, IconsPage.model, + InputsPage.model, LinksPage.model, ], ), diff --git a/packages/dsfr.dart/lib/src/composants/composants.dart b/packages/dsfr.dart/lib/src/composants/composants.dart index 058330a6..3a8bb6dc 100755 --- a/packages/dsfr.dart/lib/src/composants/composants.dart +++ b/packages/dsfr.dart/lib/src/composants/composants.dart @@ -1,2 +1,3 @@ export 'buttons.dart'; +export 'input.dart'; export 'links.dart'; diff --git a/packages/dsfr.dart/lib/src/composants/input.dart b/packages/dsfr.dart/lib/src/composants/input.dart new file mode 100644 index 00000000..0430b930 --- /dev/null +++ b/packages/dsfr.dart/lib/src/composants/input.dart @@ -0,0 +1,113 @@ +import 'package:dsfr/dsfr.dart'; +import 'package:flutter/material.dart'; + +class DsfrInput extends StatefulWidget { + const DsfrInput({ + required this.label, + required this.onChanged, + this.labelStyle = DsfrFonts.bodyMd, + this.inputStyle = DsfrFonts.bodyMd, + this.passwordMode = false, + this.keyboardType, + this.inputBorderColor = DsfrColors.grey200, + this.inputBorderWidth = 2, + this.fillColor = DsfrColors.grey950, + this.radius = 4, + this.focusColor = DsfrColors.focus525, + this.focusThickness = 2, + this.focusPadding = const EdgeInsets.all(4), + super.key, + }); + + final String label; + final TextStyle labelStyle; + final TextStyle inputStyle; + final ValueChanged onChanged; + final bool passwordMode; + final TextInputType? keyboardType; + final Color inputBorderColor; + final double inputBorderWidth; + final Color fillColor; + final double radius; + final Color focusColor; + final double focusThickness; + final EdgeInsetsGeometry focusPadding; + + @override + State createState() => _DsfrInputState(); +} + +class _DsfrInputState extends State { + final _focusNode = FocusNode(); + bool isFocused = false; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + _focusNode.dispose(); + super.dispose(); + } + + @override + Widget build(final BuildContext context) { + final underlineInputBorder = UnderlineInputBorder( + borderSide: BorderSide( + color: widget.inputBorderColor, + width: widget.inputBorderWidth, + ), + borderRadius: BorderRadius.vertical(top: Radius.circular(widget.radius)), + ); + + Widget child = TextField( + style: widget.inputStyle, + focusNode: _focusNode, + decoration: InputDecoration( + filled: true, + fillColor: widget.fillColor, + enabledBorder: underlineInputBorder, + focusedBorder: underlineInputBorder, + border: underlineInputBorder, + ), + keyboardType: widget.keyboardType, + obscureText: widget.passwordMode, + enableSuggestions: !widget.passwordMode, + autocorrect: !widget.passwordMode, + onChanged: widget.onChanged, + ); + + if (isFocused) { + child = DecoratedBox( + decoration: BoxDecoration( + borderRadius: + BorderRadius.vertical(top: Radius.circular(widget.radius)), + border: Border.fromBorderSide( + BorderSide( + color: widget.focusColor, + width: widget.focusThickness, + ), + ), + ), + child: Padding( + padding: widget.focusPadding + .add(EdgeInsets.only(bottom: widget.inputBorderWidth)), + child: child, + ), + ); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(widget.label, style: widget.labelStyle), + SizedBox( + height: isFocused ? DsfrSpacings.s1v : DsfrSpacings.s1w, + ), + child, + ], + ); + } +} diff --git a/packages/dsfr.dart/lib/src/fondamentaux/colors.g.dart b/packages/dsfr.dart/lib/src/fondamentaux/colors.g.dart index ace26d3c..d01318d0 100644 --- a/packages/dsfr.dart/lib/src/fondamentaux/colors.g.dart +++ b/packages/dsfr.dart/lib/src/fondamentaux/colors.g.dart @@ -4,7 +4,7 @@ // // Do not edit directly -// Generated on Thu, 23 May 2024 09:31:22 GMT +// Generated on Tue, 28 May 2024 08:56:58 GMT @@ -230,39 +230,48 @@ class DsfrColors { static const grey100 = Color(0xFF242424); static const grey1000 = Color(0xFFFFFFFF); static const grey1000Active = Color(0xFFEDEDED); - static const grey1000Hover = Color(0xFFF5F5F5); - static const grey100Active = Color(0xFF5C5C5C); + static const grey1000Hover = Color(0xFFF6F6F6); + static const grey100Active = Color(0xFF5B5B5B); static const grey100Hover = Color(0xFF474747); - static const grey125 = Color(0xFF292929); + static const grey125 = Color(0xFF2A2A2A); static const grey125Active = Color(0xFF636363); - static const grey125Hover = Color(0xFF4F4F4F); - static const grey200 = Color(0xFF3B3B3B); - static const grey200Active = Color(0xFF787878); + static const grey125Hover = Color(0xFF4E4E4E); + static const grey150 = Color(0xFF2F2F2F); + static const grey150Active = Color(0xFF696969); + static const grey150Hover = Color(0xFF545454); + static const grey175 = Color(0xFF353535); + static const grey175Active = Color(0xFF717171); + static const grey175Hover = Color(0xFF5C5C5C); + static const grey200 = Color(0xFF3A3A3A); + static const grey200Active = Color(0xFF777777); static const grey200Hover = Color(0xFF616161); static const grey425 = Color(0xFF666666); static const grey425Active = Color(0xFFA6A6A6); static const grey425Hover = Color(0xFF919191); static const grey50 = Color(0xFF161616); static const grey50Active = Color(0xFF474747); - static const grey50Hover = Color(0xFF333333); + static const grey50Hover = Color(0xFF343434); static const grey625 = Color(0xFF929292); static const grey625Active = Color(0xFFCECECE); static const grey625Hover = Color(0xFFBBBBBB); - static const grey75 = Color(0xFF1F1F1F); - static const grey75Active = Color(0xFFCFCFCF); - static const grey75Hover = Color(0xFF404040); - static const grey850 = Color(0xFFCFCFCF); - static const grey850Active = Color(0xFF949494); + static const grey75 = Color(0xFF1E1E1E); + static const grey75Active = Color(0xFF525252); + static const grey75Hover = Color(0xFF3F3F3F); + static const grey850 = Color(0xFFCECECE); + static const grey850Active = Color(0xFF939393); static const grey850Hover = Color(0xFFA8A8A8); + static const grey900 = Color(0xFFDDDDDD); + static const grey900Active = Color(0xFFA7A7A7); + static const grey900Hover = Color(0xFFBBBBBB); static const grey925 = Color(0xFFE5E5E5); static const grey925Active = Color(0xFFB2B2B2); static const grey925Hover = Color(0xFFC5C5C5); - static const grey950 = Color(0xFFEDEDED); - static const grey950Active = Color(0xFFC2C2C2); - static const grey950Hover = Color(0xFFD1D1D1); - static const grey975 = Color(0xFFF5F5F5); + static const grey950 = Color(0xFFEEEEEE); + static const grey950Active = Color(0xFFC1C1C1); + static const grey950Hover = Color(0xFFD2D2D2); + static const grey975 = Color(0xFFF6F6F6); static const grey975Active = Color(0xFFCFCFCF); - static const grey975Hover = Color(0xFFDEDEDE); + static const grey975Hover = Color(0xFFDFDFDF); static const info100 = Color(0xFF1D2437); static const info100Active = Color(0xFF4C5B85); static const info100Hover = Color(0xFF3C4768); diff --git a/packages/dsfr.dart/lib/src/fondamentaux/spacing.g.dart b/packages/dsfr.dart/lib/src/fondamentaux/spacing.g.dart index 160b2258..8d49562e 100644 --- a/packages/dsfr.dart/lib/src/fondamentaux/spacing.g.dart +++ b/packages/dsfr.dart/lib/src/fondamentaux/spacing.g.dart @@ -4,7 +4,7 @@ // // Do not edit directly -// Generated on Thu, 23 May 2024 09:31:22 GMT +// Generated on Tue, 28 May 2024 08:56:58 GMT diff --git a/packages/dsfr.dart/style-dictionary/tokens/colors/grey.json b/packages/dsfr.dart/style-dictionary/tokens/colors/grey.json index 4ffd8390..d2835088 100755 --- a/packages/dsfr.dart/style-dictionary/tokens/colors/grey.json +++ b/packages/dsfr.dart/style-dictionary/tokens/colors/grey.json @@ -3,40 +3,49 @@ "grey": { "50" : {"value": "#161616"}, "50-active" : {"value": "#474747"}, - "50-hover" : {"value": "#333333"}, - "75" : {"value": "#1F1F1F"}, - "75-active" : {"value": "#CFCFCF"}, - "75-hover" : {"value": "#404040"}, + "50-hover" : {"value": "#343434"}, + "75" : {"value": "#1e1e1e"}, + "75-active" : {"value": "#525252"}, + "75-hover" : {"value": "#3f3f3f"}, "100" : {"value": "#242424"}, - "100-active" : {"value": "#5C5C5C"}, + "100-active" : {"value": "#5b5b5b"}, "100-hover" : {"value": "#474747"}, - "125" : {"value": "#292929"}, + "125" : {"value": "#2a2a2a"}, "125-active" : {"value": "#636363"}, - "125-hover" : {"value": "#4F4F4F"}, - "200" : {"value": "#3B3B3B"}, - "200-active" : {"value": "#787878"}, + "125-hover" : {"value": "#4e4e4e"}, + "150" : {"value": "#2f2f2f"}, + "150-active" : {"value": "#696969"}, + "150-hover" : {"value": "#545454"}, + "175" : {"value": "#353535"}, + "175-active" : {"value": "#717171"}, + "175-hover" : {"value": "#5c5c5c"}, + "200" : {"value": "#3a3a3a"}, + "200-active" : {"value": "#777777"}, "200-hover" : {"value": "#616161"}, "425" : {"value": "#666666"}, - "425-active" : {"value": "#A6A6A6"}, + "425-active" : {"value": "#a6a6a6"}, "425-hover" : {"value": "#919191"}, "625" : {"value": "#929292"}, - "625-active" : {"value": "#CECECE"}, - "625-hover" : {"value": "#BBBBBB"}, - "850" : {"value": "#CFCFCF"}, - "850-active" : {"value": "#949494"}, - "850-hover" : {"value": "#A8A8A8"}, - "925" : {"value": "#E5E5E5"}, - "925-active" : {"value": "#B2B2B2"}, - "925-hover" : {"value": "#C5C5C5"}, - "950" : {"value": "#EDEDED"}, - "950-active" : {"value": "#C2C2C2"}, - "950-hover" : {"value": "#D1D1D1"}, - "975" : {"value": "#F5F5F5"}, - "975-active" : {"value": "#CFCFCF"}, - "975-hover" : {"value": "#DEDEDE"}, - "1000" : {"value": "#FFFFFF"}, - "1000-active": {"value": "#EDEDED"}, - "1000-hover" : {"value": "#F5F5F5"} + "625-active" : {"value": "#cecece"}, + "625-hover" : {"value": "#bbbbbb"}, + "850" : {"value": "#cecece"}, + "850-active" : {"value": "#939393"}, + "850-hover" : {"value": "#a8a8a8"}, + "900" : {"value": "#dddddd"}, + "900-active" : {"value": "#a7a7a7"}, + "900-hover" : {"value": "#bbbbbb"}, + "925" : {"value": "#e5e5e5"}, + "925-active" : {"value": "#b2b2b2"}, + "925-hover" : {"value": "#c5c5c5"}, + "950" : {"value": "#eeeeee"}, + "950-active" : {"value": "#c1c1c1"}, + "950-hover" : {"value": "#d2d2d2"}, + "975" : {"value": "#f6f6f6"}, + "975-active" : {"value": "#cfcfcf"}, + "975-hover" : {"value": "#dfdfdf"}, + "1000" : {"value": "#ffffff"}, + "1000-active": {"value": "#ededed"}, + "1000-hover" : {"value": "#f6f6f6"} } } } diff --git a/packages/dsfr.dart/test/input_test.dart b/packages/dsfr.dart/test/input_test.dart new file mode 100644 index 00000000..195a673e --- /dev/null +++ b/packages/dsfr.dart/test/input_test.dart @@ -0,0 +1,44 @@ +import 'package:dsfr/dsfr.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'helpers.dart'; + +void main() { + group( + 'Champ de saisie', + () { + testWidgets('Voir le label', (final tester) async { + const label = 'Label'; + await tester.pumpWidget( + app( + DsfrInput( + label: label, + onChanged: (final String value) {}, + ), + ), + ); + expect(find.text(label), findsOneWidget); + }); + + testWidgets('Je saisie', (final tester) async { + const label = 'Label'; + const text = 'a'; + await tester.pumpWidget( + app( + DsfrInput( + label: label, + onChanged: (final String value) {}, + ), + ), + ); + await tester.enterText(find.byType(DsfrInput), text); + + await tester.testTextInput.receiveAction(TextInputAction.done); + + await tester.pump(); + + expect(find.text(text), findsOneWidget); + }); + }, + ); +}