Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2e3c8e8
feat(models): add local video ad model
fulleni Aug 31, 2025
0e44cd6
feat(models): add local native ad model
fulleni Aug 31, 2025
83d53b1
feat(models): add local interstitial ad feed item
fulleni Aug 31, 2025
fdea697
feat(models): add local banner ad feed item
fulleni Aug 31, 2025
04758ad
feat(models): add local ad exports to feed decorators
fulleni Aug 31, 2025
0268c49
feat(core): add local ads fixtures for development
fulleni Aug 31, 2025
ffc888b
refactor(local_ad): transform LocalAd into an abstract base class
fulleni Aug 31, 2025
9ef7572
refactor(LocalBannerAd): update class hierarchy and JSON serialization
fulleni Aug 31, 2025
717dbf5
feat(LocalInterstitialAd): inherit from LocalAd and update JSON seria…
fulleni Aug 31, 2025
5d6e0e8
refactor(LocalNativeAd): extend LocalAd instead of FeedItem
fulleni Aug 31, 2025
a09739d
refactor(LocalVideoAd): extend LocalAd instead of FeedItem
fulleni Aug 31, 2025
5131eea
refactor(local_ad): remove JSON serialization and simplify ad type ha…
fulleni Aug 31, 2025
13549dd
refactor(LocalBannerAd): adjust JSON serialization and property list
fulleni Aug 31, 2025
dc66c4f
refactor(LocalInterstitialAd): update adType to string and simplify t…
fulleni Aug 31, 2025
e3ce76c
refactor(LocalNativeAd): update adType to string and remove overrides
fulleni Aug 31, 2025
38c298c
refactor(local_video_ad): simplify adType handling and remove props o…
fulleni Aug 31, 2025
3a63181
style: format
fulleni Aug 31, 2025
8db4465
fix(core): replace placeholder ad IDs with predefined ones
fulleni Aug 31, 2025
508f398
test(feed_item): update tests to use fixtures for local ads
fulleni Aug 31, 2025
0ccf692
test(local_ad): migrate unit tests to support abstract LocalAd class …
fulleni Aug 31, 2025
50a86db
refactor(fixture_ids): rename local ad id constants
fulleni Aug 31, 2025
bfde6e5
refactor(fixtures): replace local ad IDs with constants
fulleni Aug 31, 2025
381564f
refactor(models): reorganize local ads classes and update exports
fulleni Aug 31, 2025
4b96821
style(local_ad): fix typo in exception message
fulleni Aug 31, 2025
5c2ba56
style: format
fulleni Aug 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/src/fixtures/fixture_ids.dart
Original file line number Diff line number Diff line change
Expand Up @@ -522,3 +522,11 @@ const kXhosaId = '6643c000a4a4e6e1a8e7b0b2';
const kYiddishId = '6643c000a4a4e6e1a8e7b0b3';
const kYorubaId = '6643c000a4a4e6e1a8e7b0b4';
const kZhuangChuangId = '6643c000a4a4e6e1a8e7b0b5';

// --- LocalAds Fixture IDs ---
const localAd1 = '1563c000a4a4e6e1a8e7f0f1';
const localAd2 = '2563c000a4a4e6e1a8e7f0f2';
const localAd3 = '3563c000a4a4e6e1a8e7f0f3';
const localAd4 = '4563c000a4a4e6e1a8e7f0f4';
const localAd5 = '5563c000a4a4e6e1a8e7f0f5';
const localAd6 = '6563c000a4a4e6e1a8e7f0f6';
1 change: 1 addition & 0 deletions lib/src/fixtures/fixtures.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export 'dashboard_summary.dart';
export 'fixture_ids.dart';
export 'headlines.dart';
export 'languages.dart';
export 'local_ads.dart';
export 'remote_configs.dart';
export 'sources.dart';
export 'topics.dart';
Expand Down
39 changes: 39 additions & 0 deletions lib/src/fixtures/local_ads.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:core/core.dart';

