Skip to content

Commit 9d8f182

Browse files
committed
fix: misc
1 parent 223f78c commit 9d8f182

16 files changed

+122
-79
lines changed

lib/src/models/feed/engagement_content.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ class EngagementContent extends FeedItem {
2323
String? id,
2424
}) : id = id ?? const Uuid().v4(),
2525
super(
26-
type:
27-
'engagement_content',); // Removed action from super constructor
26+
type: 'engagement_content',
27+
); // Removed action from super constructor
2828

2929
/// Factory method to create an [EngagementContent] instance from a JSON map.
3030
factory EngagementContent.fromJson(Map<String, dynamic> json) {

lib/src/models/feed/suggested_content.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ class SuggestedContent extends FeedItem {
2525
String? id,
2626
}) : id = id ?? const Uuid().v4(),
2727
super(
28-
type: 'suggested_content',); // Removed action from super constructor
28+
type: 'suggested_content',
29+
); // Removed action from super constructor
2930

3031
/// Factory method to create a [SuggestedContent] instance from a JSON map.
3132
factory SuggestedContent.fromJson(Map<String, dynamic> json) {
@@ -91,7 +92,8 @@ class SuggestedContent extends FeedItem {
9192
return e.toJson();
9293
}
9394
throw FormatException(
94-
'Unknown item type for serialization: ${e.runtimeType}',);
95+
'Unknown item type for serialization: ${e.runtimeType}',
96+
);
9597
}).toList(),
9698
'action': action.toJson(),
9799
'type': type, // Inherited from FeedItem

lib/src/models/remote-config/ad_config.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import 'package:equatable/equatable.dart';
22
import 'package:ht_shared/ht_shared.dart' show AppConfig;
33
import 'package:ht_shared/src/models/models.dart' show AppConfig;
4-
import 'package:ht_shared/src/models/remote-config/app_config.dart' show AppConfig;
5-
import 'package:ht_shared/src/models/remote-config/remote_config.dart' show AppConfig;
4+
import 'package:ht_shared/src/models/remote-config/app_config.dart'
5+
show AppConfig;
6+
import 'package:ht_shared/src/models/remote-config/remote_config.dart'
7+
show AppConfig;
68

79
/// {@template ad_config}
810
/// Defines configuration settings related to ad injection and display,

lib/src/models/remote-config/user_preference_limits.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import 'package:equatable/equatable.dart';
2-
import 'package:ht_shared/ht_shared.dart' show AppConfig, UserContentPreferences;
3-
import 'package:ht_shared/src/models/models.dart' show AppConfig, UserContentPreferences;
2+
import 'package:ht_shared/ht_shared.dart'
3+
show AppConfig, UserContentPreferences;
4+
import 'package:ht_shared/src/models/models.dart'
5+
show AppConfig, UserContentPreferences;
46
import 'package:meta/meta.dart';
57

68
/// {@template user_preference_limits}

lib/src/utils/json_converters.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:ht_shared/ht_shared.dart';
22
import 'package:ht_shared/src/models/content_type.dart';
3+
import 'package:ht_shared/src/models/feed/feed_item_action.dart';
34

45
/// Converts a snake_case string to a [ContentType] enum value.
56
///
@@ -377,3 +378,33 @@ String adTypeToJson(AdType type) {
377378
return 'interstitial';
378379
}
379380
}
381+
382+
/// Converts a JSON map to a [FeedItemAction] instance.
383+
///
384+
/// This function acts as a central deserializer for the sealed [FeedItemAction]
385+
/// class, dispatching to the correct subtype's `fromJson` constructor based
386+
/// on the 'type' field in the JSON.
387+
///
388+
/// Throws a [FormatException] if the 'type' is unknown or if the JSON
389+
/// structure is invalid for the determined subtype.
390+
FeedItemAction feedItemActionFromJson(Map<String, dynamic> json) {
391+
final type = json['type'] as String;
392+
switch (type) {
393+
case 'open_internal_content':
394+
return OpenInternalContent.fromJson(json);
395+
case 'show_interstitial_then_open_internal_content':
396+
return ShowInterstitialThenOpenInternalContent.fromJson(json);
397+
case 'open_external_url':
398+
return OpenExternalUrl.fromJson(json);
399+
default:
400+
throw FormatException('Unknown FeedItemAction type: $type');
401+
}
402+
}
403+
404+
/// Converts a [FeedItemAction] instance to a JSON map.
405+
///
406+
/// This function acts as a central serializer for the sealed [FeedItemAction]
407+
/// class, dispatching to the correct subtype's `toJson` method.
408+
String feedItemActionToJson(FeedItemAction action) {
409+
return action.toJson()['type'] as String;
410+
}

pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ packages:
258258
source: hosted
259259
version: "0.7.2"
260260
json_annotation:
261-
dependency: transitive
261+
dependency: "direct main"
262262
description:
263263
name: json_annotation
264264
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ environment:
88

99
dependencies:
1010
equatable: ^2.0.7
11+
json_annotation: ^4.9.0
1112
meta: ^1.17.0
1213
uuid: ^4.5.1
1314

test/src/models/feed/ad_test.dart

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,14 @@ void main() {
1212
const testTargetUrl = 'http://example.com/target';
1313
const testAdType = AdType.banner;
1414
const testPlacement = AdPlacement.feedInlineStandardBanner;
15-
const defaultAction = OpenExternalUrl(url: 'http://default.com');
15+
final defaultAction = OpenExternalUrl(url: 'http://default.com');
1616

1717
Ad createSubject({
18-
String? id,
18+
required FeedItemAction action, String? id,
1919
String imageUrl = testImageUrl,
2020
String targetUrl = testTargetUrl,
2121
AdType adType = testAdType,
2222
AdPlacement? placement = testPlacement,
23-
FeedItemAction action = defaultAction,
2423
}) {
2524
return Ad(
2625
id: id,
@@ -34,18 +33,18 @@ void main() {
3433

3534
group('constructor', () {
3635
test('generates id when not provided', () {
37-
final ad = createSubject();
36+
final ad = createSubject(action: defaultAction);
3837
expect(ad.id, isA<String>());
3938
expect(Uuid.isValidUUID(fromString: ad.id), isTrue);
4039
});
4140

4241
test('uses provided id', () {
43-
final ad = createSubject(id: testId);
42+
final ad = createSubject(id: testId, action: defaultAction);
4443
expect(ad.id, testId);
4544
});
4645

4746
test('initializes all properties correctly', () {
48-
final ad = createSubject();
47+
final ad = createSubject(action: defaultAction);
4948
expect(ad.imageUrl, testImageUrl);
5049
expect(ad.targetUrl, testTargetUrl);
5150
expect(ad.adType, testAdType);
@@ -59,12 +58,12 @@ void main() {
5958
test('returns a new instance with updated fields', () {
6059
const newImageUrl = 'http://new.com/ad.png';
6160
const newAdType = AdType.video;
62-
const newAction = OpenInternalContent(
61+
final newAction = OpenInternalContent(
6362
contentId: 'new-content',
6463
contentType: ContentType.headline,
6564
);
6665

67-
final originalAd = createSubject();
66+
final originalAd = createSubject(action: defaultAction);
6867
final updatedAd = originalAd.copyWith(
6968
imageUrl: newImageUrl,
7069
adType: newAdType,
@@ -81,7 +80,7 @@ void main() {
8180
});
8281

8382
test('returns an identical copy if no updates provided', () {
84-
final originalAd = createSubject();
83+
final originalAd = createSubject(action: defaultAction);
8584
final copiedAd = originalAd.copyWith();
8685
expect(copiedAd, originalAd);
8786
expect(identical(copiedAd, originalAd), isFalse);
@@ -90,7 +89,7 @@ void main() {
9089

9190
group('toJson', () {
9291
test('serializes full Ad object to JSON', () {
93-
final ad = createSubject();
92+
final ad = createSubject(action: defaultAction);
9493
final json = ad.toJson();
9594

9695
expect(json, <String, dynamic>{
@@ -105,7 +104,7 @@ void main() {
105104
});
106105

107106
test('omits null optional fields from JSON', () {
108-
final ad = createSubject(placement: null);
107+
final ad = createSubject(placement: null, action: defaultAction);
109108
final json = ad.toJson();
110109

111110
expect(json.containsKey('placement'), isFalse);
@@ -164,19 +163,19 @@ void main() {
164163

165164
group('Equatable', () {
166165
test('instances with same properties are equal', () {
167-
final ad1 = createSubject(id: '1');
168-
final ad2 = createSubject(id: '1');
166+
final ad1 = createSubject(id: '1', action: defaultAction);
167+
final ad2 = createSubject(id: '1', action: defaultAction);
169168
expect(ad1, ad2);
170169
});
171170

172171
test('instances with different properties are not equal', () {
173-
final ad1 = createSubject(id: '1');
174-
final ad2 = createSubject(id: '2');
172+
final ad1 = createSubject(id: '1', action: defaultAction);
173+
final ad2 = createSubject(id: '2', action: defaultAction);
175174
expect(ad1, isNot(equals(ad2)));
176175
});
177176

178177
test('props list contains all relevant fields', () {
179-
final ad = createSubject();
178+
final ad = createSubject(action: defaultAction);
180179
expect(ad.props, [
181180
ad.id,
182181
ad.imageUrl,

test/src/models/feed/engagement_content_test.dart

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,15 @@ void main() {
1313
const testEngagementContentType = EngagementContentType.signUp;
1414
const testCallToActionText = 'Sign Up Now!';
1515
const testCallToActionUrl = 'http://example.com/signup';
16-
const defaultAction = OpenExternalUrl(url: 'http://default.com');
16+
final defaultAction = OpenExternalUrl(url: 'http://default.com');
1717

1818
EngagementContent createSubject({
19-
String? id,
19+
required FeedItemAction action, String? id,
2020
String title = testTitle,
2121
String? description = testDescription,
2222
EngagementContentType engagementContentType = testEngagementContentType,
2323
String? callToActionText = testCallToActionText,
2424
String? callToActionUrl = testCallToActionUrl,
25-
FeedItemAction action = defaultAction,
2625
}) {
2726
return EngagementContent(
2827
id: id,
@@ -37,18 +36,18 @@ void main() {
3736

3837
group('constructor', () {
3938
test('generates id when not provided', () {
40-
final content = createSubject();
39+
final content = createSubject(action: defaultAction);
4140
expect(content.id, isA<String>());
4241
expect(Uuid.isValidUUID(fromString: content.id), isTrue);
4342
});
4443

4544
test('uses provided id', () {
46-
final content = createSubject(id: testId);
45+
final content = createSubject(id: testId, action: defaultAction);
4746
expect(content.id, testId);
4847
});
4948

5049
test('initializes all properties correctly', () {
51-
final content = createSubject();
50+
final content = createSubject(action: defaultAction);
5251
expect(content.title, testTitle);
5352
expect(content.description, testDescription);
5453
expect(content.engagementContentType, testEngagementContentType);
@@ -66,12 +65,12 @@ void main() {
6665
const newEngagementContentType = EngagementContentType.feedback;
6766
const newCallToActionText = 'Give Feedback';
6867
const newCallToActionUrl = 'http://example.com/feedback';
69-
const newAction = OpenInternalContent(
68+
final newAction = OpenInternalContent(
7069
contentId: 'feedback-id',
7170
contentType: ContentType.category,
7271
);
7372

74-
final originalContent = createSubject();
73+
final originalContent = createSubject(action: defaultAction);
7574
final updatedContent = originalContent.copyWith(
7675
title: newTitle,
7776
description: newDescription,
@@ -92,7 +91,7 @@ void main() {
9291
});
9392

9493
test('returns an identical copy if no updates provided', () {
95-
final originalContent = createSubject();
94+
final originalContent = createSubject(action: defaultAction);
9695
final copiedContent = originalContent.copyWith();
9796
expect(copiedContent, originalContent);
9897
expect(identical(copiedContent, originalContent), isFalse);
@@ -101,7 +100,7 @@ void main() {
101100

102101
group('toJson', () {
103102
test('serializes full EngagementContent object to JSON', () {
104-
final content = createSubject();
103+
final content = createSubject(action: defaultAction);
105104
final json = content.toJson();
106105

107106
expect(json, <String, dynamic>{
@@ -121,6 +120,7 @@ void main() {
121120
description: null,
122121
callToActionText: null,
123122
callToActionUrl: null,
123+
action: defaultAction,
124124
);
125125
final json = content.toJson();
126126

@@ -185,19 +185,19 @@ void main() {
185185

186186
group('Equatable', () {
187187
test('instances with same properties are equal', () {
188-
final content1 = createSubject(id: '1');
189-
final content2 = createSubject(id: '1');
188+
final content1 = createSubject(id: '1', action: defaultAction);
189+
final content2 = createSubject(id: '1', action: defaultAction);
190190
expect(content1, content2);
191191
});
192192

193193
test('instances with different properties are not equal', () {
194-
final content1 = createSubject(id: '1');
195-
final content2 = createSubject(id: '2');
194+
final content1 = createSubject(id: '1', action: defaultAction);
195+
final content2 = createSubject(id: '2', action: defaultAction);
196196
expect(content1, isNot(equals(content2)));
197197
});
198198

199199
test('props list contains all relevant fields', () {
200-
final content = createSubject();
200+
final content = createSubject(action: defaultAction);
201201
expect(content.props, [
202202
content.id,
203203
content.title,

0 commit comments

Comments
 (0)