From 72ddb58a11bcfcbc212fe4bcdec1c17ae157341d Mon Sep 17 00:00:00 2001 From: Dayron Date: Tue, 14 Oct 2025 12:10:42 +0200 Subject: [PATCH] Refactor user and theme management to use BLoC pattern; remove provider classes and integrate new services for user and group functionalities --- .VSCodeCounter/2025-10-14_11-10-48/details.md | 82 +++ .../2025-10-14_11-10-48/diff-details.md | 15 + .VSCodeCounter/2025-10-14_11-10-48/diff.csv | 2 + .VSCodeCounter/2025-10-14_11-10-48/diff.md | 19 + .VSCodeCounter/2025-10-14_11-10-48/diff.txt | 22 + .../2025-10-14_11-10-48/results.csv | 69 ++ .../2025-10-14_11-10-48/results.json | 1 + .VSCodeCounter/2025-10-14_11-10-48/results.md | 81 +++ .../2025-10-14_11-10-48/results.txt | 151 +++++ lib/blocs/group/group_bloc.dart | 120 ++++ lib/blocs/group/group_event.dart | 71 ++ lib/blocs/group/group_state.dart | 40 ++ lib/blocs/trip/trip_bloc.dart | 10 +- lib/blocs/user/user_bloc.dart | 126 ++++ lib/blocs/user/user_event.dart | 22 + lib/blocs/user/user_state.dart | 61 ++ lib/components/group/group_content.dart | 177 +++-- lib/components/home/create_trip_content.dart | 610 ++++++++---------- lib/components/home/home_content.dart | 237 ++++--- lib/components/profile/profile_content.dart | 148 ++--- .../settings/settings_theme_content.dart | 44 +- lib/main.dart | 2 + lib/pages/home.dart | 72 +-- lib/providers/theme_provider.dart | 50 -- lib/providers/user_provider.dart | 120 ---- lib/services/group_service.dart | 12 +- lib/services/user_service.dart | 291 +++++++++ 27 files changed, 1864 insertions(+), 791 deletions(-) create mode 100644 .VSCodeCounter/2025-10-14_11-10-48/details.md create mode 100644 .VSCodeCounter/2025-10-14_11-10-48/diff-details.md create mode 100644 .VSCodeCounter/2025-10-14_11-10-48/diff.csv create mode 100644 .VSCodeCounter/2025-10-14_11-10-48/diff.md create mode 100644 .VSCodeCounter/2025-10-14_11-10-48/diff.txt create mode 100644 .VSCodeCounter/2025-10-14_11-10-48/results.csv create mode 100644 .VSCodeCounter/2025-10-14_11-10-48/results.json create mode 100644 .VSCodeCounter/2025-10-14_11-10-48/results.md create mode 100644 .VSCodeCounter/2025-10-14_11-10-48/results.txt create mode 100644 lib/blocs/group/group_bloc.dart create mode 100644 lib/blocs/group/group_event.dart create mode 100644 lib/blocs/group/group_state.dart delete mode 100644 lib/providers/theme_provider.dart delete mode 100644 lib/providers/user_provider.dart create mode 100644 lib/services/user_service.dart diff --git a/.VSCodeCounter/2025-10-14_11-10-48/details.md b/.VSCodeCounter/2025-10-14_11-10-48/details.md new file mode 100644 index 0000000..e4d97c9 --- /dev/null +++ b/.VSCodeCounter/2025-10-14_11-10-48/details.md @@ -0,0 +1,82 @@ +# Details + +Date : 2025-10-14 11:10:48 + +Directory c:\\Users\\dayro\\Documents\\coding\\travel_mate + +Total : 67 files, 5159 codes, 328 comments, 739 blanks, all 6226 lines + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | +| [README.md](/README.md) | Markdown | 45 | 0 | 16 | 61 | +| [analysis\_options.yaml](/analysis_options.yaml) | YAML | 3 | 22 | 4 | 29 | +| [android/app/google-services.json](/android/app/google-services.json) | JSON | 54 | 0 | 0 | 54 | +| [android/app/src/debug/AndroidManifest.xml](/android/app/src/debug/AndroidManifest.xml) | XML | 3 | 4 | 1 | 8 | +| [android/app/src/main/AndroidManifest.xml](/android/app/src/main/AndroidManifest.xml) | XML | 40 | 11 | 1 | 52 | +| [android/app/src/main/res/drawable-v21/launch\_background.xml](/android/app/src/main/res/drawable-v21/launch_background.xml) | XML | 4 | 7 | 2 | 13 | +| [android/app/src/main/res/drawable/launch\_background.xml](/android/app/src/main/res/drawable/launch_background.xml) | XML | 4 | 7 | 2 | 13 | +| [android/app/src/main/res/values-night/styles.xml](/android/app/src/main/res/values-night/styles.xml) | XML | 9 | 9 | 1 | 19 | +| [android/app/src/main/res/values/styles.xml](/android/app/src/main/res/values/styles.xml) | XML | 9 | 9 | 1 | 19 | +| [android/app/src/profile/AndroidManifest.xml](/android/app/src/profile/AndroidManifest.xml) | XML | 3 | 4 | 1 | 8 | +| [android/build/reports/problems/problems-report.html](/android/build/reports/problems/problems-report.html) | HTML | 548 | 2 | 114 | 664 | +| [android/gradle.properties](/android/gradle.properties) | Properties | 3 | 0 | 1 | 4 | +| [android/gradle/wrapper/gradle-wrapper.properties](/android/gradle/wrapper/gradle-wrapper.properties) | Properties | 5 | 0 | 1 | 6 | +| [firebase.json](/firebase.json) | JSON | 1 | 0 | 0 | 1 | +| [ios/Podfile](/ios/Podfile) | Ruby | 32 | 2 | 10 | 44 | +| [ios/RunnerTests/RunnerTests.swift](/ios/RunnerTests/RunnerTests.swift) | Swift | 7 | 2 | 4 | 13 | +| [ios/Runner/AppDelegate.swift](/ios/Runner/AppDelegate.swift) | Swift | 14 | 0 | 2 | 16 | +| [ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json](/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json) | JSON | 122 | 0 | 1 | 123 | +| [ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json](/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json) | JSON | 23 | 0 | 1 | 24 | +| [ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md](/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md) | Markdown | 3 | 0 | 2 | 5 | +| [ios/Runner/Base.lproj/LaunchScreen.storyboard](/ios/Runner/Base.lproj/LaunchScreen.storyboard) | XML | 36 | 1 | 1 | 38 | +| [ios/Runner/Base.lproj/Main.storyboard](/ios/Runner/Base.lproj/Main.storyboard) | XML | 25 | 1 | 1 | 27 | +| [ios/Runner/Runner-Bridging-Header.h](/ios/Runner/Runner-Bridging-Header.h) | C++ | 1 | 0 | 1 | 2 | +| [lib/blocs/auth/auth\_bloc.dart](/lib/blocs/auth/auth_bloc.dart) | Dart | 129 | 1 | 20 | 150 | +| [lib/blocs/auth/auth\_event.dart](/lib/blocs/auth/auth_event.dart) | Dart | 40 | 0 | 15 | 55 | +| [lib/blocs/auth/auth\_state.dart](/lib/blocs/auth/auth_state.dart) | Dart | 28 | 0 | 14 | 42 | +| [lib/blocs/theme/theme\_bloc.dart](/lib/blocs/theme/theme_bloc.dart) | Dart | 40 | 1 | 5 | 46 | +| [lib/blocs/theme/theme\_event.dart](/lib/blocs/theme/theme_event.dart) | Dart | 14 | 0 | 6 | 20 | +| [lib/blocs/theme/theme\_state.dart](/lib/blocs/theme/theme_state.dart) | Dart | 16 | 0 | 5 | 21 | +| [lib/blocs/trip/trip\_bloc.dart](/lib/blocs/trip/trip_bloc.dart) | Dart | 103 | 1 | 14 | 118 | +| [lib/blocs/trip/trip\_event.dart](/lib/blocs/trip/trip_event.dart) | Dart | 51 | 0 | 20 | 71 | +| [lib/blocs/trip/trip\_state.dart](/lib/blocs/trip/trip_state.dart) | Dart | 27 | 0 | 13 | 40 | +| [lib/blocs/user/user\_bloc.dart](/lib/blocs/user/user_bloc.dart) | Dart | 0 | 0 | 1 | 1 | +| [lib/blocs/user/user\_event.dart](/lib/blocs/user/user_event.dart) | Dart | 0 | 0 | 1 | 1 | +| [lib/blocs/user/user\_state.dart](/lib/blocs/user/user_state.dart) | Dart | 0 | 0 | 1 | 1 | +| [lib/components/count/count\_content.dart](/lib/components/count/count_content.dart) | Dart | 25 | 0 | 3 | 28 | +| [lib/components/group/group\_content.dart](/lib/components/group/group_content.dart) | Dart | 115 | 2 | 12 | 129 | +| [lib/components/home/create\_trip\_content.dart](/lib/components/home/create_trip_content.dart) | Dart | 481 | 21 | 63 | 565 | +| [lib/components/home/home\_content.dart](/lib/components/home/home_content.dart) | Dart | 366 | 12 | 30 | 408 | +| [lib/components/home/show\_trip\_details\_content.dart](/lib/components/home/show_trip_details_content.dart) | Dart | 106 | 2 | 4 | 112 | +| [lib/components/map/map\_content.dart](/lib/components/map/map_content.dart) | Dart | 100 | 4 | 11 | 115 | +| [lib/components/profile/profile\_content.dart](/lib/components/profile/profile_content.dart) | Dart | 328 | 6 | 29 | 363 | +| [lib/components/settings/settings\_content.dart](/lib/components/settings/settings_content.dart) | Dart | 60 | 2 | 10 | 72 | +| [lib/components/settings/settings\_theme\_content.dart](/lib/components/settings/settings_theme_content.dart) | Dart | 123 | 4 | 10 | 137 | +| [lib/data/data\_sources/firestore\_data\_source.dart](/lib/data/data_sources/firestore_data_source.dart) | Dart | 0 | 0 | 1 | 1 | +| [lib/data/data\_sources/local\_data\_source.dart](/lib/data/data_sources/local_data_source.dart) | Dart | 0 | 0 | 1 | 1 | +| [lib/data/models/group.dart](/lib/data/models/group.dart) | Dart | 23 | 0 | 3 | 26 | +| [lib/data/models/message.dart](/lib/data/models/message.dart) | Dart | 14 | 0 | 1 | 15 | +| [lib/data/models/trip.dart](/lib/data/models/trip.dart) | Dart | 176 | 16 | 24 | 216 | +| [lib/data/models/user.dart](/lib/data/models/user.dart) | Dart | 61 | 6 | 13 | 80 | +| [lib/firebase\_options.dart](/lib/firebase_options.dart) | Dart | 60 | 12 | 5 | 77 | +| [lib/main.dart](/lib/main.dart) | Dart | 89 | 0 | 5 | 94 | +| [lib/pages/home.dart](/lib/pages/home.dart) | Dart | 147 | 6 | 13 | 166 | +| [lib/pages/login.dart](/lib/pages/login.dart) | Dart | 285 | 9 | 28 | 322 | +| [lib/pages/resetpswd.dart](/lib/pages/resetpswd.dart) | Dart | 92 | 2 | 13 | 107 | +| [lib/pages/signup.dart](/lib/pages/signup.dart) | Dart | 256 | 7 | 20 | 283 | +| [lib/providers/theme\_provider.dart](/lib/providers/theme_provider.dart) | Dart | 41 | 1 | 9 | 51 | +| [lib/providers/user\_provider.dart](/lib/providers/user_provider.dart) | Dart | 94 | 9 | 18 | 121 | +| [lib/repositories/auth\_repository.dart](/lib/repositories/auth_repository.dart) | Dart | 115 | 11 | 22 | 148 | +| [lib/repositories/trip\_repository.dart](/lib/repositories/trip_repository.dart) | Dart | 92 | 9 | 12 | 113 | +| [lib/repositories/user\_repository.dart](/lib/repositories/user_repository.dart) | Dart | 77 | 7 | 11 | 95 | +| [lib/services/auth\_service.dart](/lib/services/auth_service.dart) | Dart | 84 | 6 | 18 | 108 | +| [lib/services/group\_service.dart](/lib/services/group_service.dart) | Dart | 53 | 1 | 6 | 60 | +| [lib/services/message\_service.dart](/lib/services/message_service.dart) | Dart | 0 | 0 | 1 | 1 | +| [lib/services/trip\_service.dart](/lib/services/trip_service.dart) | Dart | 209 | 21 | 39 | 269 | +| [pubspec.yaml](/pubspec.yaml) | YAML | 31 | 58 | 14 | 103 | +| [test/widget\_test.dart](/test/widget_test.dart) | Dart | 14 | 10 | 7 | 31 | + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2025-10-14_11-10-48/diff-details.md b/.VSCodeCounter/2025-10-14_11-10-48/diff-details.md new file mode 100644 index 0000000..4912215 --- /dev/null +++ b/.VSCodeCounter/2025-10-14_11-10-48/diff-details.md @@ -0,0 +1,15 @@ +# Diff Details + +Date : 2025-10-14 11:10:48 + +Directory c:\\Users\\dayro\\Documents\\coding\\travel_mate + +Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2025-10-14_11-10-48/diff.csv b/.VSCodeCounter/2025-10-14_11-10-48/diff.csv new file mode 100644 index 0000000..b7d8d75 --- /dev/null +++ b/.VSCodeCounter/2025-10-14_11-10-48/diff.csv @@ -0,0 +1,2 @@ +"filename", "language", "", "comment", "blank", "total" +"Total", "-", , 0, 0, 0 \ No newline at end of file diff --git a/.VSCodeCounter/2025-10-14_11-10-48/diff.md b/.VSCodeCounter/2025-10-14_11-10-48/diff.md new file mode 100644 index 0000000..691dbee --- /dev/null +++ b/.VSCodeCounter/2025-10-14_11-10-48/diff.md @@ -0,0 +1,19 @@ +# Diff Summary + +Date : 2025-10-14 11:10:48 + +Directory c:\\Users\\dayro\\Documents\\coding\\travel_mate + +Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2025-10-14_11-10-48/diff.txt b/.VSCodeCounter/2025-10-14_11-10-48/diff.txt new file mode 100644 index 0000000..8746a77 --- /dev/null +++ b/.VSCodeCounter/2025-10-14_11-10-48/diff.txt @@ -0,0 +1,22 @@ +Date : 2025-10-14 11:10:48 +Directory : c:\Users\dayro\Documents\coding\travel_mate +Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines + +Languages ++----------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++----------+------------+------------+------------+------------+------------+ ++----------+------------+------------+------------+------------+------------+ + +Directories ++------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++------+------------+------------+------------+------------+------------+ ++------+------------+------------+------------+------------+------------+ + +Files ++----------+----------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++----------+----------+------------+------------+------------+------------+ +| Total | | 0 | 0 | 0 | 0 | ++----------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2025-10-14_11-10-48/results.csv b/.VSCodeCounter/2025-10-14_11-10-48/results.csv new file mode 100644 index 0000000..433590d --- /dev/null +++ b/.VSCodeCounter/2025-10-14_11-10-48/results.csv @@ -0,0 +1,69 @@ +"filename", "language", "YAML", "Markdown", "Dart", "JSON", "Swift", "C++", "XML", "Ruby", "Properties", "HTML", "comment", "blank", "total" +"c:\Users\dayro\Documents\coding\travel_mate\README.md", "Markdown", 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 61 +"c:\Users\dayro\Documents\coding\travel_mate\analysis_options.yaml", "YAML", 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 4, 29 +"c:\Users\dayro\Documents\coding\travel_mate\android\app\google-services.json", "JSON", 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 54 +"c:\Users\dayro\Documents\coding\travel_mate\android\app\src\debug\AndroidManifest.xml", "XML", 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 1, 8 +"c:\Users\dayro\Documents\coding\travel_mate\android\app\src\main\AndroidManifest.xml", "XML", 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 11, 1, 52 +"c:\Users\dayro\Documents\coding\travel_mate\android\app\src\main\res\drawable-v21\launch_background.xml", "XML", 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 7, 2, 13 +"c:\Users\dayro\Documents\coding\travel_mate\android\app\src\main\res\drawable\launch_background.xml", "XML", 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 7, 2, 13 +"c:\Users\dayro\Documents\coding\travel_mate\android\app\src\main\res\values-night\styles.xml", "XML", 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 9, 1, 19 +"c:\Users\dayro\Documents\coding\travel_mate\android\app\src\main\res\values\styles.xml", "XML", 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 9, 1, 19 +"c:\Users\dayro\Documents\coding\travel_mate\android\app\src\profile\AndroidManifest.xml", "XML", 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 1, 8 +"c:\Users\dayro\Documents\coding\travel_mate\android\build\reports\problems\problems-report.html", "HTML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 548, 2, 114, 664 +"c:\Users\dayro\Documents\coding\travel_mate\android\gradle.properties", "Properties", 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 1, 4 +"c:\Users\dayro\Documents\coding\travel_mate\android\gradle\wrapper\gradle-wrapper.properties", "Properties", 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 1, 6 +"c:\Users\dayro\Documents\coding\travel_mate\firebase.json", "JSON", 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 +"c:\Users\dayro\Documents\coding\travel_mate\ios\Podfile", "Ruby", 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 2, 10, 44 +"c:\Users\dayro\Documents\coding\travel_mate\ios\RunnerTests\RunnerTests.swift", "Swift", 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 2, 4, 13 +"c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\AppDelegate.swift", "Swift", 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 2, 16 +"c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Assets.xcassets\AppIcon.appiconset\Contents.json", "JSON", 0, 0, 0, 122, 0, 0, 0, 0, 0, 0, 0, 1, 123 +"c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Assets.xcassets\LaunchImage.imageset\Contents.json", "JSON", 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 1, 24 +"c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Assets.xcassets\LaunchImage.imageset\README.md", "Markdown", 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5 +"c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Base.lproj\LaunchScreen.storyboard", "XML", 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 1, 1, 38 +"c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Base.lproj\Main.storyboard", "XML", 0, 0, 0, 0, 0, 0, 25, 0, 0, 0, 1, 1, 27 +"c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Runner-Bridging-Header.h", "C++", 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 2 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\auth\auth_bloc.dart", "Dart", 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, 1, 20, 150 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\auth\auth_event.dart", "Dart", 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 15, 55 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\auth\auth_state.dart", "Dart", 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 14, 42 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\theme\theme_bloc.dart", "Dart", 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 1, 5, 46 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\theme\theme_event.dart", "Dart", 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 6, 20 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\theme\theme_state.dart", "Dart", 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 5, 21 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\trip\trip_bloc.dart", "Dart", 0, 0, 103, 0, 0, 0, 0, 0, 0, 0, 1, 14, 118 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\trip\trip_event.dart", "Dart", 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 20, 71 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\trip\trip_state.dart", "Dart", 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 13, 40 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\user\user_bloc.dart", "Dart", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\user\user_event.dart", "Dart", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 +"c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\user\user_state.dart", "Dart", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 +"c:\Users\dayro\Documents\coding\travel_mate\lib\components\count\count_content.dart", "Dart", 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 3, 28 +"c:\Users\dayro\Documents\coding\travel_mate\lib\components\group\group_content.dart", "Dart", 0, 0, 115, 0, 0, 0, 0, 0, 0, 0, 2, 12, 129 +"c:\Users\dayro\Documents\coding\travel_mate\lib\components\home\create_trip_content.dart", "Dart", 0, 0, 481, 0, 0, 0, 0, 0, 0, 0, 21, 63, 565 +"c:\Users\dayro\Documents\coding\travel_mate\lib\components\home\home_content.dart", "Dart", 0, 0, 366, 0, 0, 0, 0, 0, 0, 0, 12, 30, 408 +"c:\Users\dayro\Documents\coding\travel_mate\lib\components\home\show_trip_details_content.dart", "Dart", 0, 0, 106, 0, 0, 0, 0, 0, 0, 0, 2, 4, 112 +"c:\Users\dayro\Documents\coding\travel_mate\lib\components\map\map_content.dart", "Dart", 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 4, 11, 115 +"c:\Users\dayro\Documents\coding\travel_mate\lib\components\profile\profile_content.dart", "Dart", 0, 0, 328, 0, 0, 0, 0, 0, 0, 0, 6, 29, 363 +"c:\Users\dayro\Documents\coding\travel_mate\lib\components\settings\settings_content.dart", "Dart", 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 2, 10, 72 +"c:\Users\dayro\Documents\coding\travel_mate\lib\components\settings\settings_theme_content.dart", "Dart", 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 4, 10, 137 +"c:\Users\dayro\Documents\coding\travel_mate\lib\data\data_sources\firestore_data_source.dart", "Dart", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 +"c:\Users\dayro\Documents\coding\travel_mate\lib\data\data_sources\local_data_source.dart", "Dart", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 +"c:\Users\dayro\Documents\coding\travel_mate\lib\data\models\group.dart", "Dart", 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 3, 26 +"c:\Users\dayro\Documents\coding\travel_mate\lib\data\models\message.dart", "Dart", 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 1, 15 +"c:\Users\dayro\Documents\coding\travel_mate\lib\data\models\trip.dart", "Dart", 0, 0, 176, 0, 0, 0, 0, 0, 0, 0, 16, 24, 216 +"c:\Users\dayro\Documents\coding\travel_mate\lib\data\models\user.dart", "Dart", 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 6, 13, 80 +"c:\Users\dayro\Documents\coding\travel_mate\lib\firebase_options.dart", "Dart", 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 12, 5, 77 +"c:\Users\dayro\Documents\coding\travel_mate\lib\main.dart", "Dart", 0, 0, 89, 0, 0, 0, 0, 0, 0, 0, 0, 5, 94 +"c:\Users\dayro\Documents\coding\travel_mate\lib\pages\home.dart", "Dart", 0, 0, 147, 0, 0, 0, 0, 0, 0, 0, 6, 13, 166 +"c:\Users\dayro\Documents\coding\travel_mate\lib\pages\login.dart", "Dart", 0, 0, 285, 0, 0, 0, 0, 0, 0, 0, 9, 28, 322 +"c:\Users\dayro\Documents\coding\travel_mate\lib\pages\resetpswd.dart", "Dart", 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 2, 13, 107 +"c:\Users\dayro\Documents\coding\travel_mate\lib\pages\signup.dart", "Dart", 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 7, 20, 283 +"c:\Users\dayro\Documents\coding\travel_mate\lib\providers\theme_provider.dart", "Dart", 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 1, 9, 51 +"c:\Users\dayro\Documents\coding\travel_mate\lib\providers\user_provider.dart", "Dart", 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, 9, 18, 121 +"c:\Users\dayro\Documents\coding\travel_mate\lib\repositories\auth_repository.dart", "Dart", 0, 0, 115, 0, 0, 0, 0, 0, 0, 0, 11, 22, 148 +"c:\Users\dayro\Documents\coding\travel_mate\lib\repositories\trip_repository.dart", "Dart", 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 9, 12, 113 +"c:\Users\dayro\Documents\coding\travel_mate\lib\repositories\user_repository.dart", "Dart", 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 7, 11, 95 +"c:\Users\dayro\Documents\coding\travel_mate\lib\services\auth_service.dart", "Dart", 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 6, 18, 108 +"c:\Users\dayro\Documents\coding\travel_mate\lib\services\group_service.dart", "Dart", 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 1, 6, 60 +"c:\Users\dayro\Documents\coding\travel_mate\lib\services\message_service.dart", "Dart", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 +"c:\Users\dayro\Documents\coding\travel_mate\lib\services\trip_service.dart", "Dart", 0, 0, 209, 0, 0, 0, 0, 0, 0, 0, 21, 39, 269 +"c:\Users\dayro\Documents\coding\travel_mate\pubspec.yaml", "YAML", 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 14, 103 +"c:\Users\dayro\Documents\coding\travel_mate\test\widget_test.dart", "Dart", 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 10, 7, 31 +"Total", "-", 34, 48, 4134, 200, 21, 1, 133, 32, 8, 548, 328, 739, 6226 \ No newline at end of file diff --git a/.VSCodeCounter/2025-10-14_11-10-48/results.json b/.VSCodeCounter/2025-10-14_11-10-48/results.json new file mode 100644 index 0000000..ce42207 --- /dev/null +++ b/.VSCodeCounter/2025-10-14_11-10-48/results.json @@ -0,0 +1 @@ +{"file:///c%3A/Users/dayro/Documents/coding/travel_mate/pubspec.yaml":{"language":"YAML","code":31,"comment":58,"blank":14},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/README.md":{"language":"Markdown","code":45,"comment":0,"blank":16},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/data/models/trip.dart":{"language":"Dart","code":176,"comment":16,"blank":24},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/test/widget_test.dart":{"language":"Dart","code":14,"comment":10,"blank":7},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/data/models/group.dart":{"language":"Dart","code":23,"comment":0,"blank":3},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/data/models/message.dart":{"language":"Dart","code":14,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/data/models/user.dart":{"language":"Dart","code":61,"comment":6,"blank":13},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/services/message_service.dart":{"language":"Dart","code":0,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/data/data_sources/local_data_source.dart":{"language":"Dart","code":0,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/repositories/auth_repository.dart":{"language":"Dart","code":115,"comment":11,"blank":22},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/services/trip_service.dart":{"language":"Dart","code":209,"comment":21,"blank":39},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/data/data_sources/firestore_data_source.dart":{"language":"Dart","code":0,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/services/auth_service.dart":{"language":"Dart","code":84,"comment":6,"blank":18},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/repositories/trip_repository.dart":{"language":"Dart","code":92,"comment":9,"blank":12},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/firebase_options.dart":{"language":"Dart","code":60,"comment":12,"blank":5},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/pages/home.dart":{"language":"Dart","code":147,"comment":6,"blank":13},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/repositories/user_repository.dart":{"language":"Dart","code":77,"comment":7,"blank":11},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/providers/theme_provider.dart":{"language":"Dart","code":41,"comment":1,"blank":9},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/main.dart":{"language":"Dart","code":89,"comment":0,"blank":5},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/pages/resetpswd.dart":{"language":"Dart","code":92,"comment":2,"blank":13},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/pages/login.dart":{"language":"Dart","code":285,"comment":9,"blank":28},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/firebase.json":{"language":"JSON","code":1,"comment":0,"blank":0},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/components/profile/profile_content.dart":{"language":"Dart","code":328,"comment":6,"blank":29},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/components/settings/settings_content.dart":{"language":"Dart","code":60,"comment":2,"blank":10},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/components/settings/settings_theme_content.dart":{"language":"Dart","code":123,"comment":4,"blank":10},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/ios/RunnerTests/RunnerTests.swift":{"language":"Swift","code":7,"comment":2,"blank":4},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/ios/Runner/AppDelegate.swift":{"language":"Swift","code":14,"comment":0,"blank":2},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/ios/Runner/Runner-Bridging-Header.h":{"language":"C++","code":1,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/ios/Runner/Base.lproj/LaunchScreen.storyboard":{"language":"XML","code":36,"comment":1,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json":{"language":"JSON","code":23,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json":{"language":"JSON","code":122,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md":{"language":"Markdown","code":3,"comment":0,"blank":2},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/ios/Runner/Base.lproj/Main.storyboard":{"language":"XML","code":25,"comment":1,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/ios/Podfile":{"language":"Ruby","code":32,"comment":2,"blank":10},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/user/user_state.dart":{"language":"Dart","code":0,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/user/user_event.dart":{"language":"Dart","code":0,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/user/user_bloc.dart":{"language":"Dart","code":0,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/services/group_service.dart":{"language":"Dart","code":53,"comment":1,"blank":6},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/trip/trip_state.dart":{"language":"Dart","code":27,"comment":0,"blank":13},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/providers/user_provider.dart":{"language":"Dart","code":94,"comment":9,"blank":18},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/pages/signup.dart":{"language":"Dart","code":256,"comment":7,"blank":20},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/theme/theme_state.dart":{"language":"Dart","code":16,"comment":0,"blank":5},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/trip/trip_event.dart":{"language":"Dart","code":51,"comment":0,"blank":20},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/components/map/map_content.dart":{"language":"Dart","code":100,"comment":4,"blank":11},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/theme/theme_event.dart":{"language":"Dart","code":14,"comment":0,"blank":6},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/theme/theme_bloc.dart":{"language":"Dart","code":40,"comment":1,"blank":5},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/trip/trip_bloc.dart":{"language":"Dart","code":103,"comment":1,"blank":14},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/components/home/show_trip_details_content.dart":{"language":"Dart","code":106,"comment":2,"blank":4},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/components/home/home_content.dart":{"language":"Dart","code":366,"comment":12,"blank":30},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/analysis_options.yaml":{"language":"YAML","code":3,"comment":22,"blank":4},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/components/home/create_trip_content.dart":{"language":"Dart","code":481,"comment":21,"blank":63},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/auth/auth_bloc.dart":{"language":"Dart","code":129,"comment":1,"blank":20},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/components/count/count_content.dart":{"language":"Dart","code":25,"comment":0,"blank":3},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/android/gradle.properties":{"language":"Properties","code":3,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/android/gradle/wrapper/gradle-wrapper.properties":{"language":"Properties","code":5,"comment":0,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/android/build/reports/problems/problems-report.html":{"language":"HTML","code":548,"comment":2,"blank":114},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/android/app/google-services.json":{"language":"JSON","code":54,"comment":0,"blank":0},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/android/app/src/debug/AndroidManifest.xml":{"language":"XML","code":3,"comment":4,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/android/app/src/main/res/values/styles.xml":{"language":"XML","code":9,"comment":9,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/android/app/src/profile/AndroidManifest.xml":{"language":"XML","code":3,"comment":4,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/android/app/src/main/AndroidManifest.xml":{"language":"XML","code":40,"comment":11,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/android/app/src/main/res/drawable-v21/launch_background.xml":{"language":"XML","code":4,"comment":7,"blank":2},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/android/app/src/main/res/drawable/launch_background.xml":{"language":"XML","code":4,"comment":7,"blank":2},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/android/app/src/main/res/values-night/styles.xml":{"language":"XML","code":9,"comment":9,"blank":1},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/components/group/group_content.dart":{"language":"Dart","code":115,"comment":2,"blank":12},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/auth/auth_state.dart":{"language":"Dart","code":28,"comment":0,"blank":14},"file:///c%3A/Users/dayro/Documents/coding/travel_mate/lib/blocs/auth/auth_event.dart":{"language":"Dart","code":40,"comment":0,"blank":15}} \ No newline at end of file diff --git a/.VSCodeCounter/2025-10-14_11-10-48/results.md b/.VSCodeCounter/2025-10-14_11-10-48/results.md new file mode 100644 index 0000000..0bcda08 --- /dev/null +++ b/.VSCodeCounter/2025-10-14_11-10-48/results.md @@ -0,0 +1,81 @@ +# Summary + +Date : 2025-10-14 11:10:48 + +Directory c:\\Users\\dayro\\Documents\\coding\\travel_mate + +Total : 67 files, 5159 codes, 328 comments, 739 blanks, all 6226 lines + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| Dart | 43 | 4,134 | 189 | 557 | 4,880 | +| HTML | 1 | 548 | 2 | 114 | 664 | +| JSON | 4 | 200 | 0 | 2 | 202 | +| XML | 9 | 133 | 53 | 11 | 197 | +| Markdown | 2 | 48 | 0 | 18 | 66 | +| YAML | 2 | 34 | 80 | 18 | 132 | +| Ruby | 1 | 32 | 2 | 10 | 44 | +| Swift | 2 | 21 | 2 | 6 | 29 | +| Properties | 2 | 8 | 0 | 2 | 10 | +| C++ | 1 | 1 | 0 | 1 | 2 | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| . | 67 | 5,159 | 328 | 739 | 6,226 | +| . (Files) | 4 | 80 | 80 | 34 | 194 | +| android | 11 | 682 | 53 | 125 | 860 | +| android (Files) | 1 | 3 | 0 | 1 | 4 | +| android\\app | 8 | 126 | 51 | 9 | 186 | +| android\\app (Files) | 1 | 54 | 0 | 0 | 54 | +| android\\app\\src | 7 | 72 | 51 | 9 | 132 | +| android\\app\\src\\debug | 1 | 3 | 4 | 1 | 8 | +| android\\app\\src\\main | 5 | 66 | 43 | 7 | 116 | +| android\\app\\src\\main (Files) | 1 | 40 | 11 | 1 | 52 | +| android\\app\\src\\main\\res | 4 | 26 | 32 | 6 | 64 | +| android\\app\\src\\main\\res\\drawable | 1 | 4 | 7 | 2 | 13 | +| android\\app\\src\\main\\res\\drawable-v21 | 1 | 4 | 7 | 2 | 13 | +| android\\app\\src\\main\\res\\values | 1 | 9 | 9 | 1 | 19 | +| android\\app\\src\\main\\res\\values-night | 1 | 9 | 9 | 1 | 19 | +| android\\app\\src\\profile | 1 | 3 | 4 | 1 | 8 | +| android\\build | 1 | 548 | 2 | 114 | 664 | +| android\\build\\reports | 1 | 548 | 2 | 114 | 664 | +| android\\build\\reports\\problems | 1 | 548 | 2 | 114 | 664 | +| android\\gradle | 1 | 5 | 0 | 1 | 6 | +| android\\gradle\\wrapper | 1 | 5 | 0 | 1 | 6 | +| ios | 9 | 263 | 6 | 23 | 292 | +| ios (Files) | 1 | 32 | 2 | 10 | 44 | +| ios\\Runner | 7 | 224 | 2 | 9 | 235 | +| ios\\Runner (Files) | 2 | 15 | 0 | 3 | 18 | +| ios\\RunnerTests | 1 | 7 | 2 | 4 | 13 | +| ios\\Runner\\Assets.xcassets | 3 | 148 | 0 | 4 | 152 | +| ios\\Runner\\Assets.xcassets\\AppIcon.appiconset | 1 | 122 | 0 | 1 | 123 | +| ios\\Runner\\Assets.xcassets\\LaunchImage.imageset | 2 | 26 | 0 | 3 | 29 | +| ios\\Runner\\Base.lproj | 2 | 61 | 2 | 2 | 65 | +| lib | 42 | 4,120 | 179 | 550 | 4,849 | +| lib (Files) | 2 | 149 | 12 | 10 | 171 | +| lib\\blocs | 12 | 448 | 3 | 115 | 566 | +| lib\\blocs\\auth | 3 | 197 | 1 | 49 | 247 | +| lib\\blocs\\theme | 3 | 70 | 1 | 16 | 87 | +| lib\\blocs\\trip | 3 | 181 | 1 | 47 | 229 | +| lib\\blocs\\user | 3 | 0 | 0 | 3 | 3 | +| lib\\components | 9 | 1,704 | 53 | 172 | 1,929 | +| lib\\components\\count | 1 | 25 | 0 | 3 | 28 | +| lib\\components\\group | 1 | 115 | 2 | 12 | 129 | +| lib\\components\\home | 3 | 953 | 35 | 97 | 1,085 | +| lib\\components\\map | 1 | 100 | 4 | 11 | 115 | +| lib\\components\\profile | 1 | 328 | 6 | 29 | 363 | +| lib\\components\\settings | 2 | 183 | 6 | 20 | 209 | +| lib\\data | 6 | 274 | 22 | 43 | 339 | +| lib\\data\\data_sources | 2 | 0 | 0 | 2 | 2 | +| lib\\data\\models | 4 | 274 | 22 | 41 | 337 | +| lib\\pages | 4 | 780 | 24 | 74 | 878 | +| lib\\providers | 2 | 135 | 10 | 27 | 172 | +| lib\\repositories | 3 | 284 | 27 | 45 | 356 | +| lib\\services | 4 | 346 | 28 | 64 | 438 | +| test | 1 | 14 | 10 | 7 | 31 | + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2025-10-14_11-10-48/results.txt b/.VSCodeCounter/2025-10-14_11-10-48/results.txt new file mode 100644 index 0000000..417eb78 --- /dev/null +++ b/.VSCodeCounter/2025-10-14_11-10-48/results.txt @@ -0,0 +1,151 @@ +Date : 2025-10-14 11:10:48 +Directory : c:\Users\dayro\Documents\coding\travel_mate +Total : 67 files, 5159 codes, 328 comments, 739 blanks, all 6226 lines + +Languages ++------------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++------------+------------+------------+------------+------------+------------+ +| Dart | 43 | 4,134 | 189 | 557 | 4,880 | +| HTML | 1 | 548 | 2 | 114 | 664 | +| JSON | 4 | 200 | 0 | 2 | 202 | +| XML | 9 | 133 | 53 | 11 | 197 | +| Markdown | 2 | 48 | 0 | 18 | 66 | +| YAML | 2 | 34 | 80 | 18 | 132 | +| Ruby | 1 | 32 | 2 | 10 | 44 | +| Swift | 2 | 21 | 2 | 6 | 29 | +| Properties | 2 | 8 | 0 | 2 | 10 | +| C++ | 1 | 1 | 0 | 1 | 2 | ++------------+------------+------------+------------+------------+------------+ + +Directories ++-----------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++-----------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| . | 67 | 5,159 | 328 | 739 | 6,226 | +| . (Files) | 4 | 80 | 80 | 34 | 194 | +| android | 11 | 682 | 53 | 125 | 860 | +| android (Files) | 1 | 3 | 0 | 1 | 4 | +| android\app | 8 | 126 | 51 | 9 | 186 | +| android\app (Files) | 1 | 54 | 0 | 0 | 54 | +| android\app\src | 7 | 72 | 51 | 9 | 132 | +| android\app\src\debug | 1 | 3 | 4 | 1 | 8 | +| android\app\src\main | 5 | 66 | 43 | 7 | 116 | +| android\app\src\main (Files) | 1 | 40 | 11 | 1 | 52 | +| android\app\src\main\res | 4 | 26 | 32 | 6 | 64 | +| android\app\src\main\res\drawable | 1 | 4 | 7 | 2 | 13 | +| android\app\src\main\res\drawable-v21 | 1 | 4 | 7 | 2 | 13 | +| android\app\src\main\res\values | 1 | 9 | 9 | 1 | 19 | +| android\app\src\main\res\values-night | 1 | 9 | 9 | 1 | 19 | +| android\app\src\profile | 1 | 3 | 4 | 1 | 8 | +| android\build | 1 | 548 | 2 | 114 | 664 | +| android\build\reports | 1 | 548 | 2 | 114 | 664 | +| android\build\reports\problems | 1 | 548 | 2 | 114 | 664 | +| android\gradle | 1 | 5 | 0 | 1 | 6 | +| android\gradle\wrapper | 1 | 5 | 0 | 1 | 6 | +| ios | 9 | 263 | 6 | 23 | 292 | +| ios (Files) | 1 | 32 | 2 | 10 | 44 | +| ios\Runner | 7 | 224 | 2 | 9 | 235 | +| ios\Runner (Files) | 2 | 15 | 0 | 3 | 18 | +| ios\RunnerTests | 1 | 7 | 2 | 4 | 13 | +| ios\Runner\Assets.xcassets | 3 | 148 | 0 | 4 | 152 | +| ios\Runner\Assets.xcassets\AppIcon.appiconset | 1 | 122 | 0 | 1 | 123 | +| ios\Runner\Assets.xcassets\LaunchImage.imageset | 2 | 26 | 0 | 3 | 29 | +| ios\Runner\Base.lproj | 2 | 61 | 2 | 2 | 65 | +| lib | 42 | 4,120 | 179 | 550 | 4,849 | +| lib (Files) | 2 | 149 | 12 | 10 | 171 | +| lib\blocs | 12 | 448 | 3 | 115 | 566 | +| lib\blocs\auth | 3 | 197 | 1 | 49 | 247 | +| lib\blocs\theme | 3 | 70 | 1 | 16 | 87 | +| lib\blocs\trip | 3 | 181 | 1 | 47 | 229 | +| lib\blocs\user | 3 | 0 | 0 | 3 | 3 | +| lib\components | 9 | 1,704 | 53 | 172 | 1,929 | +| lib\components\count | 1 | 25 | 0 | 3 | 28 | +| lib\components\group | 1 | 115 | 2 | 12 | 129 | +| lib\components\home | 3 | 953 | 35 | 97 | 1,085 | +| lib\components\map | 1 | 100 | 4 | 11 | 115 | +| lib\components\profile | 1 | 328 | 6 | 29 | 363 | +| lib\components\settings | 2 | 183 | 6 | 20 | 209 | +| lib\data | 6 | 274 | 22 | 43 | 339 | +| lib\data\data_sources | 2 | 0 | 0 | 2 | 2 | +| lib\data\models | 4 | 274 | 22 | 41 | 337 | +| lib\pages | 4 | 780 | 24 | 74 | 878 | +| lib\providers | 2 | 135 | 10 | 27 | 172 | +| lib\repositories | 3 | 284 | 27 | 45 | 356 | +| lib\services | 4 | 346 | 28 | 64 | 438 | +| test | 1 | 14 | 10 | 7 | 31 | ++-----------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ + +Files ++-----------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++-----------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| c:\Users\dayro\Documents\coding\travel_mate\README.md | Markdown | 45 | 0 | 16 | 61 | +| c:\Users\dayro\Documents\coding\travel_mate\analysis_options.yaml | YAML | 3 | 22 | 4 | 29 | +| c:\Users\dayro\Documents\coding\travel_mate\android\app\google-services.json | JSON | 54 | 0 | 0 | 54 | +| c:\Users\dayro\Documents\coding\travel_mate\android\app\src\debug\AndroidManifest.xml | XML | 3 | 4 | 1 | 8 | +| c:\Users\dayro\Documents\coding\travel_mate\android\app\src\main\AndroidManifest.xml | XML | 40 | 11 | 1 | 52 | +| c:\Users\dayro\Documents\coding\travel_mate\android\app\src\main\res\drawable-v21\launch_background.xml | XML | 4 | 7 | 2 | 13 | +| c:\Users\dayro\Documents\coding\travel_mate\android\app\src\main\res\drawable\launch_background.xml | XML | 4 | 7 | 2 | 13 | +| c:\Users\dayro\Documents\coding\travel_mate\android\app\src\main\res\values-night\styles.xml | XML | 9 | 9 | 1 | 19 | +| c:\Users\dayro\Documents\coding\travel_mate\android\app\src\main\res\values\styles.xml | XML | 9 | 9 | 1 | 19 | +| c:\Users\dayro\Documents\coding\travel_mate\android\app\src\profile\AndroidManifest.xml | XML | 3 | 4 | 1 | 8 | +| c:\Users\dayro\Documents\coding\travel_mate\android\build\reports\problems\problems-report.html | HTML | 548 | 2 | 114 | 664 | +| c:\Users\dayro\Documents\coding\travel_mate\android\gradle.properties | Properties | 3 | 0 | 1 | 4 | +| c:\Users\dayro\Documents\coding\travel_mate\android\gradle\wrapper\gradle-wrapper.properties | Properties | 5 | 0 | 1 | 6 | +| c:\Users\dayro\Documents\coding\travel_mate\firebase.json | JSON | 1 | 0 | 0 | 1 | +| c:\Users\dayro\Documents\coding\travel_mate\ios\Podfile | Ruby | 32 | 2 | 10 | 44 | +| c:\Users\dayro\Documents\coding\travel_mate\ios\RunnerTests\RunnerTests.swift | Swift | 7 | 2 | 4 | 13 | +| c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\AppDelegate.swift | Swift | 14 | 0 | 2 | 16 | +| c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Assets.xcassets\AppIcon.appiconset\Contents.json | JSON | 122 | 0 | 1 | 123 | +| c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Assets.xcassets\LaunchImage.imageset\Contents.json | JSON | 23 | 0 | 1 | 24 | +| c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Assets.xcassets\LaunchImage.imageset\README.md | Markdown | 3 | 0 | 2 | 5 | +| c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Base.lproj\LaunchScreen.storyboard | XML | 36 | 1 | 1 | 38 | +| c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Base.lproj\Main.storyboard | XML | 25 | 1 | 1 | 27 | +| c:\Users\dayro\Documents\coding\travel_mate\ios\Runner\Runner-Bridging-Header.h | C++ | 1 | 0 | 1 | 2 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\auth\auth_bloc.dart | Dart | 129 | 1 | 20 | 150 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\auth\auth_event.dart | Dart | 40 | 0 | 15 | 55 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\auth\auth_state.dart | Dart | 28 | 0 | 14 | 42 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\theme\theme_bloc.dart | Dart | 40 | 1 | 5 | 46 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\theme\theme_event.dart | Dart | 14 | 0 | 6 | 20 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\theme\theme_state.dart | Dart | 16 | 0 | 5 | 21 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\trip\trip_bloc.dart | Dart | 103 | 1 | 14 | 118 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\trip\trip_event.dart | Dart | 51 | 0 | 20 | 71 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\trip\trip_state.dart | Dart | 27 | 0 | 13 | 40 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\user\user_bloc.dart | Dart | 0 | 0 | 1 | 1 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\user\user_event.dart | Dart | 0 | 0 | 1 | 1 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\blocs\user\user_state.dart | Dart | 0 | 0 | 1 | 1 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\components\count\count_content.dart | Dart | 25 | 0 | 3 | 28 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\components\group\group_content.dart | Dart | 115 | 2 | 12 | 129 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\components\home\create_trip_content.dart | Dart | 481 | 21 | 63 | 565 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\components\home\home_content.dart | Dart | 366 | 12 | 30 | 408 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\components\home\show_trip_details_content.dart | Dart | 106 | 2 | 4 | 112 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\components\map\map_content.dart | Dart | 100 | 4 | 11 | 115 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\components\profile\profile_content.dart | Dart | 328 | 6 | 29 | 363 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\components\settings\settings_content.dart | Dart | 60 | 2 | 10 | 72 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\components\settings\settings_theme_content.dart | Dart | 123 | 4 | 10 | 137 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\data\data_sources\firestore_data_source.dart | Dart | 0 | 0 | 1 | 1 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\data\data_sources\local_data_source.dart | Dart | 0 | 0 | 1 | 1 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\data\models\group.dart | Dart | 23 | 0 | 3 | 26 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\data\models\message.dart | Dart | 14 | 0 | 1 | 15 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\data\models\trip.dart | Dart | 176 | 16 | 24 | 216 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\data\models\user.dart | Dart | 61 | 6 | 13 | 80 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\firebase_options.dart | Dart | 60 | 12 | 5 | 77 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\main.dart | Dart | 89 | 0 | 5 | 94 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\pages\home.dart | Dart | 147 | 6 | 13 | 166 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\pages\login.dart | Dart | 285 | 9 | 28 | 322 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\pages\resetpswd.dart | Dart | 92 | 2 | 13 | 107 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\pages\signup.dart | Dart | 256 | 7 | 20 | 283 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\providers\theme_provider.dart | Dart | 41 | 1 | 9 | 51 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\providers\user_provider.dart | Dart | 94 | 9 | 18 | 121 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\repositories\auth_repository.dart | Dart | 115 | 11 | 22 | 148 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\repositories\trip_repository.dart | Dart | 92 | 9 | 12 | 113 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\repositories\user_repository.dart | Dart | 77 | 7 | 11 | 95 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\services\auth_service.dart | Dart | 84 | 6 | 18 | 108 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\services\group_service.dart | Dart | 53 | 1 | 6 | 60 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\services\message_service.dart | Dart | 0 | 0 | 1 | 1 | +| c:\Users\dayro\Documents\coding\travel_mate\lib\services\trip_service.dart | Dart | 209 | 21 | 39 | 269 | +| c:\Users\dayro\Documents\coding\travel_mate\pubspec.yaml | YAML | 31 | 58 | 14 | 103 | +| c:\Users\dayro\Documents\coding\travel_mate\test\widget_test.dart | Dart | 14 | 10 | 7 | 31 | +| Total | | 5,159 | 328 | 739 | 6,226 | ++-----------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/lib/blocs/group/group_bloc.dart b/lib/blocs/group/group_bloc.dart new file mode 100644 index 0000000..44bcf82 --- /dev/null +++ b/lib/blocs/group/group_bloc.dart @@ -0,0 +1,120 @@ +import 'dart:async'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../services/group_service.dart'; +import 'group_event.dart'; +import 'group_state.dart'; +import '../../data/models/group.dart'; + +class GroupBloc extends Bloc { + final GroupService _groupService; + StreamSubscription? _groupsSubscription; + + GroupBloc({GroupService? groupService}) + : _groupService = groupService ?? GroupService(), + super(GroupInitial()) { + on(_onLoadRequested); + on<_GroupUpdated>(_onGroupUpdated); + on(_onCreateRequested); + on(_onUpdateRequested); + on(_onDeleteRequested); + on(_onMemberAddRequested); + on(_onMemberRemoveRequested); + } + + Future _onLoadRequested( + GroupLoadRequested event, + Emitter emit, + ) async { + emit(GroupLoading()); + + await _groupsSubscription?.cancel(); + + _groupsSubscription = _groupService.getGroupsStreamByUser(event.userId).listen( + (groups) => add(_GroupUpdated(groups: groups)), + onError: (error) => emit(GroupError(message: error.toString())), + ); + } + + Future _onGroupUpdated( + _GroupUpdated event, + Emitter emit, + ) async { + emit(GroupLoaded(groups: event.groups)); + } + + Future _onCreateRequested( + GroupCreateRequested event, + Emitter emit, + ) async { + try { + await _groupService.createGroup(event.group); + emit(const GroupOperationSuccess(message: 'Groupe créé avec succès')); + } catch (e) { + emit(GroupError(message: e.toString())); + } + } + + Future _onUpdateRequested( + GroupUpdateRequested event, + Emitter emit, + ) async { + try { + await _groupService.updateGroup(event.group); + emit(const GroupOperationSuccess(message: 'Groupe mis à jour')); + } catch (e) { + emit(GroupError(message: e.toString())); + } + } + + Future _onDeleteRequested( + GroupDeleteRequested event, + Emitter emit, + ) async { + try { + await _groupService.deleteGroup(event.groupId); + emit(const GroupOperationSuccess(message: 'Groupe supprimé')); + } catch (e) { + emit(GroupError(message: e.toString())); + } + } + + Future _onMemberAddRequested( + GroupMemberAddRequested event, + Emitter emit, + ) async { + try { + await _groupService.addMemberToGroup(event.groupId, event.memberId); + emit(const GroupOperationSuccess(message: 'Membre ajouté')); + } catch (e) { + emit(GroupError(message: e.toString())); + } + } + + Future _onMemberRemoveRequested( + GroupMemberRemoveRequested event, + Emitter emit, + ) async { + try { + await _groupService.removeMemberFromGroup(event.groupId, event.memberId); + emit(const GroupOperationSuccess(message: 'Membre retiré')); + } catch (e) { + emit(GroupError(message: e.toString())); + } + } + + @override + Future close() { + _groupsSubscription?.cancel(); + return super.close(); + } +} + +// Événement interne pour les mises à jour du stream +class _GroupUpdated extends GroupEvent { + final List groups; + + const _GroupUpdated({required this.groups}); + + @override + List get props => [groups]; +} diff --git a/lib/blocs/group/group_event.dart b/lib/blocs/group/group_event.dart new file mode 100644 index 0000000..677f19e --- /dev/null +++ b/lib/blocs/group/group_event.dart @@ -0,0 +1,71 @@ +import 'package:equatable/equatable.dart'; +import '../../data/models/group.dart'; + +abstract class GroupEvent extends Equatable { + const GroupEvent(); + + @override + List get props => []; +} + +class GroupLoadRequested extends GroupEvent { + final String userId; + + const GroupLoadRequested({required this.userId}); + + @override + List get props => [userId]; +} + +class GroupCreateRequested extends GroupEvent { + final Group group; + + const GroupCreateRequested({required this.group}); + + @override + List get props => [group]; +} + +class GroupUpdateRequested extends GroupEvent { + final Group group; + + const GroupUpdateRequested({required this.group}); + + @override + List get props => [group]; +} + +class GroupDeleteRequested extends GroupEvent { + final String groupId; + + const GroupDeleteRequested({required this.groupId}); + + @override + List get props => [groupId]; +} + +class GroupMemberAddRequested extends GroupEvent { + final String groupId; + final String memberId; + + const GroupMemberAddRequested({ + required this.groupId, + required this.memberId, + }); + + @override + List get props => [groupId, memberId]; +} + +class GroupMemberRemoveRequested extends GroupEvent { + final String groupId; + final String memberId; + + const GroupMemberRemoveRequested({ + required this.groupId, + required this.memberId, + }); + + @override + List get props => [groupId, memberId]; +} diff --git a/lib/blocs/group/group_state.dart b/lib/blocs/group/group_state.dart new file mode 100644 index 0000000..220aba3 --- /dev/null +++ b/lib/blocs/group/group_state.dart @@ -0,0 +1,40 @@ +import 'package:equatable/equatable.dart'; +import '../../data/models/group.dart'; + +abstract class GroupState extends Equatable { + const GroupState(); + + @override + List get props => []; +} + +class GroupInitial extends GroupState {} + +class GroupLoading extends GroupState {} + +class GroupLoaded extends GroupState { + final List groups; + + const GroupLoaded({required this.groups}); + + @override + List get props => [groups]; +} + +class GroupOperationSuccess extends GroupState { + final String message; + + const GroupOperationSuccess({required this.message}); + + @override + List get props => [message]; +} + +class GroupError extends GroupState { + final String message; + + const GroupError({required this.message}); + + @override + List get props => [message]; +} diff --git a/lib/blocs/trip/trip_bloc.dart b/lib/blocs/trip/trip_bloc.dart index 37f1a4b..09bffb5 100644 --- a/lib/blocs/trip/trip_bloc.dart +++ b/lib/blocs/trip/trip_bloc.dart @@ -13,6 +13,7 @@ class TripBloc extends Bloc { : _tripRepository = tripRepository, super(TripInitial()) { on(_onLoadRequested); + on<_TripUpdated>(_onTripUpdated); on(_onCreateRequested); on(_onUpdateRequested); on(_onDeleteRequested); @@ -29,11 +30,18 @@ class TripBloc extends Bloc { await _tripsSubscription?.cancel(); _tripsSubscription = _tripRepository.getUserTrips(event.userId).listen( - (trips) => add(const _TripUpdated(trips: [])), // Sera géré par un événement interne + (trips) => add(_TripUpdated(trips: trips)), onError: (error) => emit(TripError(message: error.toString())), ); } + Future _onTripUpdated( + _TripUpdated event, + Emitter emit, + ) async { + emit(TripLoaded(trips: event.trips)); + } + Future _onCreateRequested( TripCreateRequested event, Emitter emit, diff --git a/lib/blocs/user/user_bloc.dart b/lib/blocs/user/user_bloc.dart index e69de29..7ac5ad1 100644 --- a/lib/blocs/user/user_bloc.dart +++ b/lib/blocs/user/user_bloc.dart @@ -0,0 +1,126 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'user_event.dart' as event; +import 'user_state.dart' as state; + +class UserBloc extends Bloc { + final FirebaseAuth _auth = FirebaseAuth.instance; + final FirebaseFirestore _firestore = FirebaseFirestore.instance; + + UserBloc() : super(state.UserInitial()) { + on(_onUserInitialized); + on(_onLoadUser); + on(_onUserUpdated); + on(_onUserLoggedOut); + } + + Future _onUserInitialized( + event.UserInitialized event, + Emitter emit, + ) async { + emit(state.UserLoading()); + + try { + final currentUser = _auth.currentUser; + + if (currentUser == null) { + emit(state.UserError('Aucun utilisateur connecté')); + return; + } + + // Récupérer les données utilisateur depuis Firestore + final userDoc = await _firestore + .collection('users') + .doc(currentUser.uid) + .get(); + + if (!userDoc.exists) { + // Créer un utilisateur par défaut si non existant + final defaultUser = state.UserModel( + id: currentUser.uid, + email: currentUser.email ?? '', + prenom: currentUser.displayName ?? 'Voyageur', + ); + + await _firestore + .collection('users') + .doc(currentUser.uid) + .set(defaultUser.toJson()); + + emit(state.UserLoaded(defaultUser)); + } else { + final user = state.UserModel.fromJson({ + 'id': currentUser.uid, + ...userDoc.data()!, + }); + emit(state.UserLoaded(user)); + } + } catch (e) { + emit(state.UserError('Erreur lors du chargement de l\'utilisateur: $e')); + } + } + + Future _onLoadUser( + event.LoadUser event, + Emitter emit, + ) async { + emit(state.UserLoading()); + + try { + final userDoc = await _firestore + .collection('users') + .doc(event.userId) + .get(); + + if (userDoc.exists) { + final user = state.UserModel.fromJson({ + 'id': event.userId, + ...userDoc.data()!, + }); + emit(state.UserLoaded(user)); + } else { + emit(state.UserError('Utilisateur non trouvé')); + } + } catch (e) { + emit(state.UserError('Erreur lors du chargement: $e')); + } + } + + Future _onUserUpdated( + event.UserUpdated event, + Emitter emit, + ) async { + if (this.state is state.UserLoaded) { + final currentUser = (this.state as state.UserLoaded).user; + + try { + await _firestore + .collection('users') + .doc(currentUser.id) + .update(event.userData); + + final updatedDoc = await _firestore + .collection('users') + .doc(currentUser.id) + .get(); + + final updatedUser = state.UserModel.fromJson({ + 'id': currentUser.id, + ...updatedDoc.data()!, + }); + + emit(state.UserLoaded(updatedUser)); + } catch (e) { + emit(state.UserError('Erreur lors de la mise à jour: $e')); + } + } + } + + Future _onUserLoggedOut( + event.UserLoggedOut event, + Emitter emit, + ) async { + emit(state.UserInitial()); + } +} diff --git a/lib/blocs/user/user_event.dart b/lib/blocs/user/user_event.dart index e69de29..93cdeb1 100644 --- a/lib/blocs/user/user_event.dart +++ b/lib/blocs/user/user_event.dart @@ -0,0 +1,22 @@ +abstract class UserEvent {} + +class UserInitialized extends UserEvent {} + +class UserLoaded extends UserEvent { + final String userId; + + UserLoaded(this.userId); +} + +class UserUpdated extends UserEvent { + final Map userData; + + UserUpdated(this.userData); +} + +class UserLoggedOut extends UserEvent {} +class LoadUser extends UserEvent { + final String userId; + + LoadUser(this.userId); +} diff --git a/lib/blocs/user/user_state.dart b/lib/blocs/user/user_state.dart index e69de29..f3d6fa5 100644 --- a/lib/blocs/user/user_state.dart +++ b/lib/blocs/user/user_state.dart @@ -0,0 +1,61 @@ +import 'package:equatable/equatable.dart'; + +abstract class UserState extends Equatable { + @override + List get props => []; +} + +class UserInitial extends UserState {} + +class UserLoading extends UserState {} + +class UserLoaded extends UserState { + final UserModel user; + + UserLoaded(this.user); + + @override + List get props => [user]; +} + +class UserError extends UserState { + final String message; + + UserError(this.message); + + @override + List get props => [message]; +} + +// Modèle utilisateur simple +class UserModel { + final String id; + final String email; + final String prenom; + final String? nom; + + UserModel({ + required this.id, + required this.email, + required this.prenom, + this.nom, + }); + + factory UserModel.fromJson(Map json) { + return UserModel( + id: json['id'] ?? '', + email: json['email'] ?? '', + prenom: json['prenom'] ?? 'Voyageur', + nom: json['nom'], + ); + } + + Map toJson() { + return { + 'id': id, + 'email': email, + 'prenom': prenom, + 'nom': nom, + }; + } +} diff --git a/lib/components/group/group_content.dart b/lib/components/group/group_content.dart index b14831b..1d7a1e8 100644 --- a/lib/components/group/group_content.dart +++ b/lib/components/group/group_content.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:travel_mate/models/group.dart'; -import 'package:provider/provider.dart'; -import '../../providers/user_provider.dart'; -import '../../services/group_service.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:travel_mate/data/models/group.dart'; +import '../../blocs/user/user_bloc.dart'; +import '../../blocs/user/user_state.dart' as user_state; +import '../../blocs/group/group_bloc.dart'; +import '../../blocs/group/group_state.dart'; +import '../../blocs/group/group_event.dart'; class GroupContent extends StatefulWidget { const GroupContent({super.key}); @@ -12,51 +15,82 @@ class GroupContent extends StatefulWidget { } class _GroupContentState extends State { + @override + void initState() { + super.initState(); + _loadGroupsIfUserLoaded(); + } + + void _loadGroupsIfUserLoaded() { + final userState = context.read().state; + if (userState is user_state.UserLoaded) { + context.read().add(GroupLoadRequested(userId: userState.user.id)); + } + } @override Widget build(BuildContext context) { - return Scaffold( - body: Consumer( - builder: (context, userProvider, child) { - final user = userProvider.currentUser; - if (user == null || user.id == null) { - return const Center( - child: Text('Utilisateur non connecté'), - ); - } + return BlocBuilder( + builder: (context, userState) { + if (userState is user_state.UserLoading) { + return Scaffold( + body: Center(child: CircularProgressIndicator()), + ); + } - return StreamBuilder>( - stream: GroupService().getGroupsStreamByUser(user.id!), // Filtrer par utilisateur - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return _buildLoadingState(); - } else if (snapshot.hasError) { - print('Erreur du stream: ${snapshot.error}'); - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Erreur lors du chargement des groupes.'), - const SizedBox(height: 8), - ElevatedButton( - onPressed: () { - setState(() { - // Forcer le rechargement du stream - }); - }, - child: const Text('Réessayer'), - ), - ], - ), - ); - } + if (userState is user_state.UserError) { + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.error, size: 64, color: Colors.red), + SizedBox(height: 16), + Text('Erreur: ${userState.message}'), + ], + ), + ), + ); + } - final groups = snapshot.data ?? []; - print("Groupes reçus pour l'utilisateur ${user.id}: ${groups.length}"); + if (userState is! user_state.UserLoaded) { + return Scaffold( + body: Center(child: Text('Utilisateur non connecté')), + ); + } - return RefreshIndicator( + final user = userState.user; + + // Charger les groupes si ce n'est pas déjà fait + if (context.read().state is GroupInitial) { + context.read().add(GroupLoadRequested(userId: user.id)); + } + + return BlocConsumer( + listener: (context, groupState) { + if (groupState is GroupOperationSuccess) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(groupState.message), + backgroundColor: Colors.green, + ), + ); + // Recharger les groupes + context.read().add(GroupLoadRequested(userId: user.id)); + } else if (groupState is GroupError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(groupState.message), + backgroundColor: Colors.red, + ), + ); + } + }, + builder: (context, groupState) { + return Scaffold( + body: RefreshIndicator( onRefresh: () async { - setState(() {}); + context.read().add(GroupLoadRequested(userId: user.id)); }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), @@ -69,31 +103,74 @@ class _GroupContentState extends State { style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, - color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white + : Colors.black, ), ), const SizedBox(height: 20), - groups.isEmpty ? _buildEmptyState() : _buildGroupList(groups), + + if (groupState is GroupLoading) + _buildLoadingState() + else if (groupState is GroupError) + _buildErrorState(groupState.message, user.id) + else if (groupState is GroupLoaded) + groupState.groups.isEmpty + ? _buildEmptyState() + : _buildGroupList(groupState.groups) + else + _buildEmptyState(), + const SizedBox(height: 80), ], ), ), - ); - }, - ); - }, - ), + ), + ); + }, + ); + }, ); } Widget _buildLoadingState() { return const Center( - child: Padding(padding: EdgeInsets.all(16.0), + child: Padding( + padding: EdgeInsets.all(16.0), child: CircularProgressIndicator(), ), ); } + Widget _buildErrorState(String error, String userId) { + return Center( + child: Padding( + padding: EdgeInsets.all(32), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.error, size: 64, color: Colors.red), + const SizedBox(height: 16), + const Text('Erreur lors du chargement des groupes.'), + const SizedBox(height: 8), + Text( + error, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 12, color: Colors.grey), + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () { + context.read().add(GroupLoadRequested(userId: userId)); + }, + child: const Text('Réessayer'), + ), + ], + ), + ), + ); + } + Widget _buildEmptyState() { return const Center( child: Text( diff --git a/lib/components/home/create_trip_content.dart b/lib/components/home/create_trip_content.dart index 6b5db29..a9baa07 100644 --- a/lib/components/home/create_trip_content.dart +++ b/lib/components/home/create_trip_content.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:travel_mate/data/models/trip.dart'; -import 'package:travel_mate/providers/user_provider.dart'; -import 'package:travel_mate/services/trip_service.dart'; -import 'package:travel_mate/services/group_service.dart'; -import 'package:travel_mate/data/models/group.dart'; - +import '../../blocs/user/user_bloc.dart'; +import '../../blocs/user/user_state.dart' as user_state; +import '../../blocs/trip/trip_bloc.dart'; +import '../../blocs/trip/trip_event.dart'; +import '../../blocs/group/group_bloc.dart'; +import '../../blocs/group/group_event.dart'; +import '../../data/models/group.dart'; +import '../../services/user_service.dart'; class CreateTripContent extends StatefulWidget { const CreateTripContent({super.key}); @@ -20,12 +23,12 @@ class _CreateTripContentState extends State { final _descriptionController = TextEditingController(); final _locationController = TextEditingController(); final _budgetController = TextEditingController(); + final _userService = UserService(); DateTime? _startDate; DateTime? _endDate; bool _isLoading = false; - // Liste des participants (emails) final List _participants = []; final _participantController = TextEditingController(); @@ -41,233 +44,235 @@ class _CreateTripContentState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Créer un voyage'), - backgroundColor: Theme.of(context).colorScheme.primary, - foregroundColor: Colors.white, - ), - body: SingleChildScrollView( - padding: EdgeInsets.all(16), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Titre du voyage - _buildSectionTitle('Informations générales'), - SizedBox(height: 16), + return BlocBuilder( + builder: (context, userState) { + if (userState is! user_state.UserLoaded) { + return Scaffold( + appBar: AppBar(title: Text('Créer un voyage')), + body: Center(child: Text('Veuillez vous connecter')), + ); + } - TextFormField( - controller: _titleController, - validator: (value) { - if (value == null || value.trim().isEmpty) { - return 'Titre requis'; - } - return null; - }, - decoration: InputDecoration( - labelText: 'Titre du voyage *', - hintText: 'ex: Voyage à Paris', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - ), - prefixIcon: Icon(Icons.travel_explore), - ), - ), - - SizedBox(height: 16), - - // Description - TextFormField( - controller: _descriptionController, - maxLines: 3, - decoration: InputDecoration( - labelText: 'Description', - hintText: 'Décrivez votre voyage...', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - ), - prefixIcon: Icon(Icons.description), - ), - ), - - SizedBox(height: 16), - - // Destination - TextFormField( - controller: _locationController, - validator: (value) { - if (value == null || value.trim().isEmpty) { - return 'Destination requise'; - } - return null; - }, - decoration: InputDecoration( - labelText: 'Destination *', - hintText: 'ex: Paris, France', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - ), - prefixIcon: Icon(Icons.location_on), - ), - ), - - SizedBox(height: 24), - - // Dates - _buildSectionTitle('Dates du voyage'), - SizedBox(height: 16), - - Row( + return Scaffold( + appBar: AppBar( + title: Text('Créer un voyage'), + backgroundColor: Theme.of(context).colorScheme.primary, + foregroundColor: Colors.white, + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: _buildDateField( - label: 'Date de début *', - date: _startDate, - onTap: () => _selectStartDate(context), - ), - ), - SizedBox(width: 16), - Expanded( - child: _buildDateField( - label: 'Date de fin *', - date: _endDate, - onTap: () => _selectEndDate(context), - ), - ), - ], - ), + _buildSectionTitle('Informations générales'), + SizedBox(height: 16), - SizedBox(height: 24), - - // Budget - _buildSectionTitle('Budget'), - SizedBox(height: 16), - - TextFormField( - controller: _budgetController, - keyboardType: TextInputType.numberWithOptions(decimal: true), - decoration: InputDecoration( - labelText: 'Budget estimé', - hintText: 'ex: 1200.50', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - ), - prefixIcon: Icon(Icons.euro), - suffixText: '€', - ), - ), - - SizedBox(height: 24), - - // Participants - _buildSectionTitle('Participants'), - SizedBox(height: 8), - Text( - 'Ajoutez les emails des personnes que vous souhaitez inviter', - style: TextStyle(color: Colors.grey[600], fontSize: 14), - ), - SizedBox(height: 16), - - // Champ d'ajout de participant - Row( - children: [ - Expanded( - child: TextFormField( - controller: _participantController, - keyboardType: TextInputType.emailAddress, - decoration: InputDecoration( - labelText: 'Email du participant', - hintText: 'ex: ami@email.com', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - ), - prefixIcon: Icon(Icons.person_add), - ), - ), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: _addParticipant, - style: ElevatedButton.styleFrom( - shape: RoundedRectangleBorder( + TextFormField( + controller: _titleController, + validator: (value) { + if (value == null || value.trim().isEmpty) { + return 'Titre requis'; + } + return null; + }, + decoration: InputDecoration( + labelText: 'Titre du voyage *', + hintText: 'ex: Voyage à Paris', + border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), - padding: EdgeInsets.all(16), - ), - child: Icon(Icons.add), - ), - ], - ), - - SizedBox(height: 16), - - // Liste des participants ajoutés - if (_participants.isNotEmpty) ...[ - Text( - 'Participants ajoutés (${_participants.length})', - style: TextStyle(fontWeight: FontWeight.w500), - ), - SizedBox(height: 8), - Container( - width: double.infinity, - padding: EdgeInsets.all(12), - decoration: BoxDecoration( - border: Border.all(color: Colors.grey[300]!), - borderRadius: BorderRadius.circular(12), - ), - child: Wrap( - spacing: 8, - runSpacing: 8, - children: _participants - .map( - (email) => Chip( - label: Text(email, style: TextStyle(fontSize: 12)), - deleteIcon: Icon(Icons.close, size: 18), - onDeleted: () => _removeParticipant(email), - backgroundColor: Theme.of( - context, - ).colorScheme.primary.withAlpha(25), - ), - ) - .toList(), - ), - ), - ], - - SizedBox(height: 32), - - // Bouton de création - SizedBox( - width: double.infinity, - height: 50, - child: ElevatedButton( - onPressed: _isLoading ? null : _saveTrip, - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.primary, - foregroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), + prefixIcon: Icon(Icons.travel_explore), ), ), - child: _isLoading - ? CircularProgressIndicator(color: Colors.white) - : Text( - 'Créer le voyage', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, + + SizedBox(height: 16), + + TextFormField( + controller: _descriptionController, + maxLines: 3, + decoration: InputDecoration( + labelText: 'Description', + hintText: 'Décrivez votre voyage...', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + prefixIcon: Icon(Icons.description), + ), + ), + + SizedBox(height: 16), + + TextFormField( + controller: _locationController, + validator: (value) { + if (value == null || value.trim().isEmpty) { + return 'Destination requise'; + } + return null; + }, + decoration: InputDecoration( + labelText: 'Destination *', + hintText: 'ex: Paris, France', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + prefixIcon: Icon(Icons.location_on), + ), + ), + + SizedBox(height: 24), + + _buildSectionTitle('Dates du voyage'), + SizedBox(height: 16), + + Row( + children: [ + Expanded( + child: _buildDateField( + label: 'Date de début *', + date: _startDate, + onTap: () => _selectStartDate(context), + ), + ), + SizedBox(width: 16), + Expanded( + child: _buildDateField( + label: 'Date de fin *', + date: _endDate, + onTap: () => _selectEndDate(context), + ), + ), + ], + ), + + SizedBox(height: 24), + + _buildSectionTitle('Budget'), + SizedBox(height: 16), + + TextFormField( + controller: _budgetController, + keyboardType: TextInputType.numberWithOptions(decimal: true), + decoration: InputDecoration( + labelText: 'Budget estimé', + hintText: 'ex: 1200.50', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + prefixIcon: Icon(Icons.euro), + suffixText: '€', + ), + ), + + SizedBox(height: 24), + + _buildSectionTitle('Participants'), + SizedBox(height: 8), + Text( + 'Ajoutez les emails des personnes que vous souhaitez inviter', + style: TextStyle(color: Colors.grey[600], fontSize: 14), + ), + SizedBox(height: 16), + + Row( + children: [ + Expanded( + child: TextFormField( + controller: _participantController, + keyboardType: TextInputType.emailAddress, + decoration: InputDecoration( + labelText: 'Email du participant', + hintText: 'ex: ami@email.com', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + prefixIcon: Icon(Icons.person_add), ), ), - ), - ), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: _addParticipant, + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + padding: EdgeInsets.all(16), + ), + child: Icon(Icons.add), + ), + ], + ), - SizedBox(height: 20), - ], + SizedBox(height: 16), + + if (_participants.isNotEmpty) ...[ + Text( + 'Participants ajoutés (${_participants.length})', + style: TextStyle(fontWeight: FontWeight.w500), + ), + SizedBox(height: 8), + Container( + width: double.infinity, + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey[300]!), + borderRadius: BorderRadius.circular(12), + ), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: _participants + .map( + (email) => Chip( + label: Text(email, style: TextStyle(fontSize: 12)), + deleteIcon: Icon(Icons.close, size: 18), + onDeleted: () => _removeParticipant(email), + backgroundColor: Theme.of( + context, + ).colorScheme.primary.withAlpha(25), + ), + ) + .toList(), + ), + ), + ], + + SizedBox(height: 32), + + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: _isLoading ? null : () => _saveTrip(userState.user), + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.primary, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: _isLoading + ? CircularProgressIndicator(color: Colors.white) + : Text( + 'Créer le voyage', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + + SizedBox(height: 20), + ], + ), + ), ), - ), - ), + ); + }, ); } @@ -287,7 +292,6 @@ class _CreateTripContentState extends State { required DateTime? date, required VoidCallback onTap, }) { - // Détecter le thème actuel final isDarkMode = Theme.of(context).brightness == Brightness.dark; final textColor = isDarkMode ? Colors.white : Colors.black; final labelColor = isDarkMode ? Colors.white70 : Colors.grey[600]; @@ -305,10 +309,7 @@ class _CreateTripContentState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - label, - style: TextStyle(fontSize: 12, color: labelColor), - ), + Text(label, style: TextStyle(fontSize: 12, color: labelColor)), SizedBox(height: 8), Row( children: [ @@ -352,9 +353,7 @@ class _CreateTripContentState extends State { if (_startDate == null) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Veuillez d\'abord sélectionner la date de début'), - ), + SnackBar(content: Text('Veuillez d\'abord sélectionner la date de début')), ); } return; @@ -377,7 +376,6 @@ class _CreateTripContentState extends State { final email = _participantController.text.trim(); if (email.isEmpty) return; - // Validation email simple final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); if (!emailRegex.hasMatch(email)) { if (mounted) { @@ -388,7 +386,6 @@ class _CreateTripContentState extends State { return; } - // Vérifier si l'email existe déjà if (_participants.contains(email)) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( @@ -410,66 +407,7 @@ class _CreateTripContentState extends State { }); } - Future _saveGroup(String currentUserId) async { - if (!_formKey.currentState!.validate()) { - return false; - } - - // Convertir les emails en IDs - final participantIds = await _changeUserEmailById(_participants); - - // Créer la liste des membres incluant le créateur - List allMembers = [currentUserId]; - - // Ajouter tous les participants (éviter les doublons) - for (String participantId in participantIds) { - if (!allMembers.contains(participantId)) { - allMembers.add(participantId); - } - } - - print('Membres du groupe: $allMembers'); - - final group = Group( - id: '', - name: _titleController.text.trim(), - members: allMembers, // Contient le créateur + tous les participants - ); - - final groupService = GroupService(); - bool success = await groupService.createGroup(group); - print('Groupe créé avec succès: $success'); - return success; - } - - Future> _changeUserEmailById(List participants) async { - final userProvider = Provider.of(context, listen: false); - List ids = []; - - for (String email in participants) { - try { - final id = await userProvider.getUserIdByEmail(email); - if (id != null) { - ids.add(id); - } else { - print('Utilisateur non trouvé pour l\'ID: $email'); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Utilisateur non trouvé pour l\'email: $email'), - backgroundColor: Colors.orange, - duration: Duration(seconds: 2), - ), - ); - } - } catch (e) { - print('Erreur lors de la récupération de l\'utilisateur $email: $e'); - } - } - - return ids; - } - - Future _saveTrip() async { + Future _saveTrip(user_state.UserModel currentUser) async { if (!_formKey.currentState!.validate()) { return; } @@ -488,68 +426,48 @@ class _CreateTripContentState extends State { }); try { - final userProvider = Provider.of(context, listen: false); - final currentUser = userProvider.currentUser; - - if (currentUser == null || currentUser.id == null) { - throw Exception('Utilisateur non connecté'); - } - - print('Création du voyage par: ${currentUser.id} (${currentUser.email})'); - - // Convertir les emails en IDs utilisateur + // Convertir les emails en IDs List participantIds = await _changeUserEmailById(_participants); - // Ajouter le créateur aux participants s'il n'y est pas déjà - if (!participantIds.contains(currentUser.id!)) { - participantIds.insert(0, currentUser.id!); + // Ajouter le créateur + if (!participantIds.contains(currentUser.id)) { + participantIds.insert(0, currentUser.id); } - print('Participants IDs (avec créateur): $participantIds'); - - // Créer l'objet Trip avec les IDs des participants + // Créer le voyage final trip = Trip( - id: '', // Sera généré par Firebase + id: '', title: _titleController.text.trim(), description: _descriptionController.text.trim(), location: _locationController.text.trim(), startDate: _startDate!, endDate: _endDate!, budget: double.tryParse(_budgetController.text) ?? 0.0, - createdBy: currentUser.id!, - participants: participantIds, // Contient le créateur + tous les participants + createdBy: currentUser.id, + participants: participantIds, createdAt: DateTime.now(), updatedAt: DateTime.now(), ); - print('Données du voyage: ${trip.toMap()}'); + // Créer le groupe + final group = Group( + id: '', + name: _titleController.text.trim(), + members: participantIds, + ); - // Sauvegarder le voyage - final tripService = TripService(); - final success = await tripService.addTrip(trip); + // Utiliser les BLoCs pour créer + context.read().add(TripCreateRequested(trip: trip)); + context.read().add(GroupCreateRequested(group: group)); - // Créer le groupe associé au voyage avec le créateur inclus - final successGroup = await _saveGroup(currentUser.id!); - - if (success && successGroup && mounted) { - print('Voyage et groupe créés avec succès !'); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Voyage créé avec succès !'), - backgroundColor: Colors.green, - ), - ); - - Navigator.pop(context, true); // Retourner true pour indiquer le succès - } else { - throw Exception('Erreur lors de la sauvegarde'); + if (mounted) { + Navigator.pop(context, true); } } catch (e) { - print('Erreur lors de la création: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Erreur lors de la création: $e'), + content: Text('Erreur: $e'), backgroundColor: Colors.red, ), ); @@ -562,4 +480,30 @@ class _CreateTripContentState extends State { } } } + + Future> _changeUserEmailById(List participants) async { + List ids = []; + + for (String email in participants) { + try { + final id = await _userService.getUserIdByEmail(email); + if (id != null) { + ids.add(id); + } else { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Utilisateur non trouvé: $email'), + backgroundColor: Colors.orange, + ), + ); + } + } + } catch (e) { + print('Erreur: $e'); + } + } + + return ids; + } } \ No newline at end of file diff --git a/lib/components/home/home_content.dart b/lib/components/home/home_content.dart index 2099fa5..d638ba6 100644 --- a/lib/components/home/home_content.dart +++ b/lib/components/home/home_content.dart @@ -1,10 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:travel_mate/components/home/create_trip_content.dart'; -import '../../providers/user_provider.dart'; -import '../../services/trip_service.dart'; -import '../../data/models/trip.dart'; import '../home/show_trip_details_content.dart'; +import '../../blocs/user/user_bloc.dart'; +import '../../blocs/user/user_state.dart'; +import '../../blocs/trip/trip_bloc.dart'; +import '../../blocs/trip/trip_state.dart'; +import '../../blocs/trip/trip_event.dart'; +import '../../data/models/trip.dart'; class HomeContent extends StatefulWidget { const HomeContent({super.key}); @@ -14,61 +17,87 @@ class HomeContent extends StatefulWidget { } class _HomeContentState extends State { - final TripService _tripService = TripService(); + @override + void initState() { + super.initState(); + // Charger les trips quand le widget est initialisé + _loadTripsIfUserLoaded(); + } + + void _loadTripsIfUserLoaded() { + final userState = context.read().state; + if (userState is UserLoaded) { + context.read().add(TripLoadRequested(userId: userState.user.id)); + } + } @override Widget build(BuildContext context) { - return Scaffold( - body: Consumer( - builder: (context, userProvider, child) { - final user = userProvider.currentUser; - - if (user == null || user.id == null) { - return Center( - child: Text('Utilisateur non connecté'), - ); - } - - return StreamBuilder>( - stream: _tripService.getTripsStreamByUser(user.id!, user.email), - builder: (context, snapshot) { - print('StreamBuilder - ConnectionState: ${snapshot.connectionState}'); - print('StreamBuilder - HasError: ${snapshot.hasError}'); - print('StreamBuilder - Data: ${snapshot.data?.length ?? 0} trips'); - - if (snapshot.connectionState == ConnectionState.waiting) { - return _buildLoadingState(); - } - - if (snapshot.hasError) { - print('Erreur du stream: ${snapshot.error}'); - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.error, size: 64, color: Colors.red), - SizedBox(height: 16), - Text('Erreur lors du chargement des voyages'), - SizedBox(height: 8), - Text('${snapshot.error}'), - SizedBox(height: 8), - ElevatedButton( - onPressed: () { - setState(() {}); // Forcer le rebuild - }, - child: Text('Réessayer'), - ), - ], - ), - ); - } - - final trips = snapshot.data ?? []; - print('Trips reçus du stream: ${trips.length}'); - - return RefreshIndicator( + return BlocBuilder( + builder: (context, userState) { + if (userState is UserLoading) { + return Scaffold( + body: Center( + child: CircularProgressIndicator(), + ), + ); + } + + if (userState is UserError) { + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.error, size: 64, color: Colors.red), + SizedBox(height: 16), + Text('Erreur: ${userState.message}'), + ], + ), + ), + ); + } + + if (userState is! UserLoaded) { + return Scaffold( + body: Center( + child: Text('Veuillez vous connecter'), + ), + ); + } + + final user = userState.user; + + // Charger les trips si ce n'est pas déjà fait + if (context.read().state is TripInitial) { + context.read().add(TripLoadRequested(userId: user.id)); + } + + return BlocConsumer( + listener: (context, tripState) { + if (tripState is TripOperationSuccess) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(tripState.message), + backgroundColor: Colors.green, + ), + ); + // Recharger les trips après une opération réussie + context.read().add(TripLoadRequested(userId: user.id)); + } else if (tripState is TripError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(tripState.message), + backgroundColor: Colors.red, + ), + ); + } + }, + builder: (context, tripState) { + return Scaffold( + body: RefreshIndicator( onRefresh: () async { - setState(() {}); // Forcer le rebuild du stream + context.read().add(TripLoadRequested(userId: user.id)); }, child: SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(), @@ -88,43 +117,47 @@ class _HomeContentState extends State { ), SizedBox(height: 20), - // Contenu principal - trips.isEmpty ? _buildEmptyState() : _buildTripsList(trips), + // Contenu principal basé sur l'état du TripBloc + if (tripState is TripLoading) + _buildLoadingState() + else if (tripState is TripError) + _buildErrorState(tripState.message, user.id) + else if (tripState is TripLoaded) + tripState.trips.isEmpty + ? _buildEmptyState() + : _buildTripsList(tripState.trips) + else + _buildEmptyState(), // Espacement en bas pour éviter que le FAB cache le contenu const SizedBox(height: 80), ], ), ), - ); - }, - ); - }, - ), - - // FloatingActionButton - floatingActionButton: FloatingActionButton( - onPressed: () async { - final result = await Navigator.push( - context, - MaterialPageRoute(builder: (context) => CreateTripContent()), - ); - - // Le stream se mettra à jour automatiquement - if (result == true) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Voyage créé ! Il apparaîtra dans quelques secondes.'), - backgroundColor: Colors.green, ), + + // FloatingActionButton + floatingActionButton: FloatingActionButton( + onPressed: () async { + final result = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => CreateTripContent()), + ); + + if (result == true) { + // Recharger les trips + context.read().add(TripLoadRequested(userId: user.id)); + } + }, + backgroundColor: Theme.of(context).colorScheme.primary, + foregroundColor: Colors.white, + child: const Icon(Icons.add), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, ); - } - }, - backgroundColor: Theme.of(context).colorScheme.primary, - foregroundColor: Colors.white, - child: const Icon(Icons.add), - ), - floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, + }, + ); + }, ); } @@ -137,6 +170,35 @@ class _HomeContentState extends State { ); } + Widget _buildErrorState(String error, String userId) { + return Center( + child: Padding( + padding: EdgeInsets.all(32), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.error, size: 64, color: Colors.red), + SizedBox(height: 16), + Text('Erreur lors du chargement des voyages'), + SizedBox(height: 8), + Text( + error, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 12, color: Colors.grey), + ), + SizedBox(height: 16), + ElevatedButton( + onPressed: () { + context.read().add(TripLoadRequested(userId: userId)); + }, + child: Text('Réessayer'), + ), + ], + ), + ), + ); + } + Widget _buildEmptyState() { return Center( child: Padding( @@ -182,7 +244,6 @@ class _HomeContentState extends State { final colors = [Colors.blue, Colors.orange, Colors.green, Colors.purple, Colors.red]; final color = colors[trip.title.hashCode.abs() % colors.length]; - // Détecter le thème actuel final isDarkMode = Theme.of(context).brightness == Brightness.dark; final textColor = isDarkMode ? Colors.white : Colors.black; final secondaryTextColor = isDarkMode ? Colors.white70 : Colors.grey[700]; @@ -255,9 +316,9 @@ class _HomeContentState extends State { Expanded( child: Text( trip.location, - style: TextStyle( + style: const TextStyle( fontSize: 14, - color: textColor, + color: Colors.white, ), overflow: TextOverflow.ellipsis, ), @@ -341,7 +402,7 @@ class _HomeContentState extends State { Icon(Icons.euro, size: 16, color: iconColor), SizedBox(width: 8), Text( - 'Budget: ${trip.budget?.toStringAsFixed(2)}€', + 'Budget: ${trip.budget!.toStringAsFixed(2)}€', style: TextStyle( fontSize: 14, color: iconColor, diff --git a/lib/components/profile/profile_content.dart b/lib/components/profile/profile_content.dart index def1557..6086dd0 100644 --- a/lib/components/profile/profile_content.dart +++ b/lib/components/profile/profile_content.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import '../../providers/user_provider.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../blocs/user/user_bloc.dart'; +import '../../blocs/user/user_state.dart' as user_state; +import '../../blocs/user/user_event.dart' as user_event; import '../../services/auth_service.dart'; class ProfileContent extends StatelessWidget { @@ -8,14 +10,22 @@ class ProfileContent extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, userProvider, child) { - final user = userProvider.currentUser; + return BlocBuilder( + builder: (context, state) { + if (state is user_state.UserLoading) { + return Center(child: CircularProgressIndicator()); + } - if (user == null) { + if (state is user_state.UserError) { + return Center(child: Text('Erreur: ${state.message}')); + } + + if (state is! user_state.UserLoaded) { return Center(child: Text('Aucun utilisateur connecté')); } + final user = state.user; + return Column( children: [ // Section titre @@ -62,7 +72,7 @@ class ProfileContent extends StatelessWidget { // Nom complet Text( - user.fullName, + '${user.prenom} ${user.nom ?? ''}', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, @@ -96,7 +106,7 @@ class ProfileContent extends StatelessWidget { title: Text('Changer le mot de passe'), trailing: Icon(Icons.arrow_forward_ios), onTap: () { - _showChangePasswordDialog(context); + _showChangePasswordDialog(context, user); }, ), @@ -107,7 +117,7 @@ class ProfileContent extends StatelessWidget { title: Text('Supprimer le compte'), trailing: Icon(Icons.arrow_forward_ios), onTap: () { - _showDeleteAccountDialog(context); + _showDeleteAccountDialog(context, user); }, ), ], @@ -116,13 +126,13 @@ class ProfileContent extends StatelessWidget { ); } - void _showEditProfileDialog(BuildContext context, user) { + void _showEditProfileDialog(BuildContext context, user_state.UserModel user) { final nomController = TextEditingController(text: user.nom); final prenomController = TextEditingController(text: user.prenom); showDialog( context: context, - builder: (BuildContext context) { + builder: (BuildContext dialogContext) { return AlertDialog( title: Text('Modifier le profil'), content: Column( @@ -141,37 +151,26 @@ class ProfileContent extends StatelessWidget { ), actions: [ TextButton( - onPressed: () => Navigator.of(context).pop(), + onPressed: () => Navigator.of(dialogContext).pop(), child: Text('Annuler'), ), TextButton( - onPressed: () async { - if (prenomController.text.trim().isNotEmpty && - nomController.text.trim().isNotEmpty) { - final updatedUser = user.copyWith( - nom: nomController.text.trim(), - prenom: prenomController.text.trim(), + onPressed: () { + if (prenomController.text.trim().isNotEmpty) { + context.read().add( + user_event.UserUpdated({ + 'prenom': prenomController.text.trim(), + 'nom': nomController.text.trim(), + }), ); - final success = await Provider.of(context, - listen: false) - .updateUser(updatedUser); - - Navigator.of(context).pop(); - - if (success) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Profil mis à jour !'), - backgroundColor: Colors.green), - ); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Erreur lors de la mise à jour'), - backgroundColor: Colors.red), - ); - } + Navigator.of(dialogContext).pop(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Profil mis à jour !'), + backgroundColor: Colors.green, + ), + ); } }, child: Text('Sauvegarder'), @@ -182,7 +181,7 @@ class ProfileContent extends StatelessWidget { ); } - void _showChangePasswordDialog(BuildContext context) { + void _showChangePasswordDialog(BuildContext context, user_state.UserModel user) { final currentPasswordController = TextEditingController(); final newPasswordController = TextEditingController(); final confirmPasswordController = TextEditingController(); @@ -190,7 +189,7 @@ class ProfileContent extends StatelessWidget { showDialog( context: context, - builder: (BuildContext context) { + builder: (BuildContext dialogContext) { return AlertDialog( title: Text('Changer le mot de passe'), content: Column( @@ -211,15 +210,13 @@ class ProfileContent extends StatelessWidget { TextField( controller: confirmPasswordController, obscureText: true, - decoration: InputDecoration( - labelText: 'Confirmer le mot de passe', - ), + decoration: InputDecoration(labelText: 'Confirmer le mot de passe'), ), ], ), actions: [ TextButton( - onPressed: () => Navigator.of(context).pop(), + onPressed: () => Navigator.of(dialogContext).pop(), child: Text('Annuler'), ), TextButton( @@ -229,8 +226,9 @@ class ProfileContent extends StatelessWidget { confirmPasswordController.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Tous les champs sont requis'), - backgroundColor: Colors.red), + content: Text('Tous les champs sont requis'), + backgroundColor: Colors.red, + ), ); return; } @@ -238,42 +236,33 @@ class ProfileContent extends StatelessWidget { if (newPasswordController.text != confirmPasswordController.text) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Les mots de passe ne correspondent pas'), - backgroundColor: Colors.red), - ); - return; - } - - if (newPasswordController.text.length < 8) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: - Text('Le mot de passe doit contenir au moins 8 caractères'), - backgroundColor: Colors.red), + content: Text('Les mots de passe ne correspondent pas'), + backgroundColor: Colors.red, + ), ); return; } try { - final user = Provider.of(context, listen: false) - .currentUser; await authService.resetPasswordFromCurrentPassword( currentPassword: currentPasswordController.text, newPassword: newPasswordController.text, - email: user!.email, + email: user.email, ); - Navigator.of(context).pop(); + Navigator.of(dialogContext).pop(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Mot de passe changé !'), - backgroundColor: Colors.green), + content: Text('Mot de passe changé !'), + backgroundColor: Colors.green, + ), ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Erreur: Mot de passe actuel incorrect'), - backgroundColor: Colors.red), + content: Text('Erreur: Mot de passe actuel incorrect'), + backgroundColor: Colors.red, + ), ); } }, @@ -285,13 +274,13 @@ class ProfileContent extends StatelessWidget { ); } - void _showDeleteAccountDialog(BuildContext context) { + void _showDeleteAccountDialog(BuildContext context, user_state.UserModel user) { final passwordController = TextEditingController(); final authService = AuthService(); showDialog( context: context, - builder: (BuildContext context) { + builder: (BuildContext dialogContext) { return AlertDialog( title: Text('Supprimer le compte'), content: Column( @@ -313,30 +302,19 @@ class ProfileContent extends StatelessWidget { ), actions: [ TextButton( - onPressed: () => Navigator.of(context).pop(), + onPressed: () => Navigator.of(dialogContext).pop(), child: Text('Annuler'), ), TextButton( onPressed: () async { - if (passwordController.text.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Mot de passe requis'), - backgroundColor: Colors.red), - ); - return; - } - try { - final user = Provider.of(context, listen: false) - .currentUser; await authService.deleteAccount( password: passwordController.text, - email: user!.email, + email: user.email, ); - Navigator.of(context).pop(); - Provider.of(context, listen: false).logout(); + Navigator.of(dialogContext).pop(); + context.read().add(user_event.UserLoggedOut()); Navigator.pushNamedAndRemoveUntil( context, '/login', @@ -345,9 +323,9 @@ class ProfileContent extends StatelessWidget { } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: - Text('Erreur lors de la suppression: Mot de passe incorrect'), - backgroundColor: Colors.red), + content: Text('Erreur: Mot de passe incorrect'), + backgroundColor: Colors.red, + ), ); } }, diff --git a/lib/components/settings/settings_theme_content.dart b/lib/components/settings/settings_theme_content.dart index d4484e8..96043a3 100644 --- a/lib/components/settings/settings_theme_content.dart +++ b/lib/components/settings/settings_theme_content.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import '../../providers/theme_provider.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../blocs/theme/theme_bloc.dart'; +import '../../blocs/theme/theme_state.dart'; +import '../../blocs/theme/theme_event.dart'; class SettingsThemeContent extends StatelessWidget { const SettingsThemeContent({super.key}); @@ -12,8 +14,8 @@ class SettingsThemeContent extends StatelessWidget { title: const Text('Thème'), backgroundColor: Theme.of(context).colorScheme.inversePrimary, ), - body: Consumer( - builder: (context, themeProvider, child) { + body: BlocBuilder( + builder: (context, state) { return ListView( padding: const EdgeInsets.all(16), children: [ @@ -27,19 +29,21 @@ class SettingsThemeContent extends StatelessWidget { Card( child: ListTile( leading: Icon( - themeProvider.themeMode == ThemeMode.system + state.themeMode == ThemeMode.system ? Icons.radio_button_checked : Icons.radio_button_unchecked, - color: themeProvider.themeMode == ThemeMode.system + color: state.themeMode == ThemeMode.system ? Theme.of(context).colorScheme.primary : null, ), title: const Text('Système'), subtitle: const Text('Suit les paramètres de votre appareil'), trailing: const Icon(Icons.brightness_auto), - selected: themeProvider.themeMode == ThemeMode.system, + selected: state.themeMode == ThemeMode.system, onTap: () { - themeProvider.setThemeMode(ThemeMode.system); + context.read().add( + const ThemeChanged(themeMode: ThemeMode.system), + ); }, ), ), @@ -50,19 +54,21 @@ class SettingsThemeContent extends StatelessWidget { Card( child: ListTile( leading: Icon( - themeProvider.themeMode == ThemeMode.light + state.themeMode == ThemeMode.light ? Icons.radio_button_checked : Icons.radio_button_unchecked, - color: themeProvider.themeMode == ThemeMode.light + color: state.themeMode == ThemeMode.light ? Theme.of(context).colorScheme.primary : null, ), title: const Text('Clair'), subtitle: const Text('Thème clair en permanence'), trailing: const Icon(Icons.light_mode), - selected: themeProvider.themeMode == ThemeMode.light, + selected: state.themeMode == ThemeMode.light, onTap: () { - themeProvider.setThemeMode(ThemeMode.light); + context.read().add( + const ThemeChanged(themeMode: ThemeMode.light), + ); }, ), ), @@ -73,19 +79,21 @@ class SettingsThemeContent extends StatelessWidget { Card( child: ListTile( leading: Icon( - themeProvider.themeMode == ThemeMode.dark + state.themeMode == ThemeMode.dark ? Icons.radio_button_checked : Icons.radio_button_unchecked, - color: themeProvider.themeMode == ThemeMode.dark + color: state.themeMode == ThemeMode.dark ? Theme.of(context).colorScheme.primary : null, ), title: const Text('Sombre'), subtitle: const Text('Thème sombre en permanence'), trailing: const Icon(Icons.dark_mode), - selected: themeProvider.themeMode == ThemeMode.dark, + selected: state.themeMode == ThemeMode.dark, onTap: () { - themeProvider.setThemeMode(ThemeMode.dark); + context.read().add( + const ThemeChanged(themeMode: ThemeMode.dark), + ); }, ), ), @@ -107,14 +115,14 @@ class SettingsThemeContent extends StatelessWidget { Row( children: [ Icon( - themeProvider.isDarkMode + state.isDarkMode ? Icons.dark_mode : Icons.light_mode, color: Theme.of(context).colorScheme.primary, ), const SizedBox(width: 10), Text( - themeProvider.isDarkMode + state.isDarkMode ? 'Mode sombre actif' : 'Mode clair actif', style: TextStyle( diff --git a/lib/main.dart b/lib/main.dart index d7e52a7..feee91c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'blocs/auth/auth_state.dart'; import 'blocs/theme/theme_bloc.dart'; import 'blocs/theme/theme_event.dart'; import 'blocs/theme/theme_state.dart'; +import 'blocs/group/group_bloc.dart'; import 'repositories/auth_repository.dart'; import 'repositories/trip_repository.dart'; import 'repositories/user_repository.dart'; @@ -49,6 +50,7 @@ class MyApp extends StatelessWidget { authRepository: context.read(), )..add(AuthCheckRequested()), ), + BlocProvider(create: (context) => GroupBloc()), ], child: BlocBuilder( builder: (context, themeState) { diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 2452b15..8990aee 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -1,9 +1,14 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import '../components/home/home_content.dart'; import '../components/settings/settings_content.dart'; import '../components/map/map_content.dart'; import '../components/group/group_content.dart'; import '../components/count/count_content.dart'; +import '../blocs/user/user_bloc.dart'; +import '../blocs/user/user_event.dart'; +import '../blocs/auth/auth_bloc.dart'; +import '../blocs/auth/auth_event.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @@ -14,25 +19,28 @@ class HomePage extends StatefulWidget { class _HomePageState extends State { int _currentIndex = 0; - - // Cache pour les pages créées final Map _pageCache = {}; final List titles = [ - 'Mes voyages', // 0 - 'Paramètres', // 1 - 'Carte', // 2 - 'Chat de groupe', // 3 - 'Comptes', // 4 + 'Mes voyages', + 'Paramètres', + 'Carte', + 'Chat de groupe', + 'Comptes', ]; + @override + void initState() { + super.initState(); + // Initialiser les données utilisateur + context.read().add(UserInitialized()); + } + Widget _buildPage(int index) { - // Vérifier si la page est déjà en cache if (_pageCache.containsKey(index)) { return _pageCache[index]!; } - // Créer la page seulement quand elle est demandée Widget page; switch (index) { case 0: @@ -54,7 +62,6 @@ class _HomePageState extends State { page = const HomeContent(); } - // Mettre en cache la page créée _pageCache[index] = page; return page; } @@ -63,7 +70,7 @@ class _HomePageState extends State { setState(() { _currentIndex = index; }); - Navigator.pop(context); // Fermer le drawer + Navigator.pop(context); } @override @@ -87,42 +94,19 @@ class _HomePageState extends State { style: TextStyle(color: Colors.white, fontSize: 24), ), ), - _buildDrawerItem( - icon: Icons.home, - title: "Mes voyages", - index: 0, - ), - _buildDrawerItem( - icon: Icons.settings, - title: "Paramètres", - index: 1, - ), - _buildDrawerItem( - icon: Icons.map, - title: "Carte", - index: 2, - ), - _buildDrawerItem( - icon: Icons.group, - title: "Chat de groupe", - index: 3, - ), - _buildDrawerItem( - icon: Icons.account_balance_wallet, - title: "Comptes", - index: 4, - ), + _buildDrawerItem(icon: Icons.home, title: "Mes voyages", index: 0), + _buildDrawerItem(icon: Icons.settings, title: "Paramètres", index: 1), + _buildDrawerItem(icon: Icons.map, title: "Carte", index: 2), + _buildDrawerItem(icon: Icons.group, title: "Chat de groupe", index: 3), + _buildDrawerItem(icon: Icons.account_balance_wallet, title: "Comptes", index: 4), const Divider(), ListTile( leading: const Icon(Icons.logout, color: Colors.red), title: const Text("Déconnexion", style: TextStyle(color: Colors.red)), onTap: () { Navigator.pop(context); - Navigator.pushNamedAndRemoveUntil( - context, - '/login', - (route) => false, - ); + context.read().add(AuthSignOutRequested()); + Navigator.pushNamedAndRemoveUntil(context, '/login', (route) => false); }, ), ], @@ -131,12 +115,11 @@ class _HomePageState extends State { body: IndexedStack( index: _currentIndex, children: [ - // Créer les pages seulement si elles sont sélectionnées for (int i = 0; i < titles.length; i++) if (_currentIndex == i || _pageCache.containsKey(i)) _buildPage(i) else - Container(), // Placeholder vide + Container(), ], ), ); @@ -158,8 +141,7 @@ class _HomePageState extends State { @override void dispose() { - // Nettoyer le cache si nécessaire _pageCache.clear(); super.dispose(); } -} +} \ No newline at end of file diff --git a/lib/providers/theme_provider.dart b/lib/providers/theme_provider.dart deleted file mode 100644 index 974afbc..0000000 --- a/lib/providers/theme_provider.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -class ThemeProvider extends ChangeNotifier { - ThemeMode _themeMode = ThemeMode.system; - - ThemeMode get themeMode => _themeMode; - - bool get isDarkMode { - if (_themeMode == ThemeMode.system) { - return WidgetsBinding.instance.platformDispatcher.platformBrightness == - Brightness.dark; - } - return _themeMode == ThemeMode.dark; - } - - ThemeProvider() { - _loadThemeMode(); - } - - void setThemeMode(ThemeMode themeMode) async { - _themeMode = themeMode; - notifyListeners(); - - // Sauvegarder la préférence - final prefs = await SharedPreferences.getInstance(); - await prefs.setString('themeMode', themeMode.toString()); - } - - void _loadThemeMode() async { - final prefs = await SharedPreferences.getInstance(); - final themeModeString = prefs.getString('themeMode'); - - if (themeModeString != null) { - switch (themeModeString) { - case 'ThemeMode.light': - _themeMode = ThemeMode.light; - break; - case 'ThemeMode.dark': - _themeMode = ThemeMode.dark; - break; - case 'ThemeMode.system': - default: - _themeMode = ThemeMode.system; - break; - } - notifyListeners(); - } - } -} diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart deleted file mode 100644 index 12a4ce4..0000000 --- a/lib/providers/user_provider.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:firebase_auth/firebase_auth.dart' as firebase_auth; -import 'package:cloud_firestore/cloud_firestore.dart'; -import '../models/user.dart'; -import '../services/auth_service.dart'; - -class UserProvider extends ChangeNotifier { - User? _currentUser; - final AuthService _authService = AuthService(); - final FirebaseFirestore _firestore = FirebaseFirestore.instance; - - User? get currentUser => _currentUser; - - void setCurrentUser(User user) { - _currentUser = user; - notifyListeners(); - } - - Future logout() async { - await _authService.signOut(); - _currentUser = null; - notifyListeners(); - } - - bool get isLoggedIn => _currentUser != null; - - // Méthode pour récupérer les données utilisateur depuis Firestore - Future getUserData(String uid) async { - try { - DocumentSnapshot doc = await _firestore.collection('users').doc(uid).get(); - if (doc.exists) { - Map data = doc.data() as Map; - return User.fromMap({...data, 'id': uid}); - } - return null; - } catch (e) { - print('Erreur lors de la récupération des données utilisateur: $e'); - return null; - } - } - - // Méthode pour sauvegarder les données utilisateur dans Firestore - Future saveUserData(User user) async { - try { - await _firestore.collection('users').doc(user.id).set(user.toMap()); - } catch (e) { - print('Erreur lors de la sauvegarde des données utilisateur: $e'); - rethrow; - } - } - - // Méthode pour mettre à jour les données utilisateur - Future updateUser(User updatedUser) async { - try { - await _firestore.collection('users').doc(updatedUser.id).update(updatedUser.toMap()); - - // Mettre à jour le displayName dans Firebase Auth - await _authService.updateDisplayName(displayName: updatedUser.fullName); - - _currentUser = updatedUser; - notifyListeners(); - return true; - } catch (e) { - print('Erreur lors de la mise à jour de l\'utilisateur: $e'); - return false; - } - } - - // Méthode pour créer un nouvel utilisateur dans Firestore - Future createUser(Map userData) async { - try { - // Structurer les données pour que tous les utilisateurs aient le même format - final userDoc = { - 'email': userData['email'] ?? '', - 'nom': '', // Nom vide pour tous les utilisateurs - 'prenom': userData['name'] ?? userData['nom'] ?? 'Utilisateur', // Nom complet dans prenom - }; - - await _firestore.collection('users').doc(userData['uid']).set(userDoc); - - // Retourner l'objet User créé avec l'ID correct - return User.fromMap({...userDoc, 'id': userData['uid']}); - } catch (e) { - print('Erreur lors de la création de l\'utilisateur: $e'); - return null; - } - } - - // Méthode pour obtenir l'ID d'un utilisateur par son email - Future getUserIdByEmail(String email) async { - try { - final QuerySnapshot querySnapshot = await _firestore - .collection('users') - .where('email', isEqualTo: email.trim()) - .limit(1) - .get(); - - if (querySnapshot.docs.isNotEmpty) { - return querySnapshot.docs.first.id; - } - return null; - } catch (e) { - print('Erreur lors de la recherche de l\'utilisateur par email: $e'); - return null; - } - } - - // Initialiser l'utilisateur connecté - Future initializeUser() async { - firebase_auth.User? firebaseUser = _authService.currentUser; - if (firebaseUser != null) { - User? userData = await getUserData(firebaseUser.uid); - if (userData != null) { - _currentUser = userData; - notifyListeners(); - } - } - } -} - diff --git a/lib/services/group_service.dart b/lib/services/group_service.dart index 3ebbd9f..edacb24 100644 --- a/lib/services/group_service.dart +++ b/lib/services/group_service.dart @@ -1,5 +1,5 @@ import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:travel_mate/models/group.dart'; +import 'package:travel_mate/data/models/group.dart'; class GroupService { final FirebaseFirestore _firestore = FirebaseFirestore.instance; @@ -57,4 +57,14 @@ class GroupService { }).toList(); }); } + + Future removeMemberFromGroup(String groupId, String memberId) async { + // TODO: Implémenter la suppression d'un membre d'un groupe + } + + Future addMemberToGroup(String groupId, String memberId) async { + // TODO: Implémenter l'ajout d'un membre à un groupe + } + + } \ No newline at end of file diff --git a/lib/services/user_service.dart b/lib/services/user_service.dart new file mode 100644 index 0000000..327ce43 --- /dev/null +++ b/lib/services/user_service.dart @@ -0,0 +1,291 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import '../blocs/user/user_state.dart'; + +class UserService { + final FirebaseFirestore _firestore; + final FirebaseAuth _auth; + static const String _usersCollection = 'users'; + + UserService({ + FirebaseFirestore? firestore, + FirebaseAuth? auth, + }) : _firestore = firestore ?? FirebaseFirestore.instance, + _auth = auth ?? FirebaseAuth.instance; + + // Obtenir l'utilisateur connecté actuel + User? getCurrentFirebaseUser() { + return _auth.currentUser; + } + + // Obtenir l'ID de l'utilisateur connecté + String? getCurrentUserId() { + return _auth.currentUser?.uid; + } + + // Créer un nouvel utilisateur dans Firestore + Future createUser(UserModel user) async { + try { + await _firestore + .collection(_usersCollection) + .doc(user.id) + .set(user.toJson()); + return true; + } catch (e) { + print('Erreur lors de la création de l\'utilisateur: $e'); + return false; + } + } + + // Obtenir un utilisateur par son ID + Future getUserById(String userId) async { + try { + final doc = await _firestore + .collection(_usersCollection) + .doc(userId) + .get(); + + if (doc.exists) { + return UserModel.fromJson({ + 'id': doc.id, + ...doc.data() as Map, + }); + } + return null; + } catch (e) { + print('Erreur lors de la récupération de l\'utilisateur: $e'); + return null; + } + } + + // Obtenir un utilisateur par son email + Future getUserByEmail(String email) async { + try { + final querySnapshot = await _firestore + .collection(_usersCollection) + .where('email', isEqualTo: email) + .limit(1) + .get(); + + if (querySnapshot.docs.isNotEmpty) { + final doc = querySnapshot.docs.first; + return UserModel.fromJson({ + 'id': doc.id, + ...doc.data(), + }); + } + return null; + } catch (e) { + print('Erreur lors de la récupération de l\'utilisateur par email: $e'); + return null; + } + } + + // Obtenir l'ID d'un utilisateur par son email + Future getUserIdByEmail(String email) async { + try { + final querySnapshot = await _firestore + .collection(_usersCollection) + .where('email', isEqualTo: email) + .limit(1) + .get(); + + if (querySnapshot.docs.isNotEmpty) { + return querySnapshot.docs.first.id; + } + return null; + } catch (e) { + print('Erreur lors de la récupération de l\'ID utilisateur: $e'); + return null; + } + } + + // Mettre à jour un utilisateur + Future updateUser(String userId, Map userData) async { + try { + await _firestore + .collection(_usersCollection) + .doc(userId) + .update(userData); + return true; + } catch (e) { + print('Erreur lors de la mise à jour de l\'utilisateur: $e'); + return false; + } + } + + // Supprimer un utilisateur + Future deleteUser(String userId) async { + try { + await _firestore + .collection(_usersCollection) + .doc(userId) + .delete(); + return true; + } catch (e) { + print('Erreur lors de la suppression de l\'utilisateur: $e'); + return false; + } + } + + // Vérifier si un email existe déjà + Future emailExists(String email) async { + try { + final querySnapshot = await _firestore + .collection(_usersCollection) + .where('email', isEqualTo: email) + .limit(1) + .get(); + + return querySnapshot.docs.isNotEmpty; + } catch (e) { + print('Erreur lors de la vérification de l\'email: $e'); + return false; + } + } + + // Obtenir plusieurs utilisateurs par leurs IDs + Future> getUsersByIds(List userIds) async { + try { + if (userIds.isEmpty) return []; + + final List users = []; + + // Firestore a une limite de 10 éléments pour les requêtes 'in' + // Donc on divise en chunks de 10 + for (int i = 0; i < userIds.length; i += 10) { + final chunk = userIds.skip(i).take(10).toList(); + + final querySnapshot = await _firestore + .collection(_usersCollection) + .where(FieldPath.documentId, whereIn: chunk) + .get(); + + for (var doc in querySnapshot.docs) { + users.add(UserModel.fromJson({ + 'id': doc.id, + ...doc.data(), + })); + } + } + + return users; + } catch (e) { + print('Erreur lors de la récupération des utilisateurs: $e'); + return []; + } + } + + // Obtenir tous les utilisateurs (à utiliser avec précaution) + Future> getAllUsers() async { + try { + final querySnapshot = await _firestore + .collection(_usersCollection) + .get(); + + return querySnapshot.docs.map((doc) { + return UserModel.fromJson({ + 'id': doc.id, + ...doc.data(), + }); + }).toList(); + } catch (e) { + print('Erreur lors de la récupération de tous les utilisateurs: $e'); + return []; + } + } + + // Stream pour écouter les changements d'un utilisateur + Stream getUserStream(String userId) { + return _firestore + .collection(_usersCollection) + .doc(userId) + .snapshots() + .map((doc) { + if (doc.exists) { + return UserModel.fromJson({ + 'id': doc.id, + ...doc.data() as Map, + }); + } + return null; + }); + } + + // Rechercher des utilisateurs par nom ou email + Future> searchUsers(String query) async { + try { + if (query.isEmpty) return []; + + final queryLower = query.toLowerCase(); + + // Recherche par email + final emailResults = await _firestore + .collection(_usersCollection) + .where('email', isGreaterThanOrEqualTo: queryLower) + .where('email', isLessThanOrEqualTo: '$queryLower\uf8ff') + .limit(10) + .get(); + + // Recherche par prénom + final prenomResults = await _firestore + .collection(_usersCollection) + .where('prenom', isGreaterThanOrEqualTo: queryLower) + .where('prenom', isLessThanOrEqualTo: '$queryLower\uf8ff') + .limit(10) + .get(); + + // Combiner et dédupliquer les résultats + final Map usersMap = {}; + + for (var doc in emailResults.docs) { + usersMap[doc.id] = UserModel.fromJson({ + 'id': doc.id, + ...doc.data(), + }); + } + + for (var doc in prenomResults.docs) { + usersMap[doc.id] = UserModel.fromJson({ + 'id': doc.id, + ...doc.data(), + }); + } + + return usersMap.values.toList(); + } catch (e) { + print('Erreur lors de la recherche d\'utilisateurs: $e'); + return []; + } + } + + // Mettre à jour la dernière connexion de l'utilisateur + Future updateLastLogin(String userId) async { + try { + await _firestore + .collection(_usersCollection) + .doc(userId) + .update({ + 'lastLogin': FieldValue.serverTimestamp(), + }); + return true; + } catch (e) { + print('Erreur lors de la mise à jour de la dernière connexion: $e'); + return false; + } + } + + // Vérifier si l'utilisateur existe dans Firestore + Future userExists(String userId) async { + try { + final doc = await _firestore + .collection(_usersCollection) + .doc(userId) + .get(); + return doc.exists; + } catch (e) { + print('Erreur lors de la vérification de l\'existence de l\'utilisateur: $e'); + return false; + } + } +}