/// A list of predefined local ads for fixture data.
final localAdsFixturesData = <LocalAd>[
const LocalNativeAd(
id: localAd1,
title: 'Discover Our Premium Content',
subtitle: 'Unlock exclusive articles and an ad-free experience.',
imageUrl: 'https://example.com/ads/premium_native.png',
targetUrl: 'https://example.com/premium',
),
const LocalBannerAd(
id: localAd2,
imageUrl: 'https://example.com/ads/banner_ad_1.png',
targetUrl: 'https://example.com/offer1',
),
const LocalInterstitialAd(
id: localAd3,
imageUrl: 'https://example.com/ads/interstitial_ad_1.png',
targetUrl: 'https://example.com/special_offer',
),
const LocalVideoAd(
id: localAd4,
videoUrl: 'https://example.com/ads/promo_video.mp4',
targetUrl: 'https://example.com/watch_more',
),
const LocalNativeAd(
id: localAd5,
title: 'Stay Informed with Daily Briefings',
subtitle: 'Get the top news delivered straight to your inbox.',
imageUrl: 'https://example.com/ads/briefing_native.png',
targetUrl: 'https://example.com/subscribe',
),
const LocalBannerAd(
id: localAd6,
imageUrl: 'https://example.com/ads/banner_ad_2.png',
targetUrl: 'https://example.com/offer2',
),
];
4 changes: 4 additions & 0 deletions lib/src/models/feed_decorators/feed_decorators.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export 'call_to_action_item.dart';
export 'content_collection_item.dart';
export 'local_ad.dart';
export 'local_banner_ad.dart';
export 'local_interstitial_ad.dart';
export 'local_native_ad.dart';
export 'local_video_ad.dart';
106 changes: 35 additions & 71 deletions lib/src/models/feed_decorators/local_ad.dart
Original file line number Diff line number Diff line change
@@ -1,83 +1,47 @@
import 'package:core/core.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:meta/meta.dart';

part 'local_ad.g.dart';

