@@ -39,14 +39,14 @@ class AppDependencies {
39
39
40
40
// --- Repositories ---
41
41
late final HtDataRepository <Headline > headlineRepository;
42
- late final HtDataRepository <Category > categoryRepository ;
42
+ late final HtDataRepository <Topic > topicRepository ;
43
43
late final HtDataRepository <Source > sourceRepository;
44
44
late final HtDataRepository <Country > countryRepository;
45
45
late final HtDataRepository <User > userRepository;
46
46
late final HtDataRepository <UserAppSettings > userAppSettingsRepository;
47
47
late final HtDataRepository <UserContentPreferences >
48
- userContentPreferencesRepository;
49
- late final HtDataRepository <AppConfig > appConfigRepository ;
48
+ userContentPreferencesRepository;
49
+ late final HtDataRepository <RemoteConfig > remoteConfigRepository ;
50
50
51
51
// --- Services ---
52
52
late final HtEmailRepository emailRepository;
@@ -100,178 +100,102 @@ class AppDependencies {
100
100
headlineRepository = _createRepository (
101
101
connection,
102
102
'headlines' ,
103
- (json) {
104
- if (json['created_at' ] is DateTime ) {
105
- json['created_at' ] =
106
- (json['created_at' ] as DateTime ).toIso8601String ();
107
- }
108
- if (json['updated_at' ] is DateTime ) {
109
- json['updated_at' ] =
110
- (json['updated_at' ] as DateTime ).toIso8601String ();
111
- }
112
- if (json['published_at' ] is DateTime ) {
113
- json['published_at' ] =
114
- (json['published_at' ] as DateTime ).toIso8601String ();
115
- }
116
- return Headline .fromJson (json);
117
- },
103
+ // The HtDataPostgresClient returns DateTime objects from TIMESTAMPTZ
104
+ // columns. The Headline.fromJson factory expects ISO 8601 strings.
105
+ // This handler converts them before deserialization.
106
+ (json) => Headline .fromJson (_convertTimestampsToString (json)),
118
107
(headline) {
119
108
final json = headline.toJson ();
120
- // The database expects source_id and category_id, not nested objects.
121
- // We extract the IDs and remove the original objects to match the
122
- // schema.
123
- if (headline.source != null ) {
124
- json['source_id' ] = headline.source! .id;
125
- }
126
- if (headline.category != null ) {
127
- json['category_id' ] = headline.category! .id;
128
- }
109
+ // The database expects foreign key IDs, not nested objects.
110
+ // We extract the IDs and remove the original objects.
111
+ json['source_id' ] = headline.source.id;
112
+ json['topic_id' ] = headline.topic.id;
113
+ json['event_country_id' ] = headline.eventCountry.id;
129
114
json.remove ('source' );
130
- json.remove ('category' );
115
+ json.remove ('topic' );
116
+ json.remove ('eventCountry' );
131
117
return json;
132
118
},
133
119
);
134
- categoryRepository = _createRepository (
120
+ topicRepository = _createRepository (
135
121
connection,
136
- 'categories' ,
137
- (json) {
138
- if (json['created_at' ] is DateTime ) {
139
- json['created_at' ] =
140
- (json['created_at' ] as DateTime ).toIso8601String ();
141
- }
142
- if (json['updated_at' ] is DateTime ) {
143
- json['updated_at' ] =
144
- (json['updated_at' ] as DateTime ).toIso8601String ();
145
- }
146
- return Category .fromJson (json);
147
- },
148
- (c) => c.toJson (),
122
+ 'topics' ,
123
+ (json) => Topic .fromJson (_convertTimestampsToString (json)),
124
+ (topic) => topic.toJson (),
149
125
);
150
126
sourceRepository = _createRepository (
151
127
connection,
152
128
'sources' ,
153
- (json) {
154
- if (json['created_at' ] is DateTime ) {
155
- json['created_at' ] =
156
- (json['created_at' ] as DateTime ).toIso8601String ();
157
- }
158
- if (json['updated_at' ] is DateTime ) {
159
- json['updated_at' ] =
160
- (json['updated_at' ] as DateTime ).toIso8601String ();
161
- }
162
- return Source .fromJson (json);
163
- },
129
+ (json) => Source .fromJson (_convertTimestampsToString (json)),
164
130
(source) {
165
131
final json = source.toJson ();
166
132
// The database expects headquarters_country_id, not a nested object.
167
- // We extract the ID and remove the original object to match the
168
- // schema.
169
- json['headquarters_country_id' ] = source.headquarters? .id;
133
+ json['headquarters_country_id' ] = source.headquarters.id;
170
134
json.remove ('headquarters' );
171
135
return json;
172
136
},
173
137
);
174
138
countryRepository = _createRepository (
175
139
connection,
176
140
'countries' ,
177
- (json) {
178
- if (json['created_at' ] is DateTime ) {
179
- json['created_at' ] =
180
- (json['created_at' ] as DateTime ).toIso8601String ();
181
- }
182
- if (json['updated_at' ] is DateTime ) {
183
- json['updated_at' ] =
184
- (json['updated_at' ] as DateTime ).toIso8601String ();
185
- }
186
- return Country .fromJson (json);
187
- },
188
- (c) => c.toJson (),
141
+ (json) => Country .fromJson (_convertTimestampsToString (json)),
142
+ (country) => country.toJson (),
189
143
);
190
144
userRepository = _createRepository (
191
145
connection,
192
146
'users' ,
193
- (json) {
194
- // The postgres driver returns DateTime objects, but the model's
195
- // fromJson expects ISO 8601 strings. We must convert them first.
196
- if (json['created_at' ] is DateTime ) {
197
- json['created_at' ] = (json['created_at' ] as DateTime ).toIso8601String ();
198
- }
199
- if (json['last_engagement_shown_at' ] is DateTime ) {
200
- json['last_engagement_shown_at' ] =
201
- (json['last_engagement_shown_at' ] as DateTime ).toIso8601String ();
202
- }
203
- return User .fromJson (json);
204
- },
147
+ (json) => User .fromJson (_convertTimestampsToString (json)),
205
148
(user) {
206
- // The `roles` field is a List<String>, but the database expects a
207
- // JSONB array. We must explicitly encode it.
208
149
final json = user.toJson ();
209
- json['roles' ] = jsonEncode (json['roles' ]);
150
+ // Convert enums to their string names for the database.
151
+ json['app_role' ] = user.appRole.name;
152
+ json['dashboard_role' ] = user.dashboardRole.name;
153
+ // The `feed_action_status` map must be JSON encoded for the JSONB column.
154
+ json['feed_action_status' ] = jsonEncode (json['feed_action_status' ]);
210
155
return json;
211
156
},
212
157
);
213
158
userAppSettingsRepository = _createRepository (
214
159
connection,
215
160
'user_app_settings' ,
216
- (json) {
217
- // The DB has created_at/updated_at, but the model doesn't.
218
- // Remove them before deserialization to avoid CheckedFromJsonException.
219
- json.remove ('created_at' );
220
- json.remove ('updated_at' );
221
- return UserAppSettings .fromJson (json);
222
- },
161
+ (json) => UserAppSettings .fromJson (json),
223
162
(settings) {
224
163
final json = settings.toJson ();
225
164
// These fields are complex objects and must be JSON encoded for the DB.
226
165
json['display_settings' ] = jsonEncode (json['display_settings' ]);
227
166
json['feed_preferences' ] = jsonEncode (json['feed_preferences' ]);
228
- json['engagement_shown_counts' ] =
229
- jsonEncode (json['engagement_shown_counts' ]);
230
- json['engagement_last_shown_timestamps' ] =
231
- jsonEncode (json['engagement_last_shown_timestamps' ]);
232
167
return json;
233
168
},
234
169
);
235
170
userContentPreferencesRepository = _createRepository (
236
171
connection,
237
172
'user_content_preferences' ,
238
- (json) {
239
- // The postgres driver returns DateTime objects, but the model's
240
- // fromJson expects ISO 8601 strings. We must convert them first.
241
- if (json['created_at' ] is DateTime ) {
242
- json['created_at' ] =
243
- (json['created_at' ] as DateTime ).toIso8601String ();
244
- }
245
- if (json['updated_at' ] is DateTime ) {
246
- json['updated_at' ] =
247
- (json['updated_at' ] as DateTime ).toIso8601String ();
248
- }
249
- return UserContentPreferences .fromJson (json);
250
- },
173
+ (json) => UserContentPreferences .fromJson (json),
251
174
(preferences) {
252
175
final json = preferences.toJson ();
253
- json['followed_categories' ] = jsonEncode (json['followed_categories' ]);
176
+ // These fields are lists of complex objects and must be JSON encoded.
177
+ json['followed_topics' ] = jsonEncode (json['followed_topics' ]);
254
178
json['followed_sources' ] = jsonEncode (json['followed_sources' ]);
255
179
json['followed_countries' ] = jsonEncode (json['followed_countries' ]);
256
180
json['saved_headlines' ] = jsonEncode (json['saved_headlines' ]);
257
181
return json;
258
182
},
259
183
);
260
- appConfigRepository = _createRepository (
184
+ remoteConfigRepository = _createRepository (
261
185
connection,
262
- 'app_config' ,
263
- (json) {
264
- if (json['created_at' ] is DateTime ) {
265
- json['created_at' ] =
266
- (json['created_at' ] as DateTime ).toIso8601String ();
267
- }
268
- if (json['updated_at' ] is DateTime ) {
269
- json['updated_at' ] =
270
- (json['updated_at' ] as DateTime ).toIso8601String ();
271
- }
272
- return AppConfig .fromJson (json);
186
+ 'remote_config' ,
187
+ (json) => RemoteConfig .fromJson (_convertTimestampsToString (json)),
188
+ (config) {
189
+ final json = config.toJson ();
190
+ // All nested config objects must be JSON encoded for JSONB columns.
191
+ json['user_preference_limits' ] =
192
+ jsonEncode (json['user_preference_limits' ]);
193
+ json['ad_config' ] = jsonEncode (json['ad_config' ]);
194
+ json['account_action_config' ] =
195
+ jsonEncode (json['account_action_config' ]);
196
+ json['app_status' ] = jsonEncode (json['app_status' ]);
197
+ return json;
273
198
},
274
- (c) => c.toJson (),
275
199
);
276
200
277
201
// 4. Initialize Services.
@@ -296,12 +220,12 @@ class AppDependencies {
296
220
);
297
221
dashboardSummaryService = DashboardSummaryService (
298
222
headlineRepository: headlineRepository,
299
- categoryRepository : categoryRepository ,
223
+ topicRepository : topicRepository ,
300
224
sourceRepository: sourceRepository,
301
225
);
302
226
permissionService = const PermissionService ();
303
227
userPreferenceLimitService = DefaultUserPreferenceLimitService (
304
- appConfigRepository : appConfigRepository ,
228
+ remoteConfigRepository : remoteConfigRepository ,
305
229
);
306
230
}
307
231
@@ -321,4 +245,20 @@ class AppDependencies {
321
245
),
322
246
);
323
247
}
248
+
249
+ /// Converts DateTime values in a JSON map to ISO 8601 strings.
250
+ ///
251
+ /// The postgres driver returns DateTime objects for TIMESTAMPTZ columns,
252
+ /// but our models' `fromJson` factories expect ISO 8601 strings. This
253
+ /// utility function performs the conversion for known timestamp fields.
254
+ Map <String , dynamic > _convertTimestampsToString (Map <String , dynamic > json) {
255
+ const timestampKeys = {'created_at' , 'updated_at' };
256
+ final newJson = Map <String , dynamic >.from (json);
257
+ for (final key in timestampKeys) {
258
+ if (newJson[key] is DateTime ) {
259
+ newJson[key] = (newJson[key] as DateTime ).toIso8601String ();
260
+ }
261
+ }
262
+ return newJson;
263
+ }
324
264
}
0 commit comments