Skip to content

Commit c48cb86

Browse files
authored
Merge pull request #5 from headlines-toolkit/feed_enhancer_models
Feed_enhancer_models
2 parents 59c9098 + b6b199b commit c48cb86

19 files changed

+616
-11
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 🛠️ ht_shared
22

3-
![coverage: percentage](https://img.shields.io/badge/coverage-96-green)
3+
![coverage: percentage](https://img.shields.io/badge/coverage-81-green)
44
[![style: very good analysis](https://img.shields.io/badge/style-very_good_analysis-B22C89.svg)](https://pub.dev/packages/very_good_analysis)
55
[![License: PolyForm Free Trial](https://img.shields.io/badge/License-PolyForm%20Free%20Trial-blue)](https://polyformproject.org/licenses/free-trial/1.0.0)
66

lib/src/models/auth/user.dart

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ class User extends Equatable {
1515
/// Requires a unique [id] and a [role].
1616
/// The [email] is optional and typically present only for users
1717
/// who have verified their email address.
18-
const User({required this.id, required this.role, this.email});
18+
const User({
19+
required this.id,
20+
required this.role,
21+
this.email,
22+
this.createdAt,
23+
});
1924

2025
/// Creates a User from JSON data.
2126
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
@@ -31,14 +36,46 @@ class User extends Equatable {
3136
/// The role of the user.
3237
final UserRole role;
3338

39+
/// The date and time the user account was created.
40+
/// This is typically set on the backend upon user creation.
41+
@JsonKey(fromJson: _dateTimeFromJson, toJson: _dateTimeToJson)
42+
final DateTime? createdAt;
43+
3444
/// Converts this User instance to JSON data.
3545
Map<String, dynamic> toJson() => _$UserToJson(this);
3646

3747
@override
38-
List<Object?> get props => [id, email, role];
48+
List<Object?> get props => [id, email, role, createdAt];
3949

4050
@override
4151
String toString() {
42-
return 'User(id: $id, email: $email, role: $role)';
52+
return 'User(id: $id, email: $email, role: $role, createdAt: $createdAt)';
53+
}
54+
55+
/// Creates a copy of this [User] but with the given fields replaced with
56+
/// the new values.
57+
User copyWith({
58+
String? id,
59+
String? email,
60+
UserRole? role,
61+
DateTime? createdAt,
62+
}) {
63+
return User(
64+
id: id ?? this.id,
65+
email: email ?? this.email,
66+
role: role ?? this.role,
67+
createdAt: createdAt ?? this.createdAt,
68+
);
4369
}
4470
}
71+
72+
// Helper function for parsing DateTime, returning null on error
73+
DateTime? _dateTimeFromJson(String? dateString) {
74+
if (dateString == null) return null;
75+
return DateTime.tryParse(dateString);
76+
}
77+
78+
// Helper function for serializing DateTime to ISO 8601 string
79+
String? _dateTimeToJson(DateTime? dateTime) {
80+
return dateTime?.toIso8601String();
81+
}

lib/src/models/auth/user.g.dart

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import 'package:equatable/equatable.dart';
2+
import 'package:ht_shared/src/models/feed_extras/feed_template_types.dart';
3+
import 'package:json_annotation/json_annotation.dart';
4+
5+
part 'engagement_content_template.g.dart';
6+
7+
/// {@template engagement_content_template}
8+
/// Defines the static content for an engagement prompt.
9+
/// The 'type' of an instance should match an [EngagementTemplateType] value.
10+
/// {@endtemplate}
11+
@JsonSerializable(explicitToJson: true)
12+
class EngagementContentTemplate extends Equatable {
13+
/// {@macro engagement_content_template}
14+
const EngagementContentTemplate({
15+
required this.type,
16+
required this.title,
17+
this.description,
18+
this.callToActionText,
19+
});
20+
21+
/// Creates an [EngagementContentTemplate] from JSON data.
22+
factory EngagementContentTemplate.fromJson(Map<String, dynamic> json) =>
23+
_$EngagementContentTemplateFromJson(json);
24+
25+
/// The type of engagement template, matching an [EngagementTemplateType] value.
26+
final EngagementTemplateType type;
27+
28+
/// The main title or heading for the engagement content.
29+
final String title;
30+
31+
/// An optional description providing more details.
32+
final String? description;
33+
34+
/// The text for the call-to-action button or link.
35+
final String? callToActionText;
36+
37+
/// Converts this [EngagementContentTemplate] instance to JSON data.
38+
Map<String, dynamic> toJson() => _$EngagementContentTemplateToJson(this);
39+
40+
@override
41+
List<Object?> get props => [type, title, description, callToActionText];
42+
}

lib/src/models/feed_extras/engagement_content_template.g.dart

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export 'engagement_content_template.dart';
2+
export 'feed_template_types.dart';
3+
export 'suggested_content_template.dart';
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'package:json_annotation/json_annotation.dart';
2+
3+
/// Defines the types of engagement content templates available.
4+
/// The string value of the enum (e.g., 'rate-app') will be used as the ID
5+
/// to link rules in AppConfig to specific EngagementContentTemplate instances.
6+
@JsonEnum(fieldRename: FieldRename.kebab)
7+
enum EngagementTemplateType {
8+
/// Prompt to rate the application.
9+
rateApp,
10+
11+
/// Prompt for guest users to create/link an account.
12+
linkAccount,
13+
14+
/// Prompt for standard users to upgrade to premium.
15+
upgradeToPremium,
16+
17+
/// Prompt to complete user profile (e.g., select content preferences).
18+
completeProfile,
19+
20+
/// Prompt to explore a new feature.
21+
exploreNewFeature,
22+
}
23+
24+
/// Defines the types of suggested content templates available.
25+
/// The string value of the enum (e.g., 'categories-to-follow') will be used as the ID
26+
/// to link rules in AppConfig to specific SuggestedContentTemplate instances.
27+
@JsonEnum(fieldRename: FieldRename.kebab)
28+
enum SuggestionTemplateType {
29+
/// Suggest categories to follow.
30+
categoriesToFollow,
31+
32+
/// Suggest sources to follow.
33+
sourcesToFollow,
34+
35+
/// Suggest countries to follow for news.
36+
countriesToFollow,
37+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import 'package:equatable/equatable.dart';
2+
import 'package:ht_shared/src/models/core/content_type.dart';
3+
import 'package:ht_shared/src/models/feed_decorators/suggested_content_display_type.dart';
4+
import 'package:ht_shared/src/models/feed_extras/feed_template_types.dart';
5+
import 'package:json_annotation/json_annotation.dart';
6+
7+
part 'suggested_content_template.g.dart';
8+
9+
/// {@template suggested_content_template}
10+
/// Defines the static content and configuration for a suggestion block.
11+
/// The 'type' of an instance should match a [SuggestionTemplateType] value.
12+
/// {@endtemplate}
13+
@JsonSerializable(explicitToJson: true)
14+
class SuggestedContentTemplate extends Equatable {
15+
/// {@macro suggested_content_template}
16+
const SuggestedContentTemplate({
17+
required this.type,
18+
required this.displayType,
19+
required this.suggestedContentType,
20+
this.title,
21+
this.description,
22+
this.maxItemsToDisplay,
23+
this.fetchCriteria,
24+
});
25+
26+
/// Creates a [SuggestedContentTemplate] from JSON data.
27+
factory SuggestedContentTemplate.fromJson(Map<String, dynamic> json) =>
28+
_$SuggestedContentTemplateFromJson(json);
29+
30+
/// The type of suggestion template, matching a [SuggestionTemplateType] value.
31+
final SuggestionTemplateType type;
32+
33+
/// An optional title for the suggestion block (e.g., "You might like...").
34+
final String? title;
35+
36+
/// An optional description for the suggestion block.
37+
final String? description;
38+
39+
/// The visual presentation or layout style for this suggestion block.
40+
final SuggestedContentDisplayType displayType;
41+
42+
/// Defines what kind of primary content this suggestion block will contain
43+
/// (e.g., if suggesting categories, this would be [ContentType.category]).
44+
final ContentType suggestedContentType;
45+
46+
/// Maximum number of items to display within this suggestion block.
47+
final int? maxItemsToDisplay;
48+
49+
/// Criteria for fetching dynamic items, e.g., "popular", "newest".
50+
/// This is a simple string; the decorator will interpret it.
51+
final String? fetchCriteria;
52+
53+
/// Converts this [SuggestedContentTemplate] instance to JSON data.
54+
Map<String, dynamic> toJson() => _$SuggestedContentTemplateToJson(this);
55+
56+
@override
57+
List<Object?> get props => [
58+
type,
59+
title,
60+
description,
61+
displayType,
62+
suggestedContentType,
63+
maxItemsToDisplay,
64+
fetchCriteria,
65+
];
66+
}

lib/src/models/feed_extras/suggested_content_template.g.dart

Lines changed: 60 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/src/models/models.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export 'auth/auth.dart';
22
export 'core/core.dart';
33
export 'entities/entities.dart';
44
export 'feed_decorators/feed_decorators.dart';
5+
export 'feed_extras/feed_extras.dart';
56
export 'remote_config/remote_config.dart';
67
export 'responses/responses.dart';
78
export 'user_preferences/user_preferences.dart';

0 commit comments

Comments
 (0)