/// {@template local_ad}
/// Defines a single custom ad that can be served locally.
/// Includes title and subtitle to mimic native ads.
/// An abstract base class for all local ad types.
///
/// This class acts as a router for deserializing specific local ad types
/// (e.g., native, banner, interstitial, video) based on their `adType` field.
/// Concrete implementations must provide their own `toJson` method.
/// {@endtemplate}
@immutable
@JsonSerializable(explicitToJson: true, includeIfNull: true, checked: true)
class LocalAd extends FeedItem {
abstract class LocalAd extends FeedItem {
/// {@macro local_ad}
const LocalAd({
required this.id,
required this.title,
required this.subtitle,
required this.imageUrl,
required this.targetUrl,
required this.adType,
}) : super(type: 'localAd');

/// Creates a [LocalAd] from JSON data.
factory LocalAd.fromJson(Map<String, dynamic> json) =>
_$LocalAdFromJson(json);

/// Converts this [LocalAd] instance to JSON data.
Map<String, dynamic> toJson() {
final json = _$LocalAdToJson(this);
json['type'] = type;
return json;
const LocalAd({required this.adType}) : super(type: 'localAd');

/// Factory method to create a [LocalAd] instance from a JSON map.
///
/// This factory uses the `adType` field in the JSON map to dispatch to the
/// correct concrete `fromJson` constructor for each local ad type.
///
/// Throws [FormatException] if the `adType` field is missing or unknown.
factory LocalAd.fromJson(Map<String, dynamic> json) {
final adType = json['adType'] as String?;
if (adType == null) {
throw const FormatException('Missing "adType" field in LocalAd JSON.');
}

switch (adType) {
case 'native':
return LocalNativeAd.fromJson(json);
case 'banner':
return LocalBannerAd.fromJson(json);
case 'interstitial':
return LocalInterstitialAd.fromJson(json);
case 'video':
return LocalVideoAd.fromJson(json);
default:
throw FormatException('Unknown LocalAds type: $adType');
}
}

/// Unique identifier for the local ad.
final String id;

/// The title of the local ad.
final String title;

/// The subtitle or description of the local ad.
final String subtitle;

/// The URL of the image for the local ad.
final String imageUrl;

/// The URL to navigate to when the local ad is clicked.
final String targetUrl;

/// The type of the ad (e.g., banner, native, interstitial).
final AdType adType;
/// The type of the ad (e.g., banner, native, interstitial, video).
final String adType;

@override
List<Object?> get props => [
id,
title,
subtitle,
imageUrl,
targetUrl,
adType,
type,
];

/// Creates a copy of this [LocalAd] but with the given fields replaced with
/// the new values.
LocalAd copyWith({
String? id,
String? title,
String? subtitle,
String? imageUrl,
String? targetUrl,
AdType? adType,
}) {
return LocalAd(
id: id ?? this.id,
title: title ?? this.title,
subtitle: subtitle ?? this.subtitle,
imageUrl: imageUrl ?? this.imageUrl,
targetUrl: targetUrl ?? this.targetUrl,
adType: adType ?? this.adType,
);
}
List<Object?> get props => [adType, type];
}
39 changes: 0 additions & 39 deletions lib/src/models/feed_decorators/local_ad.g.dart

This file was deleted.

54 changes: 54 additions & 0 deletions lib/src/models/feed_decorators/local_banner_ad.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'package:core/core.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:meta/meta.dart';

part 'local_banner_ad.g.dart';

/// {@template local_banner_ad}
/// Defines a custom banner ad that can be served locally.
///
/// Banner ads are typically rectangular images with a target URL.
/// {@endtemplate}
@immutable
@JsonSerializable(explicitToJson: true, includeIfNull: true, checked: true)
class LocalBannerAd extends LocalAd {
/// {@macro local_banner_ad}
const LocalBannerAd({
required this.id,
required this.imageUrl,
required this.targetUrl,
}) : super(adType: 'banner');

/// Creates a [LocalBannerAd] from JSON data.
factory LocalBannerAd.fromJson(Map<String, dynamic> json) =>
_$LocalBannerAdFromJson(json);

/// Unique identifier for the local banner ad.
final String id;

/// The URL of the image for the local banner ad.
final String imageUrl;

/// The URL to navigate to when the local banner ad is clicked.
final String targetUrl;

Map<String, dynamic> toJson() {
final json = _$LocalBannerAdToJson(this);
json['adType'] = adType;
json['type'] = type;
return json;
}

@override
List<Object?> get props => [id, imageUrl, targetUrl, adType, type];

/// Creates a copy of this [LocalBannerAd] but with the given fields replaced with
/// the new values.
LocalBannerAd copyWith({String? id, String? imageUrl, String? targetUrl}) {
return LocalBannerAd(
id: id ?? this.id,
imageUrl: imageUrl ?? this.imageUrl,
targetUrl: targetUrl ?? this.targetUrl,
);
}
}
24 changes: 24 additions & 0 deletions lib/src/models/feed_decorators/local_banner_ad.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 59 additions & 0 deletions lib/src/models/feed_decorators/local_interstitial_ad.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'package:core/core.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:meta/meta.dart';

part 'local_interstitial_ad.g.dart';

/// {@template local_interstitial_ad}
/// Defines a custom interstitial ad that can be served locally.
///
/// Interstitial ads are full-screen advertisements, typically with an image
/// and a target URL, that appear between content transitions.
/// {@endtemplate}
@immutable
@JsonSerializable(explicitToJson: true, includeIfNull: true, checked: true)
class LocalInterstitialAd extends LocalAd {
/// {@macro local_interstitial_ad}
const LocalInterstitialAd({
required this.id,
required this.imageUrl,
required this.targetUrl,
}) : super(adType: 'interstitial');

/// Creates a [LocalInterstitialAd] from JSON data.
factory LocalInterstitialAd.fromJson(Map<String, dynamic> json) =>
_$LocalInterstitialAdFromJson(json);

/// Unique identifier for the local interstitial ad.
final String id;

/// The URL of the image for the local interstitial ad.
final String imageUrl;

/// The URL to navigate to when the local interstitial ad is clicked.
final String targetUrl;

Map<String, dynamic> toJson() {
final json = _$LocalInterstitialAdToJson(this);
json['adType'] = adType;
json['type'] = type;
return json;
}

@override
List<Object?> get props => [id, imageUrl, targetUrl, adType, type];

/// Creates a copy of this [LocalInterstitialAd] but with the given fields
/// replaced with the new values.
LocalInterstitialAd copyWith({
String? id,
String? imageUrl,
String? targetUrl,
}) {
return LocalInterstitialAd(
id: id ?? this.id,
imageUrl: imageUrl ?? this.imageUrl,
targetUrl: targetUrl ?? this.targetUrl,
);
}
}
25 changes: 25 additions & 0 deletions lib/src/models/feed_decorators/local_interstitial_ad.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading