coregen-cli 3.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- coregen.py +1585 -0
- coregen_cli-3.1.6.dist-info/METADATA +1211 -0
- coregen_cli-3.1.6.dist-info/RECORD +172 -0
- coregen_cli-3.1.6.dist-info/WHEEL +5 -0
- coregen_cli-3.1.6.dist-info/entry_points.txt +2 -0
- coregen_cli-3.1.6.dist-info/top_level.txt +2 -0
- generators/__init__.py +1 -0
- generators/assets/__init__.py +1 -0
- generators/assets/assets.py +30 -0
- generators/assets/main.py +4 -0
- generators/config/__init__.py +1 -0
- generators/config/analisy_options.py +42 -0
- generators/config/main.py +11 -0
- generators/config/pubspec.py +139 -0
- generators/cursor/__init__.py +3 -0
- generators/cursor/setup.py +87 -0
- generators/helpers/__init__.py +56 -0
- generators/helpers/component.py +1030 -0
- generators/helpers/config.py +255 -0
- generators/helpers/data_source.py +180 -0
- generators/helpers/domain.py +469 -0
- generators/helpers/feature.py +654 -0
- generators/helpers/navigation.py +274 -0
- generators/helpers/page.py +63 -0
- generators/helpers/project.py +33 -0
- generators/helpers/utils.py +338 -0
- generators/helpers/validation.py +364 -0
- generators/initializator.py +68 -0
- generators/main.py +81 -0
- generators/static/assets/logo.png +0 -0
- generators/static/assets/mock/auth_user.json +5 -0
- generators/static/assets/svgs/apple.svg +4 -0
- generators/static/assets/svgs/google.svg +2 -0
- generators/static/cursor/AGENTS.md.jinja +37 -0
- generators/static/cursor/agents/doc-writer.md +20 -0
- generators/static/cursor/agents/epic-orchestrator.md +28 -0
- generators/static/cursor/agents/feature-implementer.md +29 -0
- generators/static/cursor/agents/integration-wiring.md +30 -0
- generators/static/cursor/agents/layer-application.md +21 -0
- generators/static/cursor/agents/layer-guardian.md +21 -0
- generators/static/cursor/agents/layer-infrastructure.md +31 -0
- generators/static/cursor/agents/layer-model.md +24 -0
- generators/static/cursor/agents/layer-presentation.md +33 -0
- generators/static/cursor/docs/architecture/APIS_AND_INTEGRATION.md.jinja +42 -0
- generators/static/cursor/docs/architecture/CARAVAGGIO_COMPONENTS.md.jinja +317 -0
- generators/static/cursor/docs/architecture/DDD_LAYERS.md.jinja +32 -0
- generators/static/cursor/docs/architecture/FILE_TEMPLATES.md.jinja +76 -0
- generators/static/cursor/docs/architecture/MOCK_AND_REMOTE_DATA.md.jinja +87 -0
- generators/static/cursor/docs/architecture/REFERENCE_IMPLEMENTATIONS.md.jinja +48 -0
- generators/static/cursor/docs/architecture/WIDGETS_AND_CARAVAGGIO.md.jinja +76 -0
- generators/static/cursor/docs/epics/README.md +17 -0
- generators/static/cursor/lib/domain/AGENTS.md +23 -0
- generators/static/cursor/lib/features/AGENTS.md +23 -0
- generators/static/cursor/lib/widgets/AGENTS.md +21 -0
- generators/static/cursor/rules/apis-layer.mdc.jinja +21 -0
- generators/static/cursor/rules/application-layer.mdc.jinja +12 -0
- generators/static/cursor/rules/architecture-core.mdc.jinja +43 -0
- generators/static/cursor/rules/dart-conventions.mdc.jinja +14 -0
- generators/static/cursor/rules/domain-layer.mdc.jinja +24 -0
- generators/static/cursor/rules/login-context.mdc.jinja +16 -0
- generators/static/cursor/rules/presentation-layer.mdc.jinja +15 -0
- generators/static/cursor/rules/quality-gate.mdc.jinja +14 -0
- generators/static/cursor/rules/ui-caravaggio.mdc.jinja +22 -0
- generators/static/cursor/skills/caravaggio-ui/SKILL.md +138 -0
- generators/static/cursor/skills/epic-delivery/SKILL.md +74 -0
- generators/static/cursor/skills/epic-delivery/templates/epic-status.md +14 -0
- generators/static/cursor/skills/epic-delivery/templates/epic.md +36 -0
- generators/static/templates/apis/common/constants_template.jinja +48 -0
- generators/static/templates/apis/common/data_source_config_template.jinja +47 -0
- generators/static/templates/apis/core/api_injectable_module_template.jinja +14 -0
- generators/static/templates/apis/interceptors/api_logger_template.jinja +135 -0
- generators/static/templates/apis/interceptors/auth_interceptor_template.jinja +207 -0
- generators/static/templates/auth/application/auth_bloc_template.jinja +36 -0
- generators/static/templates/auth/application/auth_event_template.jinja +7 -0
- generators/static/templates/auth/application/auth_state_template.jinja +8 -0
- generators/static/templates/auth/infrastructure/auth_facade_module_template.jinja +16 -0
- generators/static/templates/auth/infrastructure/auth_facade_template.jinja +288 -0
- generators/static/templates/auth/infrastructure/mock_auth_facade_template.jinja +106 -0
- generators/static/templates/auth/model/auth_failure_template.jinja +11 -0
- generators/static/templates/auth/model/i_auth_facade_template.jinja +24 -0
- generators/static/templates/auth/model/user_template.jinja +34 -0
- generators/static/templates/auth/model/value_objects_template.jinja +54 -0
- generators/static/templates/auth/presentation/login_screen_template.jinja +24 -0
- generators/static/templates/auth/sign_in_form/application/sign_in_form_bloc_template.jinja +139 -0
- generators/static/templates/auth/sign_in_form/application/sign_in_form_event_template.jinja +15 -0
- generators/static/templates/auth/sign_in_form/application/sign_in_form_state_template.jinja +26 -0
- generators/static/templates/auth/sign_in_form/presentation/apple_form_template.jinja +16 -0
- generators/static/templates/auth/sign_in_form/presentation/email_password_form_template_INACTIVE.jinja +94 -0
- generators/static/templates/auth/sign_in_form/presentation/google_form_template.jinja +15 -0
- generators/static/templates/auth/sign_in_form/presentation/sign_in_form_template.jinja +381 -0
- generators/static/templates/component/component_bloc_template.jinja +49 -0
- generators/static/templates/component/component_event_template.jinja +7 -0
- generators/static/templates/component/component_form_bloc_template.jinja +56 -0
- generators/static/templates/component/component_form_event_template.jinja +7 -0
- generators/static/templates/component/component_form_state_template.jinja +18 -0
- generators/static/templates/component/component_form_widget_template.jinja +130 -0
- generators/static/templates/component/component_list_widget_template.jinja +95 -0
- generators/static/templates/component/component_state_template.jinja +9 -0
- generators/static/templates/component/component_widget_template.jinja +113 -0
- generators/static/templates/core/bloc/base_form_bloc_template.jinja +32 -0
- generators/static/templates/core/errors/error_localizer_template.jinja +61 -0
- generators/static/templates/core/infrastructure/base_mapper_template.jinja +21 -0
- generators/static/templates/core/infrastructure/base_repository_mixin_template.jinja +78 -0
- generators/static/templates/core/infrastructure/firebase_injectable_module_template.jinja +11 -0
- generators/static/templates/core/infrastructure/firestore_helpers_template.jinja +22 -0
- generators/static/templates/core/infrastructure/repository_error_handler_template.jinja +44 -0
- generators/static/templates/core/model/common_interfaces_template.jinja +3 -0
- generators/static/templates/core/model/entity_template.jinja +5 -0
- generators/static/templates/core/model/errors_template.jinja +15 -0
- generators/static/templates/core/model/failures_template.jinja +97 -0
- generators/static/templates/core/model/value_objects_template.jinja +135 -0
- generators/static/templates/core/model/value_validators_template.jinja +105 -0
- generators/static/templates/core/presentation/app_drawer_template.jinja +52 -0
- generators/static/templates/core/presentation/app_widget_template.jinja +41 -0
- generators/static/templates/core/presentation/bottom_nav_bar_template.jinja +32 -0
- generators/static/templates/domain/domain_mapper_template.jinja +39 -0
- generators/static/templates/domain/domain_remote_service_template.jinja +46 -0
- generators/static/templates/domain/domain_repository_template.jinja +90 -0
- generators/static/templates/domain/domain_service_module_template.jinja +23 -0
- generators/static/templates/domain/enum_template.jinja +3 -0
- generators/static/templates/domain/i_domain_service_template.jinja +18 -0
- generators/static/templates/domain/mock_domain_service_template.jinja +73 -0
- generators/static/templates/feature/feature_bloc_template.jinja +104 -0
- generators/static/templates/feature/feature_dto_template.jinja +26 -0
- generators/static/templates/feature/feature_entity_template.jinja +23 -0
- generators/static/templates/feature/feature_event_template.jinja +20 -0
- generators/static/templates/feature/feature_extensions_template.jinja +25 -0
- generators/static/templates/feature/feature_failure_template.jinja +11 -0
- generators/static/templates/feature/feature_page_template.jinja +113 -0
- generators/static/templates/feature/feature_repository_template.jinja +112 -0
- generators/static/templates/feature/feature_state_template.jinja +20 -0
- generators/static/templates/feature/i_feature_repository_template.jinja +21 -0
- generators/static/templates/feature/value_object_field_template.jinja +0 -0
- generators/static/templates/feature/value_object_template.jinja +0 -0
- generators/static/templates/feature/value_validators_template.jinja +57 -0
- generators/static/templates/home/home_screen_template.jinja +68 -0
- generators/static/templates/home/presentation/home_screen_template.jinja +52 -0
- generators/static/templates/home/screen_template.jinja +12 -0
- generators/static/templates/injection_template.jinja +8 -0
- generators/static/templates/logging/analytics_logging_template.jinja +42 -0
- generators/static/templates/logging/console_template.jinja +50 -0
- generators/static/templates/logging/logger_injectable_module_template.jinja +24 -0
- generators/static/templates/logging/logger_template.jinja +118 -0
- generators/static/templates/main_template.jinja +43 -0
- generators/static/templates/page_template.jinja +29 -0
- generators/static/templates/router_template.jinja +26 -0
- generators/static/templates/splash/presentation/splash_screen_auth_template.jinja +97 -0
- generators/static/templates/splash/presentation/splash_screen_template.jinja +51 -0
- generators/static/templates/storage/storage_repository_template.jinja +80 -0
- generators/static/templates/user_profile/infrastructure/user_profile_dto_template.jinja +27 -0
- generators/static/templates/user_profile/infrastructure/user_profile_mapper_template.jinja +43 -0
- generators/static/templates/user_profile/infrastructure/user_profile_repository_template.jinja +82 -0
- generators/static/templates/user_profile/infrastructure/user_profile_service_template.jinja +27 -0
- generators/static/templates/user_profile/model/i_user_profile_repository_template.jinja +34 -0
- generators/static/templates/user_profile/model/user_profile_failure_template.jinja +11 -0
- generators/static/templates/user_profile/model/user_profile_template.jinja +31 -0
- generators/static/templates/widgets/common/custom_scaffold_template.jinja +52 -0
- generators/static/templates/widgets/common/error_widget_template.jinja +22 -0
- generators/static/templates/widgets/common/loading_widget_template.jinja +14 -0
- generators/static/templates/widgets/common/unknown_state_widget_template.jinja +23 -0
- generators/templates/__init__.py +1 -0
- generators/templates/_core/core_generator.py +121 -0
- generators/templates/apis/apis_generator.py +50 -0
- generators/templates/auth/auth_generator.py +40 -0
- generators/templates/auth/sign_in_form_generator.py +22 -0
- generators/templates/copier.py +51 -0
- generators/templates/home/home_generator.py +26 -0
- generators/templates/lib/lib_generator.py +40 -0
- generators/templates/logging/logging_generator.py +28 -0
- generators/templates/main.py +75 -0
- generators/templates/splash/splash_generator.py +20 -0
- generators/templates/storage/storage_generator.py +5 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import 'package:dartz/dartz.dart';
|
|
2
|
+
import 'package:injectable/injectable.dart';
|
|
3
|
+
import 'package:[[project_name]]/storage/storage_repository.dart';
|
|
4
|
+
import 'package:[[project_name]]/domain/auth/model/value_objects.dart';
|
|
5
|
+
import 'package:[[project_name]]/domain/auth/model/user.dart' as auth_user;
|
|
6
|
+
import 'package:[[project_name]]/domain/auth/model/auth_failure.dart';
|
|
7
|
+
import 'package:[[project_name]]/domain/auth/model/i_auth_facade.dart';
|
|
8
|
+
|
|
9
|
+
/// NOTE: This class is currently a skeleton implementation. Uncomment and implement
|
|
10
|
+
/// the necessary parts to enable authentication functionality.
|
|
11
|
+
///
|
|
12
|
+
/// This facade supports multiple authentication providers (Firebase, Google, Apple, etc.).
|
|
13
|
+
/// Implement the specific authentication logic for your chosen provider(s).
|
|
14
|
+
///
|
|
15
|
+
/// To avoid circular dependency with IUserProfileRepository (UserProfileRepository
|
|
16
|
+
/// depends on IAuthFacade), AuthFacade does NOT inject IUserProfileRepository.
|
|
17
|
+
/// When implementing getSignedInUser/sign-in flows that need to sync UserProfile,
|
|
18
|
+
/// resolve IUserProfileRepository lazily (e.g. via GetIt) only when needed.
|
|
19
|
+
|
|
20
|
+
@lazySingleton
|
|
21
|
+
class AuthFacade implements IAuthFacade {
|
|
22
|
+
// final FirebaseAuth _firebaseAuth; // Uncomment the following line to use Firebase Authentication
|
|
23
|
+
// final FirebaseUserMapper _firebaseUserMapper; // Uncomment the following line to use Firebase Authentication
|
|
24
|
+
final StorageRepository _storageRepository;
|
|
25
|
+
|
|
26
|
+
AuthFacade(
|
|
27
|
+
// this._firebaseAuth, // Uncomment the following line to use Firebase Authentication
|
|
28
|
+
// this._firebaseUserMapper, // Uncomment the following line to use Firebase Authentication
|
|
29
|
+
this._storageRepository,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
@override
|
|
33
|
+
Future<Option<auth_user.User>> getSignedInUser() async {
|
|
34
|
+
// Step 1: Get auth user from authentication provider
|
|
35
|
+
// Uncomment the following lines to use Firebase Authentication
|
|
36
|
+
// final firebaseUser = _firebaseAuth.currentUser;
|
|
37
|
+
// if (firebaseUser == null) {
|
|
38
|
+
// return none();
|
|
39
|
+
// }
|
|
40
|
+
// final authUser = _firebaseUserMapper.toDomain(firebaseUser);
|
|
41
|
+
// if (authUser == null) {
|
|
42
|
+
// return none();
|
|
43
|
+
// }
|
|
44
|
+
|
|
45
|
+
// Stub: For now, return none() until authentication provider is implemented
|
|
46
|
+
// TODO: Replace with actual authentication provider user retrieval
|
|
47
|
+
// final authUser = ...;
|
|
48
|
+
|
|
49
|
+
// Step 2: Get or create UserProfile using authId
|
|
50
|
+
// The UserProfile is synced with the authentication User
|
|
51
|
+
// Both use the same ID (authId) for correlation
|
|
52
|
+
// try {
|
|
53
|
+
// final authId = authUser.id.getOrCrash();
|
|
54
|
+
// final userProfileResult = await _userProfileRepository.getByAuthId(authId);
|
|
55
|
+
//
|
|
56
|
+
// final UserProfile userProfile = userProfileResult.fold(
|
|
57
|
+
// (failure) {
|
|
58
|
+
// // UserProfile doesn't exist, create it from auth User
|
|
59
|
+
// final newUserProfile = authUser.toUserProfile();
|
|
60
|
+
// return newUserProfile;
|
|
61
|
+
// },
|
|
62
|
+
// (existingProfile) {
|
|
63
|
+
// // UserProfile exists, update it with latest auth data if needed
|
|
64
|
+
// // Check if name or email changed
|
|
65
|
+
// if (existingProfile.name.getOrCrash() != authUser.name.getOrCrash() ||
|
|
66
|
+
// existingProfile.emailAddress.getOrCrash() != authUser.emailAddress.getOrCrash()) {
|
|
67
|
+
// final updatedProfile = authUser.toUserProfileWithTimestamps(
|
|
68
|
+
// createdAt: existingProfile.createdAt,
|
|
69
|
+
// updatedAt: DateTime.now(),
|
|
70
|
+
// avatarUrl: existingProfile.avatarUrl,
|
|
71
|
+
// );
|
|
72
|
+
// return updatedProfile;
|
|
73
|
+
// }
|
|
74
|
+
// return existingProfile;
|
|
75
|
+
// },
|
|
76
|
+
// );
|
|
77
|
+
//
|
|
78
|
+
// // Save/update UserProfile if it was created or updated
|
|
79
|
+
// if (userProfileResult.isLeft()) {
|
|
80
|
+
// await _userProfileRepository.createOrUpdate(userProfile);
|
|
81
|
+
// } else {
|
|
82
|
+
// // Check if update is needed
|
|
83
|
+
// final needsUpdate = userProfile.name.getOrCrash() != authUser.name.getOrCrash() ||
|
|
84
|
+
// userProfile.emailAddress.getOrCrash() != authUser.emailAddress.getOrCrash();
|
|
85
|
+
// if (needsUpdate) {
|
|
86
|
+
// await _userProfileRepository.createOrUpdate(userProfile);
|
|
87
|
+
// }
|
|
88
|
+
// }
|
|
89
|
+
//
|
|
90
|
+
// // Step 3: Return UserProfile mapped to User (for AuthBloc compatibility)
|
|
91
|
+
// return optionOf(userProfile.toUser());
|
|
92
|
+
// } catch (e) {
|
|
93
|
+
// // If UserProfile sync fails, fall back to auth User
|
|
94
|
+
// return optionOf(authUser);
|
|
95
|
+
// }
|
|
96
|
+
|
|
97
|
+
return none();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@override
|
|
101
|
+
Future<Either<AuthFailure, Unit>> registerWithEmailAndPassword({
|
|
102
|
+
required EmailAddress emailAddress,
|
|
103
|
+
required Password password,
|
|
104
|
+
}) async {
|
|
105
|
+
final String emailAddressStr = emailAddress.value.getOrElse(() => throw 'INVALID EMAIL');
|
|
106
|
+
final String passwordStr = password.value.getOrElse(() => throw 'INVALID PASSWORD');
|
|
107
|
+
|
|
108
|
+
// Uncomment the following code to implement Email/Password Registration with Firebase Authentication
|
|
109
|
+
|
|
110
|
+
// try {
|
|
111
|
+
// final userCredential = await _firebaseAuth.createUserWithEmailAndPassword(
|
|
112
|
+
// email: emailAddressStr,
|
|
113
|
+
// password: passwordStr,
|
|
114
|
+
// );
|
|
115
|
+
|
|
116
|
+
// // Uncomment to save the token if needed
|
|
117
|
+
|
|
118
|
+
// // // User registered and logged
|
|
119
|
+
// // final user = userCredential.user;
|
|
120
|
+
|
|
121
|
+
// // // Get ID token (JWT)
|
|
122
|
+
// // final idToken = await user?.getIdToken();
|
|
123
|
+
|
|
124
|
+
// // if (idToken != null) {
|
|
125
|
+
// // await _storageRepository.saveToken(idToken);
|
|
126
|
+
// // }
|
|
127
|
+
|
|
128
|
+
// // Create UserProfile after successful registration
|
|
129
|
+
// // The UserProfile is automatically synced with the authentication User
|
|
130
|
+
// // Both use the same ID (authId) for correlation
|
|
131
|
+
// // if (user != null) {
|
|
132
|
+
// // final authUser = _firebaseUserMapper.toDomain(user);
|
|
133
|
+
// // if (authUser != null) {
|
|
134
|
+
// // final userProfile = authUser.toUserProfile();
|
|
135
|
+
// // await _userProfileRepository.createOrUpdate(userProfile);
|
|
136
|
+
// // }
|
|
137
|
+
// // }
|
|
138
|
+
|
|
139
|
+
// return right(unit);
|
|
140
|
+
// } on PlatformException catch (e) {
|
|
141
|
+
// if (e.code == __STRING_12__) {
|
|
142
|
+
// return left(const AuthFailure.emailAlreadyInUse());
|
|
143
|
+
// } else {
|
|
144
|
+
// return left(const AuthFailure.serverError());
|
|
145
|
+
// }
|
|
146
|
+
// }
|
|
147
|
+
|
|
148
|
+
return right(unit);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@override
|
|
152
|
+
Future<Either<AuthFailure, Unit>> signInWithEmailAndPassword({
|
|
153
|
+
required EmailAddress emailAddress,
|
|
154
|
+
required Password password,
|
|
155
|
+
}) async {
|
|
156
|
+
final String emailAddressStr = emailAddress.value.getOrElse(() => throw 'INVALID EMAIL');
|
|
157
|
+
final String passwordStr = password.value.getOrElse(() => throw 'INVALID PASSWORD');
|
|
158
|
+
|
|
159
|
+
// Uncomment the following code to implement Email/Password Sign-In with Firebase Authentication
|
|
160
|
+
|
|
161
|
+
// try {
|
|
162
|
+
// final userCredential = await _firebaseAuth.signInWithEmailAndPassword(
|
|
163
|
+
// email: emailAddressStr,
|
|
164
|
+
// password: passwordStr,
|
|
165
|
+
// );
|
|
166
|
+
|
|
167
|
+
// // Uncomment to save the token if needed
|
|
168
|
+
|
|
169
|
+
// // // User logged
|
|
170
|
+
// // final user = userCredential.user;
|
|
171
|
+
|
|
172
|
+
// // // Get ID token (JWT)
|
|
173
|
+
// // final idToken = await user?.getIdToken();
|
|
174
|
+
|
|
175
|
+
// // if (idToken != null) {
|
|
176
|
+
// // await _storageRepository.saveToken(idToken);
|
|
177
|
+
// // }
|
|
178
|
+
|
|
179
|
+
// // Sync UserProfile after successful sign-in
|
|
180
|
+
// // The UserProfile is automatically synced with the authentication User
|
|
181
|
+
// // Both use the same ID (authId) for correlation
|
|
182
|
+
// // if (user != null) {
|
|
183
|
+
// // final authUser = _firebaseUserMapper.toDomain(user);
|
|
184
|
+
// // if (authUser != null) {
|
|
185
|
+
// // final authId = authUser.id.getOrCrash();
|
|
186
|
+
// // final userProfileResult = await _userProfileRepository.getByAuthId(authId);
|
|
187
|
+
// //
|
|
188
|
+
// // final UserProfile userProfile = userProfileResult.fold(
|
|
189
|
+
// // (failure) {
|
|
190
|
+
// // // UserProfile doesn't exist, create it from auth User
|
|
191
|
+
// // return authUser.toUserProfile();
|
|
192
|
+
// // },
|
|
193
|
+
// // (existingProfile) {
|
|
194
|
+
// // // UserProfile exists, update it with latest auth data if needed
|
|
195
|
+
// // if (existingProfile.name.getOrCrash() != authUser.name.getOrCrash() ||
|
|
196
|
+
// // existingProfile.emailAddress.getOrCrash() != authUser.emailAddress.getOrCrash()) {
|
|
197
|
+
// // return authUser.toUserProfileWithTimestamps(
|
|
198
|
+
// // createdAt: existingProfile.createdAt,
|
|
199
|
+
// // updatedAt: DateTime.now(),
|
|
200
|
+
// // avatarUrl: existingProfile.avatarUrl,
|
|
201
|
+
// // );
|
|
202
|
+
// // }
|
|
203
|
+
// // return existingProfile;
|
|
204
|
+
// // },
|
|
205
|
+
// // );
|
|
206
|
+
// //
|
|
207
|
+
// // // Save/update UserProfile
|
|
208
|
+
// // await _userProfileRepository.createOrUpdate(userProfile);
|
|
209
|
+
// // }
|
|
210
|
+
// // }
|
|
211
|
+
|
|
212
|
+
// return right(unit);
|
|
213
|
+
// } on PlatformException catch (e) {
|
|
214
|
+
// if (e.code == __STRING_15__ || e.code == __STRING_16__) {
|
|
215
|
+
// return left(const AuthFailure.invalidEmailAndPasswordCombination());
|
|
216
|
+
// }
|
|
217
|
+
// return left(const AuthFailure.serverError());
|
|
218
|
+
// }
|
|
219
|
+
|
|
220
|
+
return right(unit);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@override
|
|
224
|
+
Future<Either<AuthFailure, Unit>> signInWithGoogle() async {
|
|
225
|
+
// TODO: Implement Google Sign-In
|
|
226
|
+
// Example implementation with Firebase:
|
|
227
|
+
// try {
|
|
228
|
+
// final GoogleSignIn googleSignIn = GoogleSignIn();
|
|
229
|
+
// final GoogleSignInAccount? googleUser = await googleSignIn.signIn();
|
|
230
|
+
// if (googleUser == null) {
|
|
231
|
+
// return left(const AuthFailure.cancelledByUser());
|
|
232
|
+
// }
|
|
233
|
+
// final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
|
|
234
|
+
// final credential = GoogleAuthProvider.credential(
|
|
235
|
+
// accessToken: googleAuth.accessToken,
|
|
236
|
+
// idToken: googleAuth.idToken,
|
|
237
|
+
// );
|
|
238
|
+
// await _firebaseAuth.signInWithCredential(credential);
|
|
239
|
+
// // Sync UserProfile similar to signInWithEmailAndPassword
|
|
240
|
+
// return right(unit);
|
|
241
|
+
// } catch (e) {
|
|
242
|
+
// return left(const AuthFailure.serverError());
|
|
243
|
+
// }
|
|
244
|
+
return right(unit);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
@override
|
|
248
|
+
Future<Either<AuthFailure, Unit>> signInWithApple() async {
|
|
249
|
+
// TODO: Implement Apple Sign-In
|
|
250
|
+
// Example implementation with Firebase:
|
|
251
|
+
// try {
|
|
252
|
+
// final appleProvider = AppleAuthProvider();
|
|
253
|
+
// final userCredential = await _firebaseAuth.signInWithProvider(appleProvider);
|
|
254
|
+
// // Sync UserProfile similar to signInWithEmailAndPassword
|
|
255
|
+
// return right(unit);
|
|
256
|
+
// } catch (e) {
|
|
257
|
+
// if (e.code == 'sign_in_canceled') {
|
|
258
|
+
// return left(const AuthFailure.cancelledByUser());
|
|
259
|
+
// }
|
|
260
|
+
// return left(const AuthFailure.serverError());
|
|
261
|
+
// }
|
|
262
|
+
return right(unit);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@override
|
|
266
|
+
Future<Either<AuthFailure, Unit>> registerWithGoogle() async {
|
|
267
|
+
// TODO: Implement Google Registration
|
|
268
|
+
// For Google Sign-In, registration and sign-in are typically the same flow
|
|
269
|
+
// You can reuse the signInWithGoogle implementation or create a unified method
|
|
270
|
+
return await signInWithGoogle();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
@override
|
|
274
|
+
Future<Either<AuthFailure, Unit>> registerWithApple() async {
|
|
275
|
+
// TODO: Implement Apple Registration
|
|
276
|
+
// For Apple Sign-In, registration and sign-in are typically the same flow
|
|
277
|
+
// You can reuse the signInWithApple implementation or create a unified method
|
|
278
|
+
return await signInWithApple();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@override
|
|
282
|
+
Future<void> signOut() async {
|
|
283
|
+
await Future.wait(<Future<void>>[
|
|
284
|
+
// _firebaseAuth.signOut(), // Uncomment the following line to use Firebase Authentication
|
|
285
|
+
_storageRepository.removeToken(),
|
|
286
|
+
]);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import 'dart:convert';
|
|
2
|
+
|
|
3
|
+
import 'package:dartz/dartz.dart';
|
|
4
|
+
import 'package:flutter/services.dart';
|
|
5
|
+
import 'package:injectable/injectable.dart';
|
|
6
|
+
import 'package:shared_preferences/shared_preferences.dart';
|
|
7
|
+
import 'package:[[project_name]]/core/model/value_objects.dart';
|
|
8
|
+
import 'package:[[project_name]]/domain/auth/model/auth_failure.dart';
|
|
9
|
+
import 'package:[[project_name]]/domain/auth/model/i_auth_facade.dart';
|
|
10
|
+
import 'package:[[project_name]]/domain/auth/model/user.dart' as auth_user;
|
|
11
|
+
import 'package:[[project_name]]/domain/auth/model/value_objects.dart';
|
|
12
|
+
import 'package:[[project_name]]/storage/storage_repository.dart';
|
|
13
|
+
|
|
14
|
+
/// Mock authentication — any sign-in succeeds using [assets/mock/auth_user.json].
|
|
15
|
+
@lazySingleton
|
|
16
|
+
class MockAuthFacade implements IAuthFacade {
|
|
17
|
+
static const String _assetPath = 'assets/mock/auth_user.json';
|
|
18
|
+
static const String _userPrefsKey = 'mock_auth_user_json';
|
|
19
|
+
|
|
20
|
+
final StorageRepository _storageRepository;
|
|
21
|
+
|
|
22
|
+
MockAuthFacade(this._storageRepository);
|
|
23
|
+
|
|
24
|
+
Future<auth_user.User> _seedUser() async {
|
|
25
|
+
final String raw = await rootBundle.loadString(_assetPath);
|
|
26
|
+
final Map<String, dynamic> json = jsonDecode(raw) as Map<String, dynamic>;
|
|
27
|
+
return auth_user.User(
|
|
28
|
+
id: UniqueId.fromUniqueString(json['id'] as String),
|
|
29
|
+
name: StringSingleLine(json['name'] as String),
|
|
30
|
+
emailAddress: EmailAddress(json['emailAddress'] as String),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Future<void> _persistUser(auth_user.User user) async {
|
|
35
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
36
|
+
await prefs.setString(
|
|
37
|
+
_userPrefsKey,
|
|
38
|
+
jsonEncode(<String, String>{
|
|
39
|
+
'id': user.id.getOrCrash(),
|
|
40
|
+
'name': user.name.getOrCrash(),
|
|
41
|
+
'emailAddress': user.emailAddress.getOrCrash(),
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
await _storageRepository.saveToken(
|
|
45
|
+
const TokenData(token: 'mock-access-token', refreshToken: 'mock-refresh-token'),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Future<Option<auth_user.User>> _readUser() async {
|
|
50
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
51
|
+
final String? raw = prefs.getString(_userPrefsKey);
|
|
52
|
+
if (raw == null || raw.isEmpty) {
|
|
53
|
+
return none();
|
|
54
|
+
}
|
|
55
|
+
final Map<String, dynamic> json = jsonDecode(raw) as Map<String, dynamic>;
|
|
56
|
+
return some(
|
|
57
|
+
auth_user.User(
|
|
58
|
+
id: UniqueId.fromUniqueString(json['id'] as String),
|
|
59
|
+
name: StringSingleLine(json['name'] as String),
|
|
60
|
+
emailAddress: EmailAddress(json['emailAddress'] as String),
|
|
61
|
+
),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Future<Either<AuthFailure, Unit>> _signIn() async {
|
|
66
|
+
final auth_user.User user = await _seedUser();
|
|
67
|
+
await _persistUser(user);
|
|
68
|
+
return right(unit);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@override
|
|
72
|
+
Future<Option<auth_user.User>> getSignedInUser() => _readUser();
|
|
73
|
+
|
|
74
|
+
@override
|
|
75
|
+
Future<Either<AuthFailure, Unit>> registerWithEmailAndPassword({
|
|
76
|
+
required EmailAddress emailAddress,
|
|
77
|
+
required Password password,
|
|
78
|
+
}) =>
|
|
79
|
+
_signIn();
|
|
80
|
+
|
|
81
|
+
@override
|
|
82
|
+
Future<Either<AuthFailure, Unit>> signInWithEmailAndPassword({
|
|
83
|
+
required EmailAddress emailAddress,
|
|
84
|
+
required Password password,
|
|
85
|
+
}) =>
|
|
86
|
+
_signIn();
|
|
87
|
+
|
|
88
|
+
@override
|
|
89
|
+
Future<Either<AuthFailure, Unit>> signInWithGoogle() => _signIn();
|
|
90
|
+
|
|
91
|
+
@override
|
|
92
|
+
Future<Either<AuthFailure, Unit>> signInWithApple() => _signIn();
|
|
93
|
+
|
|
94
|
+
@override
|
|
95
|
+
Future<Either<AuthFailure, Unit>> registerWithGoogle() => _signIn();
|
|
96
|
+
|
|
97
|
+
@override
|
|
98
|
+
Future<Either<AuthFailure, Unit>> registerWithApple() => _signIn();
|
|
99
|
+
|
|
100
|
+
@override
|
|
101
|
+
Future<void> signOut() async {
|
|
102
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
103
|
+
await prefs.remove(_userPrefsKey);
|
|
104
|
+
await _storageRepository.removeToken();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
2
|
+
|
|
3
|
+
part 'auth_failure.freezed.dart';
|
|
4
|
+
|
|
5
|
+
@freezed
|
|
6
|
+
abstract class AuthFailure with _$AuthFailure {
|
|
7
|
+
const factory AuthFailure.cancelledByUser() = CancelledByUser;
|
|
8
|
+
const factory AuthFailure.serverError() = ServerError;
|
|
9
|
+
const factory AuthFailure.emailAlreadyInUse() = EmailAlreadyInUse;
|
|
10
|
+
const factory AuthFailure.invalidEmailAndPasswordCombination() = InvalidEmailAndPasswordCombination;
|
|
11
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import 'package:dartz/dartz.dart';
|
|
2
|
+
import 'package:[[project_name]]/domain/auth/model/auth_failure.dart';
|
|
3
|
+
import 'package:[[project_name]]/domain/auth/model/user.dart';
|
|
4
|
+
import 'package:[[project_name]]/domain/auth/model/value_objects.dart';
|
|
5
|
+
|
|
6
|
+
abstract class IAuthFacade {
|
|
7
|
+
/// Get the currently signed-in user
|
|
8
|
+
/// Returns User mapped from UserProfile (database entity)
|
|
9
|
+
/// The UserProfile is automatically synced with the authentication provider
|
|
10
|
+
Future<Option<User>> getSignedInUser();
|
|
11
|
+
Future<Either<AuthFailure, Unit>> registerWithEmailAndPassword({
|
|
12
|
+
required EmailAddress emailAddress,
|
|
13
|
+
required Password password,
|
|
14
|
+
});
|
|
15
|
+
Future<Either<AuthFailure, Unit>> signInWithEmailAndPassword({
|
|
16
|
+
required EmailAddress emailAddress,
|
|
17
|
+
required Password password,
|
|
18
|
+
});
|
|
19
|
+
Future<Either<AuthFailure, Unit>> signInWithGoogle();
|
|
20
|
+
Future<Either<AuthFailure, Unit>> signInWithApple();
|
|
21
|
+
Future<Either<AuthFailure, Unit>> registerWithGoogle();
|
|
22
|
+
Future<Either<AuthFailure, Unit>> registerWithApple();
|
|
23
|
+
Future<void> signOut();
|
|
24
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* User entity (authentication)
|
|
3
|
+
*
|
|
4
|
+
* NOTE: This entity is used by AuthBloc and the authentication flow.
|
|
5
|
+
* It is mapped from UserProfile (database entity) which contains additional fields.
|
|
6
|
+
*
|
|
7
|
+
* Flow: Auth → UserProfile sync → User (mapped)
|
|
8
|
+
* Both entities use the same ID (authId) for correlation.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import 'package:dartz/dartz.dart';
|
|
12
|
+
import 'package:flutter/foundation.dart';
|
|
13
|
+
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
14
|
+
import 'package:[[project_name]]/domain/auth/model/value_objects.dart';
|
|
15
|
+
import 'package:[[project_name]]/core/model/entity.dart';
|
|
16
|
+
import 'package:[[project_name]]/core/model/failures.dart';
|
|
17
|
+
import 'package:[[project_name]]/core/model/value_objects.dart';
|
|
18
|
+
|
|
19
|
+
part 'user.freezed.dart';
|
|
20
|
+
|
|
21
|
+
@freezed
|
|
22
|
+
abstract class User with _$User implements IEntity {
|
|
23
|
+
const factory User({
|
|
24
|
+
required UniqueId id,
|
|
25
|
+
required StringSingleLine name,
|
|
26
|
+
required EmailAddress emailAddress,
|
|
27
|
+
}) = _User;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
extension UserX on User {
|
|
31
|
+
Option<ValueFailure<dynamic>> get failureOption {
|
|
32
|
+
return name.failureOrUnit.andThen(emailAddress.failureOrUnit).fold((ValueFailure<dynamic> l) => some(l), (Unit r) => none());
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import 'package:dartz/dartz.dart';
|
|
2
|
+
import 'package:[[project_name]]/core/model/failures.dart';
|
|
3
|
+
import 'package:[[project_name]]/core/model/value_objects.dart';
|
|
4
|
+
import 'package:[[project_name]]/core/model/value_validators.dart';
|
|
5
|
+
|
|
6
|
+
class EmailAddress extends ValueObject<String> {
|
|
7
|
+
@override
|
|
8
|
+
final Either<ValueFailure<String>, String> value;
|
|
9
|
+
|
|
10
|
+
factory EmailAddress(String input) {
|
|
11
|
+
return EmailAddress._(
|
|
12
|
+
validateEmailAddress(input),
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const EmailAddress._(this.value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class Password extends ValueObject<String> {
|
|
20
|
+
@override
|
|
21
|
+
final Either<ValueFailure<String>, String> value;
|
|
22
|
+
|
|
23
|
+
factory Password(String input) {
|
|
24
|
+
return Password._(
|
|
25
|
+
validatePassword(input),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
String get rawValue {
|
|
30
|
+
return value.fold((ValueFailure<String> l) => l.failedValue, (String r) => r);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const Password._(this.value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class ConfirmPassword extends ValueObject<String> {
|
|
37
|
+
@override
|
|
38
|
+
final Either<ValueFailure<String>, String> value;
|
|
39
|
+
|
|
40
|
+
factory ConfirmPassword(
|
|
41
|
+
String password,
|
|
42
|
+
String confirmPassword,
|
|
43
|
+
) {
|
|
44
|
+
return ConfirmPassword._(
|
|
45
|
+
validateConfirmPassword(password, confirmPassword),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
String get rawValue {
|
|
50
|
+
return value.fold((ValueFailure<String> l) => l.failedValue, (String r) => r);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const ConfirmPassword._(this.value);
|
|
54
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
3
|
+
import 'package:[[project_name]]/features/auth/sign_in_form/application/sign_in_form_bloc.dart';
|
|
4
|
+
import 'package:[[project_name]]/injection.dart';
|
|
5
|
+
import 'package:[[project_name]]/features/auth/sign_in_form/presentation/sign_in_form.dart';
|
|
6
|
+
import 'package:[[project_name]]/domain/auth/model/i_auth_facade.dart';
|
|
7
|
+
|
|
8
|
+
class LoginPage extends StatelessWidget {
|
|
9
|
+
static const String routeName = '/login';
|
|
10
|
+
static const String logoHeroTag = 'auth-logo';
|
|
11
|
+
|
|
12
|
+
const LoginPage({super.key});
|
|
13
|
+
|
|
14
|
+
@override
|
|
15
|
+
Widget build(BuildContext context) {
|
|
16
|
+
return Scaffold(
|
|
17
|
+
backgroundColor: Colors.transparent,
|
|
18
|
+
body: BlocProvider<SignInFormBloc>(
|
|
19
|
+
create: (BuildContext context) => SignInFormBloc(getIt<IAuthFacade>()),
|
|
20
|
+
child: const SignInForm(),
|
|
21
|
+
),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
}
|