@@ -74,9 +74,9 @@ class AuthService {
74
74
/// Throws [OperationFailedException] for user lookup/creation or token errors.
75
75
Future <({User user, String token})> completeEmailSignIn (
76
76
String email,
77
- String code,
78
- // User? currentAuthUser, // Parameter for potential future linking logic
79
- ) async {
77
+ String code, {
78
+ User ? currentAuthUser, // Parameter for potential future linking logic
79
+ } ) async {
80
80
// 1. Validate the code for standard sign-in
81
81
final isValidCode = await _verificationCodeStorageService
82
82
.validateSignInCode (email, code);
@@ -96,53 +96,141 @@ class AuthService {
96
96
);
97
97
}
98
98
99
- // 2. Find or create the user
99
+ // 2. Find or create the user, and migrate data if anonymous
100
100
User user;
101
101
try {
102
- // Attempt to find user by email (assuming a query method exists)
103
- // NOTE: HtDataRepository<User> currently lacks findByEmail.
104
- // We'll simulate this by querying all and filtering for now.
105
- // Replace with a proper query when available.
106
- final query = {'email' : email}; // Hypothetical query
107
- final paginatedResponse = await _userRepository.readAllByQuery (query);
108
-
109
- if (paginatedResponse.items.isNotEmpty) {
110
- user = paginatedResponse.items.first;
111
- print ('Found existing user: ${user .id } for email $email ' );
112
- } else {
113
- // User not found, create a new one
114
- print ('User not found for $email , creating new user.' );
115
- user = User (
116
- id: _uuid.v4 (), // Generate new ID
102
+ if (currentAuthUser != null &&
103
+ currentAuthUser.role == UserRole .guestUser) {
104
+ // This is an anonymous user linking their account.
105
+ // Migrate their existing data to the new permanent user.
106
+ print (
107
+ 'Anonymous user ${currentAuthUser .id } is linking email $email . '
108
+ 'Migrating data...' ,
109
+ );
110
+
111
+ // Fetch existing settings and preferences for the anonymous user
112
+ UserAppSettings ? existingAppSettings;
113
+ UserContentPreferences ? existingUserPreferences;
114
+ try {
115
+ existingAppSettings = await _userAppSettingsRepository.read (
116
+ id: currentAuthUser.id,
117
+ userId: currentAuthUser.id,
118
+ );
119
+ existingUserPreferences = await _userContentPreferencesRepository
120
+ .read (id: currentAuthUser.id, userId: currentAuthUser.id);
121
+ print (
122
+ 'Fetched existing settings and preferences for anonymous user '
123
+ '${currentAuthUser .id }.' ,
124
+ );
125
+ } on NotFoundException {
126
+ print (
127
+ 'No existing settings/preferences found for anonymous user '
128
+ '${currentAuthUser .id }. Creating new ones.' ,
129
+ );
130
+ // If not found, proceed to create new ones later.
131
+ } catch (e) {
132
+ print (
133
+ 'Error fetching existing settings/preferences for anonymous user '
134
+ '${currentAuthUser .id }: $e ' ,
135
+ );
136
+ // Log and continue, new defaults will be created.
137
+ }
138
+
139
+ // Update the existing anonymous user to be permanent
140
+ user = currentAuthUser.copyWith (
117
141
email: email,
118
- role: UserRole .standardUser, // Email verified user is standard user
142
+ role: UserRole .standardUser,
119
143
);
120
- user = await _userRepository.create (item: user); // Save the new user
121
- print ('Created new user: ${user .id }' );
122
-
123
- // Create default UserAppSettings for the new user
124
- final defaultAppSettings = UserAppSettings (id: user.id);
125
- await _userAppSettingsRepository.create (
126
- item: defaultAppSettings,
127
- userId: user.id, // Pass user ID for scoping
144
+ user = await _userRepository.update (id: user.id, item: user);
145
+ print (
146
+ 'Updated anonymous user ${user .id } to permanent with email $email .' ,
128
147
);
129
- print ('Created default UserAppSettings for user: ${user .id }' );
130
148
131
- // Create default UserContentPreferences for the new user
132
- final defaultUserPreferences = UserContentPreferences (id: user.id);
133
- await _userContentPreferencesRepository.create (
134
- item: defaultUserPreferences,
135
- userId: user.id, // Pass user ID for scoping
136
- );
137
- print ('Created default UserContentPreferences for user: ${user .id }' );
149
+ // Update or create UserAppSettings for the now-permanent user
150
+ if (existingAppSettings != null ) {
151
+ // Update existing settings with the new user ID (though it's the same)
152
+ // and persist.
153
+ await _userAppSettingsRepository.update (
154
+ id: existingAppSettings.id,
155
+ item: existingAppSettings.copyWith (id: user.id),
156
+ userId: user.id,
157
+ );
158
+ print ('Migrated UserAppSettings for user: ${user .id }' );
159
+ } else {
160
+ // Create default settings if none existed for the anonymous user
161
+ final defaultAppSettings = UserAppSettings (id: user.id);
162
+ await _userAppSettingsRepository.create (
163
+ item: defaultAppSettings,
164
+ userId: user.id,
165
+ );
166
+ print ('Created default UserAppSettings for user: ${user .id }' );
167
+ }
168
+
169
+ // Update or create UserContentPreferences for the now-permanent user
170
+ if (existingUserPreferences != null ) {
171
+ // Update existing preferences with the new user ID (though it's the same)
172
+ // and persist.
173
+ await _userContentPreferencesRepository.update (
174
+ id: existingUserPreferences.id,
175
+ item: existingUserPreferences.copyWith (id: user.id),
176
+ userId: user.id,
177
+ );
178
+ print ('Migrated UserContentPreferences for user: ${user .id }' );
179
+ } else {
180
+ // Create default preferences if none existed for the anonymous user
181
+ final defaultUserPreferences = UserContentPreferences (id: user.id);
182
+ await _userContentPreferencesRepository.create (
183
+ item: defaultUserPreferences,
184
+ userId: user.id,
185
+ );
186
+ print ('Created default UserContentPreferences for user: ${user .id }' );
187
+ }
188
+ } else {
189
+ // Standard sign-in/sign-up flow (not anonymous linking)
190
+ // Attempt to find user by email
191
+ final query = {'email' : email};
192
+ final paginatedResponse = await _userRepository.readAllByQuery (query);
193
+
194
+ if (paginatedResponse.items.isNotEmpty) {
195
+ user = paginatedResponse.items.first;
196
+ print ('Found existing user: ${user .id } for email $email ' );
197
+ } else {
198
+ // User not found, create a new one
199
+ print ('User not found for $email , creating new user.' );
200
+ user = User (
201
+ id: _uuid.v4 (), // Generate new ID
202
+ email: email,
203
+ role: UserRole .standardUser, // Email verified user is standard user
204
+ );
205
+ user = await _userRepository.create (item: user); // Save the new user
206
+ print ('Created new user: ${user .id }' );
207
+
208
+ // Create default UserAppSettings for the new user
209
+ final defaultAppSettings = UserAppSettings (id: user.id);
210
+ await _userAppSettingsRepository.create (
211
+ item: defaultAppSettings,
212
+ userId: user.id, // Pass user ID for scoping
213
+ );
214
+ print ('Created default UserAppSettings for user: ${user .id }' );
215
+
216
+ // Create default UserContentPreferences for the new user
217
+ final defaultUserPreferences = UserContentPreferences (id: user.id);
218
+ await _userContentPreferencesRepository.create (
219
+ item: defaultUserPreferences,
220
+ userId: user.id, // Pass user ID for scoping
221
+ );
222
+ print ('Created default UserContentPreferences for user: ${user .id }' );
223
+ }
138
224
}
139
225
} on HtHttpException catch (e) {
140
- print ('Error finding/creating user for $email : $e ' );
226
+ print ('Error finding/creating/migrating user for $email : $e ' );
141
227
throw const OperationFailedException (
142
- 'Failed to find or create user account.' ,
228
+ 'Failed to find, create, or migrate user account.' ,
143
229
);
144
230
} catch (e) {
145
- print ('Unexpected error during user lookup/creation for $email : $e ' );
231
+ print (
232
+ 'Unexpected error during user lookup/creation/migration for $email : $e ' ,
233
+ );
146
234
throw const OperationFailedException ('Failed to process user account.' );
147
235
}
148
236
0 commit comments