Skip to content

Commit b6b199b

Browse files
committed
chore: misc
1 parent 3e1abf6 commit b6b199b

File tree

13 files changed

+191
-11
lines changed

13 files changed

+191
-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: 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+
}

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';

lib/src/models/remote_config/app_config.dart

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:equatable/equatable.dart';
22
import 'package:ht_shared/src/models/remote_config/ad_config.dart';
3+
import 'package:ht_shared/src/models/remote_config/feed_rules.dart';
34
import 'package:ht_shared/src/models/remote_config/user_preference_limits.dart';
45
import 'package:json_annotation/json_annotation.dart';
56
import 'package:meta/meta.dart';
@@ -31,6 +32,8 @@ class AppConfig extends Equatable {
3132
required this.id,
3233
UserPreferenceLimits? userPreferenceLimits,
3334
AdConfig? adConfig,
35+
List<EngagementRule>? engagementRules,
36+
List<SuggestionRule>? suggestionRules,
3437
}) : userPreferenceLimits = userPreferenceLimits ??
3538
const UserPreferenceLimits(
3639
guestFollowedItemsLimit: 5,
@@ -48,7 +51,9 @@ class AppConfig extends Equatable {
4851
authenticatedAdPlacementInterval: 5,
4952
premiumAdFrequency: 0, // No ads for premium users by default
5053
premiumAdPlacementInterval: 0,
51-
); // Default ad config
54+
), // Default ad config
55+
engagementRules = engagementRules ?? const [],
56+
suggestionRules = suggestionRules ?? const [];
5257

5358
/// Factory method to create an [AppConfig] instance from a JSON map.
5459
factory AppConfig.fromJson(Map<String, dynamic> json) =>
@@ -66,11 +71,23 @@ class AppConfig extends Equatable {
6671
/// tiered by user role.
6772
final AdConfig adConfig;
6873

74+
/// Defines rules for triggering engagement prompts.
75+
final List<EngagementRule> engagementRules;
76+
77+
/// Defines rules for triggering content suggestion blocks.
78+
final List<SuggestionRule> suggestionRules;
79+
6980
/// Converts this [AppConfig] instance to a JSON map.
7081
Map<String, dynamic> toJson() => _$AppConfigToJson(this);
7182

7283
@override
73-
List<Object> get props => [id, userPreferenceLimits, adConfig];
84+
List<Object> get props => [
85+
id,
86+
userPreferenceLimits,
87+
adConfig,
88+
engagementRules,
89+
suggestionRules,
90+
];
7491

7592
@override
7693
bool get stringify => true;

lib/src/models/remote_config/app_config.g.dart

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export 'ad_config.dart';
22
export 'app_config.dart';
3+
export 'feed_rules.dart';
34
export 'remote_config.dart';
45
export 'user_preference_limits.dart';

lib/src/models/user_settings/user_app_settings.dart

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,14 @@ class UserAppSettings extends Equatable {
2626
DisplaySettings? displaySettings,
2727
AppLanguage? language,
2828
FeedDisplayPreferences? feedPreferences,
29+
Map<String, int>? engagementShownCounts,
30+
Map<String, DateTime>? engagementLastShownTimestamps,
2931
}) : displaySettings = displaySettings ?? const DisplaySettings(),
3032
language = language ?? 'en', // Default language is English
31-
feedPreferences = feedPreferences ?? const FeedDisplayPreferences();
33+
feedPreferences = feedPreferences ?? const FeedDisplayPreferences(),
34+
engagementShownCounts = engagementShownCounts ?? const {},
35+
engagementLastShownTimestamps =
36+
engagementLastShownTimestamps ?? const {};
3237

3338
/// Factory method to create a [UserAppSettings] instance from a JSON map.
3439
factory UserAppSettings.fromJson(Map<String, dynamic> json) =>
@@ -46,11 +51,30 @@ class UserAppSettings extends Equatable {
4651
/// User-configurable settings for how content feeds are displayed.
4752
final FeedDisplayPreferences feedPreferences;
4853

54+
/// A map tracking how many times each engagement type has been shown to the user.
55+
/// Key: EngagementTemplateType.name (string), Value: count.
56+
final Map<String, int> engagementShownCounts;
57+
58+
/// A map tracking the last time each engagement type was shown to the user.
59+
/// Key: EngagementTemplateType.name (string), Value: timestamp.
60+
@JsonKey(
61+
fromJson: _engagementLastShownTimestampsFromJson,
62+
toJson: _engagementLastShownTimestampsToJson,
63+
)
64+
final Map<String, DateTime> engagementLastShownTimestamps;
65+
4966
/// Converts this [UserAppSettings] instance to a JSON map.
5067
Map<String, dynamic> toJson() => _$UserAppSettingsToJson(this);
5168

5269
@override
53-
List<Object?> get props => [id, displaySettings, language, feedPreferences];
70+
List<Object?> get props => [
71+
id,
72+
displaySettings,
73+
language,
74+
feedPreferences,
75+
engagementShownCounts,
76+
engagementLastShownTimestamps,
77+
];
5478

5579
/// Creates a copy of this [UserAppSettings] but with the given fields
5680
/// replaced with the new values.
@@ -59,12 +83,34 @@ class UserAppSettings extends Equatable {
5983
DisplaySettings? displaySettings,
6084
AppLanguage? language,
6185
FeedDisplayPreferences? feedPreferences,
86+
Map<String, int>? engagementShownCounts,
87+
Map<String, DateTime>? engagementLastShownTimestamps,
6288
}) {
6389
return UserAppSettings(
6490
id: id ?? this.id,
6591
displaySettings: displaySettings ?? this.displaySettings,
6692
language: language ?? this.language,
6793
feedPreferences: feedPreferences ?? this.feedPreferences,
94+
engagementShownCounts:
95+
engagementShownCounts ?? this.engagementShownCounts,
96+
engagementLastShownTimestamps:
97+
engagementLastShownTimestamps ?? this.engagementLastShownTimestamps,
6898
);
6999
}
70100
}
101+
102+
// Helper function for parsing Map<String, DateTime> from JSON
103+
Map<String, DateTime> _engagementLastShownTimestampsFromJson(
104+
Map<String, dynamic>? json,
105+
) {
106+
if (json == null) return const {};
107+
return json
108+
.map((key, value) => MapEntry(key, DateTime.parse(value as String)));
109+
}
110+
111+
// Helper function for serializing Map<String, DateTime> to JSON
112+
Map<String, dynamic> _engagementLastShownTimestampsToJson(
113+
Map<String, DateTime> map,
114+
) {
115+
return map.map((key, value) => MapEntry(key, value.toIso8601String()));
116+
}

0 commit comments

Comments
 (0)