Compare commits
	
		
			2 Commits
		
	
	
		
			f03f0181f8
			...
			e367fc3f5c
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e367fc3f5c | |||
| 8a1af120ea | 
| @@ -675,5 +675,6 @@ | ||||
|   "publisherFeatureDevelop": "Developer Program", | ||||
|   "publisherFeatureDevelopDescription": "Unlock development abilities for your publisher, including custom apps, API keys, and more.", | ||||
|   "publisherFeatureDevelopHint": "Currently, this feature is under active development, you need send a request to unlock this feature.", | ||||
|   "learnMore": "Learn More" | ||||
|   "learnMore": "Learn More", | ||||
|   "discoverWebArticles": "Articles from external sites" | ||||
| } | ||||
|   | ||||
| @@ -7,57 +7,58 @@ part 'webfeed.freezed.dart'; | ||||
| part 'webfeed.g.dart'; | ||||
|  | ||||
| @freezed | ||||
| sealed class WebFeedConfig with _$WebFeedConfig { | ||||
|   const factory WebFeedConfig({@Default(false) bool scrapPage}) = | ||||
|       _WebFeedConfig; | ||||
| sealed class SnWebFeedConfig with _$SnWebFeedConfig { | ||||
|   const factory SnWebFeedConfig({@Default(false) bool scrapPage}) = | ||||
|       _SnWebFeedConfig; | ||||
|  | ||||
|   factory WebFeedConfig.fromJson(Map<String, dynamic> json) => | ||||
|       _$WebFeedConfigFromJson(json); | ||||
|   factory SnWebFeedConfig.fromJson(Map<String, dynamic> json) => | ||||
|       _$SnWebFeedConfigFromJson(json); | ||||
| } | ||||
|  | ||||
| @freezed | ||||
| sealed class WebFeed with _$WebFeed { | ||||
|   const factory WebFeed({ | ||||
| sealed class SnWebFeed with _$SnWebFeed { | ||||
|   const factory SnWebFeed({ | ||||
|     required String id, | ||||
|     required String url, | ||||
|     required String title, | ||||
|     String? description, | ||||
|     SnScrappedLink? preview, | ||||
|     @Default(WebFeedConfig()) WebFeedConfig config, | ||||
|     @Default(SnWebFeedConfig()) SnWebFeedConfig config, | ||||
|     required String publisherId, | ||||
|     @Default([]) List<WebArticle> articles, | ||||
|     @Default([]) List<SnWebArticle> articles, | ||||
|     required DateTime createdAt, | ||||
|     required DateTime updatedAt, | ||||
|     DateTime? deletedAt, | ||||
|   }) = _WebFeed; | ||||
|   }) = _SnWebFeed; | ||||
|  | ||||
|   factory WebFeed.fromJson(Map<String, dynamic> json) => | ||||
|       _$WebFeedFromJson(json); | ||||
|   factory SnWebFeed.fromJson(Map<String, dynamic> json) => | ||||
|       _$SnWebFeedFromJson(json); | ||||
|  | ||||
|   factory WebFeed.fromJsonString(String jsonString) => | ||||
|       WebFeed.fromJson(jsonDecode(jsonString) as Map<String, dynamic>); | ||||
|   factory SnWebFeed.fromJsonString(String jsonString) => | ||||
|       SnWebFeed.fromJson(jsonDecode(jsonString) as Map<String, dynamic>); | ||||
| } | ||||
|  | ||||
| @freezed | ||||
| sealed class WebArticle with _$WebArticle { | ||||
|   const factory WebArticle({ | ||||
| sealed class SnWebArticle with _$SnWebArticle { | ||||
|   const factory SnWebArticle({ | ||||
|     required String id, | ||||
|     required String title, | ||||
|     required String url, | ||||
|     String? author, | ||||
|     Map<String, dynamic>? meta, | ||||
|     SnScrappedLink? preview, | ||||
|     SnWebFeed? feed, | ||||
|     String? content, | ||||
|     DateTime? publishedAt, | ||||
|     required String feedId, | ||||
|     required DateTime createdAt, | ||||
|     required DateTime updatedAt, | ||||
|     DateTime? deletedAt, | ||||
|   }) = _WebArticle; | ||||
|   }) = _SnWebArticle; | ||||
|  | ||||
|   factory WebArticle.fromJson(Map<String, dynamic> json) => | ||||
|       _$WebArticleFromJson(json); | ||||
|   factory SnWebArticle.fromJson(Map<String, dynamic> json) => | ||||
|       _$SnWebArticleFromJson(json); | ||||
|  | ||||
|   factory WebArticle.fromJsonString(String jsonString) => | ||||
|       WebArticle.fromJson(jsonDecode(jsonString) as Map<String, dynamic>); | ||||
|   factory SnWebArticle.fromJsonString(String jsonString) => | ||||
|       SnWebArticle.fromJson(jsonDecode(jsonString) as Map<String, dynamic>); | ||||
| } | ||||
|   | ||||
| @@ -14,22 +14,22 @@ part of 'webfeed.dart'; | ||||
| T _$identity<T>(T value) => value; | ||||
|  | ||||
| /// @nodoc | ||||
| mixin _$WebFeedConfig { | ||||
| mixin _$SnWebFeedConfig { | ||||
|  | ||||
|  bool get scrapPage; | ||||
| /// Create a copy of WebFeedConfig | ||||
| /// Create a copy of SnWebFeedConfig | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @pragma('vm:prefer-inline') | ||||
| $WebFeedConfigCopyWith<WebFeedConfig> get copyWith => _$WebFeedConfigCopyWithImpl<WebFeedConfig>(this as WebFeedConfig, _$identity); | ||||
| $SnWebFeedConfigCopyWith<SnWebFeedConfig> get copyWith => _$SnWebFeedConfigCopyWithImpl<SnWebFeedConfig>(this as SnWebFeedConfig, _$identity); | ||||
|  | ||||
|   /// Serializes this WebFeedConfig to a JSON map. | ||||
|   /// Serializes this SnWebFeedConfig to a JSON map. | ||||
|   Map<String, dynamic> toJson(); | ||||
|  | ||||
|  | ||||
| @override | ||||
| bool operator ==(Object other) { | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is WebFeedConfig&&(identical(other.scrapPage, scrapPage) || other.scrapPage == scrapPage)); | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is SnWebFeedConfig&&(identical(other.scrapPage, scrapPage) || other.scrapPage == scrapPage)); | ||||
| } | ||||
|  | ||||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @@ -38,15 +38,15 @@ int get hashCode => Object.hash(runtimeType,scrapPage); | ||||
|  | ||||
| @override | ||||
| String toString() { | ||||
|   return 'WebFeedConfig(scrapPage: $scrapPage)'; | ||||
|   return 'SnWebFeedConfig(scrapPage: $scrapPage)'; | ||||
| } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| /// @nodoc | ||||
| abstract mixin class $WebFeedConfigCopyWith<$Res>  { | ||||
|   factory $WebFeedConfigCopyWith(WebFeedConfig value, $Res Function(WebFeedConfig) _then) = _$WebFeedConfigCopyWithImpl; | ||||
| abstract mixin class $SnWebFeedConfigCopyWith<$Res>  { | ||||
|   factory $SnWebFeedConfigCopyWith(SnWebFeedConfig value, $Res Function(SnWebFeedConfig) _then) = _$SnWebFeedConfigCopyWithImpl; | ||||
| @useResult | ||||
| $Res call({ | ||||
|  bool scrapPage | ||||
| @@ -57,14 +57,14 @@ $Res call({ | ||||
|  | ||||
| } | ||||
| /// @nodoc | ||||
| class _$WebFeedConfigCopyWithImpl<$Res> | ||||
|     implements $WebFeedConfigCopyWith<$Res> { | ||||
|   _$WebFeedConfigCopyWithImpl(this._self, this._then); | ||||
| class _$SnWebFeedConfigCopyWithImpl<$Res> | ||||
|     implements $SnWebFeedConfigCopyWith<$Res> { | ||||
|   _$SnWebFeedConfigCopyWithImpl(this._self, this._then); | ||||
|  | ||||
|   final WebFeedConfig _self; | ||||
|   final $Res Function(WebFeedConfig) _then; | ||||
|   final SnWebFeedConfig _self; | ||||
|   final $Res Function(SnWebFeedConfig) _then; | ||||
|  | ||||
| /// Create a copy of WebFeedConfig | ||||
| /// Create a copy of SnWebFeedConfig | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @pragma('vm:prefer-inline') @override $Res call({Object? scrapPage = null,}) { | ||||
|   return _then(_self.copyWith( | ||||
| @@ -79,26 +79,26 @@ as bool, | ||||
| /// @nodoc | ||||
| @JsonSerializable() | ||||
|  | ||||
| class _WebFeedConfig implements WebFeedConfig { | ||||
|   const _WebFeedConfig({this.scrapPage = false}); | ||||
|   factory _WebFeedConfig.fromJson(Map<String, dynamic> json) => _$WebFeedConfigFromJson(json); | ||||
| class _SnWebFeedConfig implements SnWebFeedConfig { | ||||
|   const _SnWebFeedConfig({this.scrapPage = false}); | ||||
|   factory _SnWebFeedConfig.fromJson(Map<String, dynamic> json) => _$SnWebFeedConfigFromJson(json); | ||||
|  | ||||
| @override@JsonKey() final  bool scrapPage; | ||||
|  | ||||
| /// Create a copy of WebFeedConfig | ||||
| /// Create a copy of SnWebFeedConfig | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @pragma('vm:prefer-inline') | ||||
| _$WebFeedConfigCopyWith<_WebFeedConfig> get copyWith => __$WebFeedConfigCopyWithImpl<_WebFeedConfig>(this, _$identity); | ||||
| _$SnWebFeedConfigCopyWith<_SnWebFeedConfig> get copyWith => __$SnWebFeedConfigCopyWithImpl<_SnWebFeedConfig>(this, _$identity); | ||||
|  | ||||
| @override | ||||
| Map<String, dynamic> toJson() { | ||||
|   return _$WebFeedConfigToJson(this, ); | ||||
|   return _$SnWebFeedConfigToJson(this, ); | ||||
| } | ||||
|  | ||||
| @override | ||||
| bool operator ==(Object other) { | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is _WebFeedConfig&&(identical(other.scrapPage, scrapPage) || other.scrapPage == scrapPage)); | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnWebFeedConfig&&(identical(other.scrapPage, scrapPage) || other.scrapPage == scrapPage)); | ||||
| } | ||||
|  | ||||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @@ -107,15 +107,15 @@ int get hashCode => Object.hash(runtimeType,scrapPage); | ||||
|  | ||||
| @override | ||||
| String toString() { | ||||
|   return 'WebFeedConfig(scrapPage: $scrapPage)'; | ||||
|   return 'SnWebFeedConfig(scrapPage: $scrapPage)'; | ||||
| } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| /// @nodoc | ||||
| abstract mixin class _$WebFeedConfigCopyWith<$Res> implements $WebFeedConfigCopyWith<$Res> { | ||||
|   factory _$WebFeedConfigCopyWith(_WebFeedConfig value, $Res Function(_WebFeedConfig) _then) = __$WebFeedConfigCopyWithImpl; | ||||
| abstract mixin class _$SnWebFeedConfigCopyWith<$Res> implements $SnWebFeedConfigCopyWith<$Res> { | ||||
|   factory _$SnWebFeedConfigCopyWith(_SnWebFeedConfig value, $Res Function(_SnWebFeedConfig) _then) = __$SnWebFeedConfigCopyWithImpl; | ||||
| @override @useResult | ||||
| $Res call({ | ||||
|  bool scrapPage | ||||
| @@ -126,17 +126,17 @@ $Res call({ | ||||
|  | ||||
| } | ||||
| /// @nodoc | ||||
| class __$WebFeedConfigCopyWithImpl<$Res> | ||||
|     implements _$WebFeedConfigCopyWith<$Res> { | ||||
|   __$WebFeedConfigCopyWithImpl(this._self, this._then); | ||||
| class __$SnWebFeedConfigCopyWithImpl<$Res> | ||||
|     implements _$SnWebFeedConfigCopyWith<$Res> { | ||||
|   __$SnWebFeedConfigCopyWithImpl(this._self, this._then); | ||||
|  | ||||
|   final _WebFeedConfig _self; | ||||
|   final $Res Function(_WebFeedConfig) _then; | ||||
|   final _SnWebFeedConfig _self; | ||||
|   final $Res Function(_SnWebFeedConfig) _then; | ||||
|  | ||||
| /// Create a copy of WebFeedConfig | ||||
| /// Create a copy of SnWebFeedConfig | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override @pragma('vm:prefer-inline') $Res call({Object? scrapPage = null,}) { | ||||
|   return _then(_WebFeedConfig( | ||||
|   return _then(_SnWebFeedConfig( | ||||
| scrapPage: null == scrapPage ? _self.scrapPage : scrapPage // ignore: cast_nullable_to_non_nullable | ||||
| as bool, | ||||
|   )); | ||||
| @@ -147,22 +147,22 @@ as bool, | ||||
|  | ||||
|  | ||||
| /// @nodoc | ||||
| mixin _$WebFeed { | ||||
| mixin _$SnWebFeed { | ||||
|  | ||||
|  String get id; String get url; String get title; String? get description; SnScrappedLink? get preview; WebFeedConfig get config; String get publisherId; List<WebArticle> get articles; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; | ||||
| /// Create a copy of WebFeed | ||||
|  String get id; String get url; String get title; String? get description; SnScrappedLink? get preview; SnWebFeedConfig get config; String get publisherId; List<SnWebArticle> get articles; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; | ||||
| /// Create a copy of SnWebFeed | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @pragma('vm:prefer-inline') | ||||
| $WebFeedCopyWith<WebFeed> get copyWith => _$WebFeedCopyWithImpl<WebFeed>(this as WebFeed, _$identity); | ||||
| $SnWebFeedCopyWith<SnWebFeed> get copyWith => _$SnWebFeedCopyWithImpl<SnWebFeed>(this as SnWebFeed, _$identity); | ||||
|  | ||||
|   /// Serializes this WebFeed to a JSON map. | ||||
|   /// Serializes this SnWebFeed to a JSON map. | ||||
|   Map<String, dynamic> toJson(); | ||||
|  | ||||
|  | ||||
| @override | ||||
| bool operator ==(Object other) { | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is WebFeed&&(identical(other.id, id) || other.id == id)&&(identical(other.url, url) || other.url == url)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.preview, preview) || other.preview == preview)&&(identical(other.config, config) || other.config == config)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&const DeepCollectionEquality().equals(other.articles, articles)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is SnWebFeed&&(identical(other.id, id) || other.id == id)&&(identical(other.url, url) || other.url == url)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.preview, preview) || other.preview == preview)&&(identical(other.config, config) || other.config == config)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&const DeepCollectionEquality().equals(other.articles, articles)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); | ||||
| } | ||||
|  | ||||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @@ -171,33 +171,33 @@ int get hashCode => Object.hash(runtimeType,id,url,title,description,preview,con | ||||
|  | ||||
| @override | ||||
| String toString() { | ||||
|   return 'WebFeed(id: $id, url: $url, title: $title, description: $description, preview: $preview, config: $config, publisherId: $publisherId, articles: $articles, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; | ||||
|   return 'SnWebFeed(id: $id, url: $url, title: $title, description: $description, preview: $preview, config: $config, publisherId: $publisherId, articles: $articles, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; | ||||
| } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| /// @nodoc | ||||
| abstract mixin class $WebFeedCopyWith<$Res>  { | ||||
|   factory $WebFeedCopyWith(WebFeed value, $Res Function(WebFeed) _then) = _$WebFeedCopyWithImpl; | ||||
| abstract mixin class $SnWebFeedCopyWith<$Res>  { | ||||
|   factory $SnWebFeedCopyWith(SnWebFeed value, $Res Function(SnWebFeed) _then) = _$SnWebFeedCopyWithImpl; | ||||
| @useResult | ||||
| $Res call({ | ||||
|  String id, String url, String title, String? description, SnScrappedLink? preview, WebFeedConfig config, String publisherId, List<WebArticle> articles, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt | ||||
|  String id, String url, String title, String? description, SnScrappedLink? preview, SnWebFeedConfig config, String publisherId, List<SnWebArticle> articles, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt | ||||
| }); | ||||
|  | ||||
|  | ||||
| $SnScrappedLinkCopyWith<$Res>? get preview;$WebFeedConfigCopyWith<$Res> get config; | ||||
| $SnScrappedLinkCopyWith<$Res>? get preview;$SnWebFeedConfigCopyWith<$Res> get config; | ||||
|  | ||||
| } | ||||
| /// @nodoc | ||||
| class _$WebFeedCopyWithImpl<$Res> | ||||
|     implements $WebFeedCopyWith<$Res> { | ||||
|   _$WebFeedCopyWithImpl(this._self, this._then); | ||||
| class _$SnWebFeedCopyWithImpl<$Res> | ||||
|     implements $SnWebFeedCopyWith<$Res> { | ||||
|   _$SnWebFeedCopyWithImpl(this._self, this._then); | ||||
|  | ||||
|   final WebFeed _self; | ||||
|   final $Res Function(WebFeed) _then; | ||||
|   final SnWebFeed _self; | ||||
|   final $Res Function(SnWebFeed) _then; | ||||
|  | ||||
| /// Create a copy of WebFeed | ||||
| /// Create a copy of SnWebFeed | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? url = null,Object? title = null,Object? description = freezed,Object? preview = freezed,Object? config = null,Object? publisherId = null,Object? articles = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { | ||||
|   return _then(_self.copyWith( | ||||
| @@ -207,15 +207,15 @@ as String,title: null == title ? _self.title : title // ignore: cast_nullable_to | ||||
| as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable | ||||
| as String?,preview: freezed == preview ? _self.preview : preview // ignore: cast_nullable_to_non_nullable | ||||
| as SnScrappedLink?,config: null == config ? _self.config : config // ignore: cast_nullable_to_non_nullable | ||||
| as WebFeedConfig,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable | ||||
| as SnWebFeedConfig,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable | ||||
| as String,articles: null == articles ? _self.articles : articles // ignore: cast_nullable_to_non_nullable | ||||
| as List<WebArticle>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable | ||||
| as List<SnWebArticle>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable | ||||
| as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable | ||||
| as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable | ||||
| as DateTime?, | ||||
|   )); | ||||
| } | ||||
| /// Create a copy of WebFeed | ||||
| /// Create a copy of SnWebFeed | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override | ||||
| @pragma('vm:prefer-inline') | ||||
| @@ -227,13 +227,13 @@ $SnScrappedLinkCopyWith<$Res>? get preview { | ||||
|   return $SnScrappedLinkCopyWith<$Res>(_self.preview!, (value) { | ||||
|     return _then(_self.copyWith(preview: value)); | ||||
|   }); | ||||
| }/// Create a copy of WebFeed | ||||
| }/// Create a copy of SnWebFeed | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override | ||||
| @pragma('vm:prefer-inline') | ||||
| $WebFeedConfigCopyWith<$Res> get config { | ||||
| $SnWebFeedConfigCopyWith<$Res> get config { | ||||
|    | ||||
|   return $WebFeedConfigCopyWith<$Res>(_self.config, (value) { | ||||
|   return $SnWebFeedConfigCopyWith<$Res>(_self.config, (value) { | ||||
|     return _then(_self.copyWith(config: value)); | ||||
|   }); | ||||
| } | ||||
| @@ -243,19 +243,19 @@ $WebFeedConfigCopyWith<$Res> get config { | ||||
| /// @nodoc | ||||
| @JsonSerializable() | ||||
|  | ||||
| class _WebFeed implements WebFeed { | ||||
|   const _WebFeed({required this.id, required this.url, required this.title, this.description, this.preview, this.config = const WebFeedConfig(), required this.publisherId, final  List<WebArticle> articles = const [], required this.createdAt, required this.updatedAt, this.deletedAt}): _articles = articles; | ||||
|   factory _WebFeed.fromJson(Map<String, dynamic> json) => _$WebFeedFromJson(json); | ||||
| class _SnWebFeed implements SnWebFeed { | ||||
|   const _SnWebFeed({required this.id, required this.url, required this.title, this.description, this.preview, this.config = const SnWebFeedConfig(), required this.publisherId, final  List<SnWebArticle> articles = const [], required this.createdAt, required this.updatedAt, this.deletedAt}): _articles = articles; | ||||
|   factory _SnWebFeed.fromJson(Map<String, dynamic> json) => _$SnWebFeedFromJson(json); | ||||
|  | ||||
| @override final  String id; | ||||
| @override final  String url; | ||||
| @override final  String title; | ||||
| @override final  String? description; | ||||
| @override final  SnScrappedLink? preview; | ||||
| @override@JsonKey() final  WebFeedConfig config; | ||||
| @override@JsonKey() final  SnWebFeedConfig config; | ||||
| @override final  String publisherId; | ||||
|  final  List<WebArticle> _articles; | ||||
| @override@JsonKey() List<WebArticle> get articles { | ||||
|  final  List<SnWebArticle> _articles; | ||||
| @override@JsonKey() List<SnWebArticle> get articles { | ||||
|   if (_articles is EqualUnmodifiableListView) return _articles; | ||||
|   // ignore: implicit_dynamic_type | ||||
|   return EqualUnmodifiableListView(_articles); | ||||
| @@ -265,20 +265,20 @@ class _WebFeed implements WebFeed { | ||||
| @override final  DateTime updatedAt; | ||||
| @override final  DateTime? deletedAt; | ||||
|  | ||||
| /// Create a copy of WebFeed | ||||
| /// Create a copy of SnWebFeed | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @pragma('vm:prefer-inline') | ||||
| _$WebFeedCopyWith<_WebFeed> get copyWith => __$WebFeedCopyWithImpl<_WebFeed>(this, _$identity); | ||||
| _$SnWebFeedCopyWith<_SnWebFeed> get copyWith => __$SnWebFeedCopyWithImpl<_SnWebFeed>(this, _$identity); | ||||
|  | ||||
| @override | ||||
| Map<String, dynamic> toJson() { | ||||
|   return _$WebFeedToJson(this, ); | ||||
|   return _$SnWebFeedToJson(this, ); | ||||
| } | ||||
|  | ||||
| @override | ||||
| bool operator ==(Object other) { | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is _WebFeed&&(identical(other.id, id) || other.id == id)&&(identical(other.url, url) || other.url == url)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.preview, preview) || other.preview == preview)&&(identical(other.config, config) || other.config == config)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&const DeepCollectionEquality().equals(other._articles, _articles)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnWebFeed&&(identical(other.id, id) || other.id == id)&&(identical(other.url, url) || other.url == url)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.preview, preview) || other.preview == preview)&&(identical(other.config, config) || other.config == config)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&const DeepCollectionEquality().equals(other._articles, _articles)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); | ||||
| } | ||||
|  | ||||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @@ -287,52 +287,52 @@ int get hashCode => Object.hash(runtimeType,id,url,title,description,preview,con | ||||
|  | ||||
| @override | ||||
| String toString() { | ||||
|   return 'WebFeed(id: $id, url: $url, title: $title, description: $description, preview: $preview, config: $config, publisherId: $publisherId, articles: $articles, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; | ||||
|   return 'SnWebFeed(id: $id, url: $url, title: $title, description: $description, preview: $preview, config: $config, publisherId: $publisherId, articles: $articles, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; | ||||
| } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| /// @nodoc | ||||
| abstract mixin class _$WebFeedCopyWith<$Res> implements $WebFeedCopyWith<$Res> { | ||||
|   factory _$WebFeedCopyWith(_WebFeed value, $Res Function(_WebFeed) _then) = __$WebFeedCopyWithImpl; | ||||
| abstract mixin class _$SnWebFeedCopyWith<$Res> implements $SnWebFeedCopyWith<$Res> { | ||||
|   factory _$SnWebFeedCopyWith(_SnWebFeed value, $Res Function(_SnWebFeed) _then) = __$SnWebFeedCopyWithImpl; | ||||
| @override @useResult | ||||
| $Res call({ | ||||
|  String id, String url, String title, String? description, SnScrappedLink? preview, WebFeedConfig config, String publisherId, List<WebArticle> articles, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt | ||||
|  String id, String url, String title, String? description, SnScrappedLink? preview, SnWebFeedConfig config, String publisherId, List<SnWebArticle> articles, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt | ||||
| }); | ||||
|  | ||||
|  | ||||
| @override $SnScrappedLinkCopyWith<$Res>? get preview;@override $WebFeedConfigCopyWith<$Res> get config; | ||||
| @override $SnScrappedLinkCopyWith<$Res>? get preview;@override $SnWebFeedConfigCopyWith<$Res> get config; | ||||
|  | ||||
| } | ||||
| /// @nodoc | ||||
| class __$WebFeedCopyWithImpl<$Res> | ||||
|     implements _$WebFeedCopyWith<$Res> { | ||||
|   __$WebFeedCopyWithImpl(this._self, this._then); | ||||
| class __$SnWebFeedCopyWithImpl<$Res> | ||||
|     implements _$SnWebFeedCopyWith<$Res> { | ||||
|   __$SnWebFeedCopyWithImpl(this._self, this._then); | ||||
|  | ||||
|   final _WebFeed _self; | ||||
|   final $Res Function(_WebFeed) _then; | ||||
|   final _SnWebFeed _self; | ||||
|   final $Res Function(_SnWebFeed) _then; | ||||
|  | ||||
| /// Create a copy of WebFeed | ||||
| /// Create a copy of SnWebFeed | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? url = null,Object? title = null,Object? description = freezed,Object? preview = freezed,Object? config = null,Object? publisherId = null,Object? articles = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { | ||||
|   return _then(_WebFeed( | ||||
|   return _then(_SnWebFeed( | ||||
| id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable | ||||
| as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable | ||||
| as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable | ||||
| as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable | ||||
| as String?,preview: freezed == preview ? _self.preview : preview // ignore: cast_nullable_to_non_nullable | ||||
| as SnScrappedLink?,config: null == config ? _self.config : config // ignore: cast_nullable_to_non_nullable | ||||
| as WebFeedConfig,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable | ||||
| as SnWebFeedConfig,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable | ||||
| as String,articles: null == articles ? _self._articles : articles // ignore: cast_nullable_to_non_nullable | ||||
| as List<WebArticle>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable | ||||
| as List<SnWebArticle>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable | ||||
| as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable | ||||
| as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable | ||||
| as DateTime?, | ||||
|   )); | ||||
| } | ||||
|  | ||||
| /// Create a copy of WebFeed | ||||
| /// Create a copy of SnWebFeed | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override | ||||
| @pragma('vm:prefer-inline') | ||||
| @@ -344,13 +344,13 @@ $SnScrappedLinkCopyWith<$Res>? get preview { | ||||
|   return $SnScrappedLinkCopyWith<$Res>(_self.preview!, (value) { | ||||
|     return _then(_self.copyWith(preview: value)); | ||||
|   }); | ||||
| }/// Create a copy of WebFeed | ||||
| }/// Create a copy of SnWebFeed | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override | ||||
| @pragma('vm:prefer-inline') | ||||
| $WebFeedConfigCopyWith<$Res> get config { | ||||
| $SnWebFeedConfigCopyWith<$Res> get config { | ||||
|    | ||||
|   return $WebFeedConfigCopyWith<$Res>(_self.config, (value) { | ||||
|   return $SnWebFeedConfigCopyWith<$Res>(_self.config, (value) { | ||||
|     return _then(_self.copyWith(config: value)); | ||||
|   }); | ||||
| } | ||||
| @@ -358,59 +358,59 @@ $WebFeedConfigCopyWith<$Res> get config { | ||||
|  | ||||
|  | ||||
| /// @nodoc | ||||
| mixin _$WebArticle { | ||||
| mixin _$SnWebArticle { | ||||
|  | ||||
|  String get id; String get title; String get url; String? get author; Map<String, dynamic>? get meta; SnScrappedLink? get preview; String? get content; DateTime? get publishedAt; String get feedId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; | ||||
| /// Create a copy of WebArticle | ||||
|  String get id; String get title; String get url; String? get author; Map<String, dynamic>? get meta; SnScrappedLink? get preview; SnWebFeed? get feed; String? get content; DateTime? get publishedAt; String get feedId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; | ||||
| /// Create a copy of SnWebArticle | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @pragma('vm:prefer-inline') | ||||
| $WebArticleCopyWith<WebArticle> get copyWith => _$WebArticleCopyWithImpl<WebArticle>(this as WebArticle, _$identity); | ||||
| $SnWebArticleCopyWith<SnWebArticle> get copyWith => _$SnWebArticleCopyWithImpl<SnWebArticle>(this as SnWebArticle, _$identity); | ||||
|  | ||||
|   /// Serializes this WebArticle to a JSON map. | ||||
|   /// Serializes this SnWebArticle to a JSON map. | ||||
|   Map<String, dynamic> toJson(); | ||||
|  | ||||
|  | ||||
| @override | ||||
| bool operator ==(Object other) { | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is WebArticle&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.url, url) || other.url == url)&&(identical(other.author, author) || other.author == author)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.preview, preview) || other.preview == preview)&&(identical(other.content, content) || other.content == content)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.feedId, feedId) || other.feedId == feedId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is SnWebArticle&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.url, url) || other.url == url)&&(identical(other.author, author) || other.author == author)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.preview, preview) || other.preview == preview)&&(identical(other.feed, feed) || other.feed == feed)&&(identical(other.content, content) || other.content == content)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.feedId, feedId) || other.feedId == feedId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); | ||||
| } | ||||
|  | ||||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @override | ||||
| int get hashCode => Object.hash(runtimeType,id,title,url,author,const DeepCollectionEquality().hash(meta),preview,content,publishedAt,feedId,createdAt,updatedAt,deletedAt); | ||||
| int get hashCode => Object.hash(runtimeType,id,title,url,author,const DeepCollectionEquality().hash(meta),preview,feed,content,publishedAt,feedId,createdAt,updatedAt,deletedAt); | ||||
|  | ||||
| @override | ||||
| String toString() { | ||||
|   return 'WebArticle(id: $id, title: $title, url: $url, author: $author, meta: $meta, preview: $preview, content: $content, publishedAt: $publishedAt, feedId: $feedId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; | ||||
|   return 'SnWebArticle(id: $id, title: $title, url: $url, author: $author, meta: $meta, preview: $preview, feed: $feed, content: $content, publishedAt: $publishedAt, feedId: $feedId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; | ||||
| } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| /// @nodoc | ||||
| abstract mixin class $WebArticleCopyWith<$Res>  { | ||||
|   factory $WebArticleCopyWith(WebArticle value, $Res Function(WebArticle) _then) = _$WebArticleCopyWithImpl; | ||||
| abstract mixin class $SnWebArticleCopyWith<$Res>  { | ||||
|   factory $SnWebArticleCopyWith(SnWebArticle value, $Res Function(SnWebArticle) _then) = _$SnWebArticleCopyWithImpl; | ||||
| @useResult | ||||
| $Res call({ | ||||
|  String id, String title, String url, String? author, Map<String, dynamic>? meta, SnScrappedLink? preview, String? content, DateTime? publishedAt, String feedId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt | ||||
|  String id, String title, String url, String? author, Map<String, dynamic>? meta, SnScrappedLink? preview, SnWebFeed? feed, String? content, DateTime? publishedAt, String feedId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt | ||||
| }); | ||||
|  | ||||
|  | ||||
| $SnScrappedLinkCopyWith<$Res>? get preview; | ||||
| $SnScrappedLinkCopyWith<$Res>? get preview;$SnWebFeedCopyWith<$Res>? get feed; | ||||
|  | ||||
| } | ||||
| /// @nodoc | ||||
| class _$WebArticleCopyWithImpl<$Res> | ||||
|     implements $WebArticleCopyWith<$Res> { | ||||
|   _$WebArticleCopyWithImpl(this._self, this._then); | ||||
| class _$SnWebArticleCopyWithImpl<$Res> | ||||
|     implements $SnWebArticleCopyWith<$Res> { | ||||
|   _$SnWebArticleCopyWithImpl(this._self, this._then); | ||||
|  | ||||
|   final WebArticle _self; | ||||
|   final $Res Function(WebArticle) _then; | ||||
|   final SnWebArticle _self; | ||||
|   final $Res Function(SnWebArticle) _then; | ||||
|  | ||||
| /// Create a copy of WebArticle | ||||
| /// Create a copy of SnWebArticle | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = null,Object? url = null,Object? author = freezed,Object? meta = freezed,Object? preview = freezed,Object? content = freezed,Object? publishedAt = freezed,Object? feedId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { | ||||
| @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = null,Object? url = null,Object? author = freezed,Object? meta = freezed,Object? preview = freezed,Object? feed = freezed,Object? content = freezed,Object? publishedAt = freezed,Object? feedId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { | ||||
|   return _then(_self.copyWith( | ||||
| id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable | ||||
| as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable | ||||
| @@ -418,7 +418,8 @@ as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nul | ||||
| as String,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable | ||||
| as String?,meta: freezed == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable | ||||
| as Map<String, dynamic>?,preview: freezed == preview ? _self.preview : preview // ignore: cast_nullable_to_non_nullable | ||||
| as SnScrappedLink?,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable | ||||
| as SnScrappedLink?,feed: freezed == feed ? _self.feed : feed // ignore: cast_nullable_to_non_nullable | ||||
| as SnWebFeed?,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable | ||||
| as String?,publishedAt: freezed == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable | ||||
| as DateTime?,feedId: null == feedId ? _self.feedId : feedId // ignore: cast_nullable_to_non_nullable | ||||
| as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable | ||||
| @@ -427,7 +428,7 @@ as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ign | ||||
| as DateTime?, | ||||
|   )); | ||||
| } | ||||
| /// Create a copy of WebArticle | ||||
| /// Create a copy of SnWebArticle | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override | ||||
| @pragma('vm:prefer-inline') | ||||
| @@ -439,6 +440,18 @@ $SnScrappedLinkCopyWith<$Res>? get preview { | ||||
|   return $SnScrappedLinkCopyWith<$Res>(_self.preview!, (value) { | ||||
|     return _then(_self.copyWith(preview: value)); | ||||
|   }); | ||||
| }/// Create a copy of SnWebArticle | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override | ||||
| @pragma('vm:prefer-inline') | ||||
| $SnWebFeedCopyWith<$Res>? get feed { | ||||
|     if (_self.feed == null) { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   return $SnWebFeedCopyWith<$Res>(_self.feed!, (value) { | ||||
|     return _then(_self.copyWith(feed: value)); | ||||
|   }); | ||||
| } | ||||
| } | ||||
|  | ||||
| @@ -446,9 +459,9 @@ $SnScrappedLinkCopyWith<$Res>? get preview { | ||||
| /// @nodoc | ||||
| @JsonSerializable() | ||||
|  | ||||
| class _WebArticle implements WebArticle { | ||||
|   const _WebArticle({required this.id, required this.title, required this.url, this.author, final  Map<String, dynamic>? meta, this.preview, this.content, this.publishedAt, required this.feedId, required this.createdAt, required this.updatedAt, this.deletedAt}): _meta = meta; | ||||
|   factory _WebArticle.fromJson(Map<String, dynamic> json) => _$WebArticleFromJson(json); | ||||
| class _SnWebArticle implements SnWebArticle { | ||||
|   const _SnWebArticle({required this.id, required this.title, required this.url, this.author, final  Map<String, dynamic>? meta, this.preview, this.feed, this.content, this.publishedAt, required this.feedId, required this.createdAt, required this.updatedAt, this.deletedAt}): _meta = meta; | ||||
|   factory _SnWebArticle.fromJson(Map<String, dynamic> json) => _$SnWebArticleFromJson(json); | ||||
|  | ||||
| @override final  String id; | ||||
| @override final  String title; | ||||
| @@ -464,6 +477,7 @@ class _WebArticle implements WebArticle { | ||||
| } | ||||
|  | ||||
| @override final  SnScrappedLink? preview; | ||||
| @override final  SnWebFeed? feed; | ||||
| @override final  String? content; | ||||
| @override final  DateTime? publishedAt; | ||||
| @override final  String feedId; | ||||
| @@ -471,65 +485,66 @@ class _WebArticle implements WebArticle { | ||||
| @override final  DateTime updatedAt; | ||||
| @override final  DateTime? deletedAt; | ||||
|  | ||||
| /// Create a copy of WebArticle | ||||
| /// Create a copy of SnWebArticle | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @pragma('vm:prefer-inline') | ||||
| _$WebArticleCopyWith<_WebArticle> get copyWith => __$WebArticleCopyWithImpl<_WebArticle>(this, _$identity); | ||||
| _$SnWebArticleCopyWith<_SnWebArticle> get copyWith => __$SnWebArticleCopyWithImpl<_SnWebArticle>(this, _$identity); | ||||
|  | ||||
| @override | ||||
| Map<String, dynamic> toJson() { | ||||
|   return _$WebArticleToJson(this, ); | ||||
|   return _$SnWebArticleToJson(this, ); | ||||
| } | ||||
|  | ||||
| @override | ||||
| bool operator ==(Object other) { | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is _WebArticle&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.url, url) || other.url == url)&&(identical(other.author, author) || other.author == author)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.preview, preview) || other.preview == preview)&&(identical(other.content, content) || other.content == content)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.feedId, feedId) || other.feedId == feedId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); | ||||
|   return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnWebArticle&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.url, url) || other.url == url)&&(identical(other.author, author) || other.author == author)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.preview, preview) || other.preview == preview)&&(identical(other.feed, feed) || other.feed == feed)&&(identical(other.content, content) || other.content == content)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.feedId, feedId) || other.feedId == feedId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); | ||||
| } | ||||
|  | ||||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @override | ||||
| int get hashCode => Object.hash(runtimeType,id,title,url,author,const DeepCollectionEquality().hash(_meta),preview,content,publishedAt,feedId,createdAt,updatedAt,deletedAt); | ||||
| int get hashCode => Object.hash(runtimeType,id,title,url,author,const DeepCollectionEquality().hash(_meta),preview,feed,content,publishedAt,feedId,createdAt,updatedAt,deletedAt); | ||||
|  | ||||
| @override | ||||
| String toString() { | ||||
|   return 'WebArticle(id: $id, title: $title, url: $url, author: $author, meta: $meta, preview: $preview, content: $content, publishedAt: $publishedAt, feedId: $feedId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; | ||||
|   return 'SnWebArticle(id: $id, title: $title, url: $url, author: $author, meta: $meta, preview: $preview, feed: $feed, content: $content, publishedAt: $publishedAt, feedId: $feedId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; | ||||
| } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| /// @nodoc | ||||
| abstract mixin class _$WebArticleCopyWith<$Res> implements $WebArticleCopyWith<$Res> { | ||||
|   factory _$WebArticleCopyWith(_WebArticle value, $Res Function(_WebArticle) _then) = __$WebArticleCopyWithImpl; | ||||
| abstract mixin class _$SnWebArticleCopyWith<$Res> implements $SnWebArticleCopyWith<$Res> { | ||||
|   factory _$SnWebArticleCopyWith(_SnWebArticle value, $Res Function(_SnWebArticle) _then) = __$SnWebArticleCopyWithImpl; | ||||
| @override @useResult | ||||
| $Res call({ | ||||
|  String id, String title, String url, String? author, Map<String, dynamic>? meta, SnScrappedLink? preview, String? content, DateTime? publishedAt, String feedId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt | ||||
|  String id, String title, String url, String? author, Map<String, dynamic>? meta, SnScrappedLink? preview, SnWebFeed? feed, String? content, DateTime? publishedAt, String feedId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt | ||||
| }); | ||||
|  | ||||
|  | ||||
| @override $SnScrappedLinkCopyWith<$Res>? get preview; | ||||
| @override $SnScrappedLinkCopyWith<$Res>? get preview;@override $SnWebFeedCopyWith<$Res>? get feed; | ||||
|  | ||||
| } | ||||
| /// @nodoc | ||||
| class __$WebArticleCopyWithImpl<$Res> | ||||
|     implements _$WebArticleCopyWith<$Res> { | ||||
|   __$WebArticleCopyWithImpl(this._self, this._then); | ||||
| class __$SnWebArticleCopyWithImpl<$Res> | ||||
|     implements _$SnWebArticleCopyWith<$Res> { | ||||
|   __$SnWebArticleCopyWithImpl(this._self, this._then); | ||||
|  | ||||
|   final _WebArticle _self; | ||||
|   final $Res Function(_WebArticle) _then; | ||||
|   final _SnWebArticle _self; | ||||
|   final $Res Function(_SnWebArticle) _then; | ||||
|  | ||||
| /// Create a copy of WebArticle | ||||
| /// Create a copy of SnWebArticle | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = null,Object? url = null,Object? author = freezed,Object? meta = freezed,Object? preview = freezed,Object? content = freezed,Object? publishedAt = freezed,Object? feedId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { | ||||
|   return _then(_WebArticle( | ||||
| @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = null,Object? url = null,Object? author = freezed,Object? meta = freezed,Object? preview = freezed,Object? feed = freezed,Object? content = freezed,Object? publishedAt = freezed,Object? feedId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { | ||||
|   return _then(_SnWebArticle( | ||||
| id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable | ||||
| as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable | ||||
| as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable | ||||
| as String,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable | ||||
| as String?,meta: freezed == meta ? _self._meta : meta // ignore: cast_nullable_to_non_nullable | ||||
| as Map<String, dynamic>?,preview: freezed == preview ? _self.preview : preview // ignore: cast_nullable_to_non_nullable | ||||
| as SnScrappedLink?,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable | ||||
| as SnScrappedLink?,feed: freezed == feed ? _self.feed : feed // ignore: cast_nullable_to_non_nullable | ||||
| as SnWebFeed?,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable | ||||
| as String?,publishedAt: freezed == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable | ||||
| as DateTime?,feedId: null == feedId ? _self.feedId : feedId // ignore: cast_nullable_to_non_nullable | ||||
| as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable | ||||
| @@ -539,7 +554,7 @@ as DateTime?, | ||||
|   )); | ||||
| } | ||||
|  | ||||
| /// Create a copy of WebArticle | ||||
| /// Create a copy of SnWebArticle | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override | ||||
| @pragma('vm:prefer-inline') | ||||
| @@ -551,6 +566,18 @@ $SnScrappedLinkCopyWith<$Res>? get preview { | ||||
|   return $SnScrappedLinkCopyWith<$Res>(_self.preview!, (value) { | ||||
|     return _then(_self.copyWith(preview: value)); | ||||
|   }); | ||||
| }/// Create a copy of SnWebArticle | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override | ||||
| @pragma('vm:prefer-inline') | ||||
| $SnWebFeedCopyWith<$Res>? get feed { | ||||
|     if (_self.feed == null) { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   return $SnWebFeedCopyWith<$Res>(_self.feed!, (value) { | ||||
|     return _then(_self.copyWith(feed: value)); | ||||
|   }); | ||||
| } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -6,13 +6,13 @@ part of 'webfeed.dart'; | ||||
| // JsonSerializableGenerator | ||||
| // ************************************************************************** | ||||
|  | ||||
| _WebFeedConfig _$WebFeedConfigFromJson(Map<String, dynamic> json) => | ||||
|     _WebFeedConfig(scrapPage: json['scrap_page'] as bool? ?? false); | ||||
| _SnWebFeedConfig _$SnWebFeedConfigFromJson(Map<String, dynamic> json) => | ||||
|     _SnWebFeedConfig(scrapPage: json['scrap_page'] as bool? ?? false); | ||||
|  | ||||
| Map<String, dynamic> _$WebFeedConfigToJson(_WebFeedConfig instance) => | ||||
| Map<String, dynamic> _$SnWebFeedConfigToJson(_SnWebFeedConfig instance) => | ||||
|     <String, dynamic>{'scrap_page': instance.scrapPage}; | ||||
|  | ||||
| _WebFeed _$WebFeedFromJson(Map<String, dynamic> json) => _WebFeed( | ||||
| _SnWebFeed _$SnWebFeedFromJson(Map<String, dynamic> json) => _SnWebFeed( | ||||
|   id: json['id'] as String, | ||||
|   url: json['url'] as String, | ||||
|   title: json['title'] as String, | ||||
| @@ -23,12 +23,12 @@ _WebFeed _$WebFeedFromJson(Map<String, dynamic> json) => _WebFeed( | ||||
|           : SnScrappedLink.fromJson(json['preview'] as Map<String, dynamic>), | ||||
|   config: | ||||
|       json['config'] == null | ||||
|           ? const WebFeedConfig() | ||||
|           : WebFeedConfig.fromJson(json['config'] as Map<String, dynamic>), | ||||
|           ? const SnWebFeedConfig() | ||||
|           : SnWebFeedConfig.fromJson(json['config'] as Map<String, dynamic>), | ||||
|   publisherId: json['publisher_id'] as String, | ||||
|   articles: | ||||
|       (json['articles'] as List<dynamic>?) | ||||
|           ?.map((e) => WebArticle.fromJson(e as Map<String, dynamic>)) | ||||
|           ?.map((e) => SnWebArticle.fromJson(e as Map<String, dynamic>)) | ||||
|           .toList() ?? | ||||
|       const [], | ||||
|   createdAt: DateTime.parse(json['created_at'] as String), | ||||
| @@ -39,45 +39,53 @@ _WebFeed _$WebFeedFromJson(Map<String, dynamic> json) => _WebFeed( | ||||
|           : DateTime.parse(json['deleted_at'] as String), | ||||
| ); | ||||
|  | ||||
| Map<String, dynamic> _$WebFeedToJson(_WebFeed instance) => <String, dynamic>{ | ||||
|   'id': instance.id, | ||||
|   'url': instance.url, | ||||
|   'title': instance.title, | ||||
|   'description': instance.description, | ||||
|   'preview': instance.preview?.toJson(), | ||||
|   'config': instance.config.toJson(), | ||||
|   'publisher_id': instance.publisherId, | ||||
|   'articles': instance.articles.map((e) => e.toJson()).toList(), | ||||
|   'created_at': instance.createdAt.toIso8601String(), | ||||
|   'updated_at': instance.updatedAt.toIso8601String(), | ||||
|   'deleted_at': instance.deletedAt?.toIso8601String(), | ||||
| }; | ||||
| Map<String, dynamic> _$SnWebFeedToJson(_SnWebFeed instance) => | ||||
|     <String, dynamic>{ | ||||
|       'id': instance.id, | ||||
|       'url': instance.url, | ||||
|       'title': instance.title, | ||||
|       'description': instance.description, | ||||
|       'preview': instance.preview?.toJson(), | ||||
|       'config': instance.config.toJson(), | ||||
|       'publisher_id': instance.publisherId, | ||||
|       'articles': instance.articles.map((e) => e.toJson()).toList(), | ||||
|       'created_at': instance.createdAt.toIso8601String(), | ||||
|       'updated_at': instance.updatedAt.toIso8601String(), | ||||
|       'deleted_at': instance.deletedAt?.toIso8601String(), | ||||
|     }; | ||||
|  | ||||
| _WebArticle _$WebArticleFromJson(Map<String, dynamic> json) => _WebArticle( | ||||
|   id: json['id'] as String, | ||||
|   title: json['title'] as String, | ||||
|   url: json['url'] as String, | ||||
|   author: json['author'] as String?, | ||||
|   meta: json['meta'] as Map<String, dynamic>?, | ||||
|   preview: | ||||
|       json['preview'] == null | ||||
|           ? null | ||||
|           : SnScrappedLink.fromJson(json['preview'] as Map<String, dynamic>), | ||||
|   content: json['content'] as String?, | ||||
|   publishedAt: | ||||
|       json['published_at'] == null | ||||
|           ? null | ||||
|           : DateTime.parse(json['published_at'] as String), | ||||
|   feedId: json['feed_id'] as String, | ||||
|   createdAt: DateTime.parse(json['created_at'] as String), | ||||
|   updatedAt: DateTime.parse(json['updated_at'] as String), | ||||
|   deletedAt: | ||||
|       json['deleted_at'] == null | ||||
|           ? null | ||||
|           : DateTime.parse(json['deleted_at'] as String), | ||||
| ); | ||||
| _SnWebArticle _$SnWebArticleFromJson(Map<String, dynamic> json) => | ||||
|     _SnWebArticle( | ||||
|       id: json['id'] as String, | ||||
|       title: json['title'] as String, | ||||
|       url: json['url'] as String, | ||||
|       author: json['author'] as String?, | ||||
|       meta: json['meta'] as Map<String, dynamic>?, | ||||
|       preview: | ||||
|           json['preview'] == null | ||||
|               ? null | ||||
|               : SnScrappedLink.fromJson( | ||||
|                 json['preview'] as Map<String, dynamic>, | ||||
|               ), | ||||
|       feed: | ||||
|           json['feed'] == null | ||||
|               ? null | ||||
|               : SnWebFeed.fromJson(json['feed'] as Map<String, dynamic>), | ||||
|       content: json['content'] as String?, | ||||
|       publishedAt: | ||||
|           json['published_at'] == null | ||||
|               ? null | ||||
|               : DateTime.parse(json['published_at'] as String), | ||||
|       feedId: json['feed_id'] as String, | ||||
|       createdAt: DateTime.parse(json['created_at'] as String), | ||||
|       updatedAt: DateTime.parse(json['updated_at'] as String), | ||||
|       deletedAt: | ||||
|           json['deleted_at'] == null | ||||
|               ? null | ||||
|               : DateTime.parse(json['deleted_at'] as String), | ||||
|     ); | ||||
|  | ||||
| Map<String, dynamic> _$WebArticleToJson(_WebArticle instance) => | ||||
| Map<String, dynamic> _$SnWebArticleToJson(_SnWebArticle instance) => | ||||
|     <String, dynamic>{ | ||||
|       'id': instance.id, | ||||
|       'title': instance.title, | ||||
| @@ -85,6 +93,7 @@ Map<String, dynamic> _$WebArticleToJson(_WebArticle instance) => | ||||
|       'author': instance.author, | ||||
|       'meta': instance.meta, | ||||
|       'preview': instance.preview?.toJson(), | ||||
|       'feed': instance.feed?.toJson(), | ||||
|       'content': instance.content, | ||||
|       'published_at': instance.publishedAt?.toIso8601String(), | ||||
|       'feed_id': instance.feedId, | ||||
|   | ||||
							
								
								
									
										31
									
								
								lib/pods/article_detail.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/pods/article_detail.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||
| import 'package:dio/dio.dart'; | ||||
| import 'package:island/models/webfeed.dart'; | ||||
| import 'package:island/pods/network.dart'; | ||||
|  | ||||
| /// Provider that fetches a single article by its ID | ||||
| final articleDetailProvider = FutureProvider.autoDispose.family<SnWebArticle, String>( | ||||
|   (ref, articleId) async { | ||||
|     final dio = ref.watch(apiClientProvider); | ||||
|      | ||||
|     try { | ||||
|       final response = await dio.get<Map<String, dynamic>>( | ||||
|         '/feeds/articles/$articleId', | ||||
|       ); | ||||
|        | ||||
|       if (response.statusCode == 200 && response.data != null) { | ||||
|         return SnWebArticle.fromJson(response.data!); | ||||
|       } else { | ||||
|         throw Exception('Failed to load article'); | ||||
|       } | ||||
|     } on DioException catch (e) { | ||||
|       if (e.response?.statusCode == 404) { | ||||
|         throw Exception('Article not found'); | ||||
|       } else { | ||||
|         throw Exception('Failed to load article: ${e.message}'); | ||||
|       } | ||||
|     } catch (e) { | ||||
|       throw Exception('Failed to load article: $e'); | ||||
|     } | ||||
|   }, | ||||
| ); | ||||
							
								
								
									
										1
									
								
								lib/pods/article_list.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								lib/pods/article_list.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
|  | ||||
| @@ -1,28 +1,31 @@ | ||||
| import 'dart:async'; | ||||
|  | ||||
| import 'package:dio/dio.dart'; | ||||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||
| import 'package:island/models/webfeed.dart'; | ||||
| import 'package:island/pods/network.dart'; | ||||
|  | ||||
| final webFeedListProvider = FutureProvider.family<List<WebFeed>, String>(( | ||||
| final webFeedListProvider = FutureProvider.family<List<SnWebFeed>, String>(( | ||||
|   ref, | ||||
|   pubName, | ||||
| ) async { | ||||
|   final client = ref.watch(apiClientProvider); | ||||
|   final response = await client.get('/publishers/$pubName/feeds'); | ||||
|   return (response.data as List).map((json) => WebFeed.fromJson(json)).toList(); | ||||
|   return (response.data as List) | ||||
|       .map((json) => SnWebFeed.fromJson(json)) | ||||
|       .toList(); | ||||
| }); | ||||
|  | ||||
| class WebFeedNotifier | ||||
|     extends | ||||
|         AutoDisposeFamilyAsyncNotifier< | ||||
|           WebFeed, | ||||
|           SnWebFeed, | ||||
|           ({String pubName, String? feedId}) | ||||
|         > { | ||||
|   @override | ||||
|   FutureOr<WebFeed> build(({String pubName, String? feedId}) arg) async { | ||||
|   FutureOr<SnWebFeed> build(({String pubName, String? feedId}) arg) async { | ||||
|     if (arg.feedId == null || arg.feedId!.isEmpty) { | ||||
|       return WebFeed( | ||||
|       return SnWebFeed( | ||||
|         id: '', | ||||
|         url: '', | ||||
|         title: '', | ||||
| @@ -38,13 +41,13 @@ class WebFeedNotifier | ||||
|       final response = await client.get( | ||||
|         '/publishers/${arg.pubName}/feeds/${arg.feedId}', | ||||
|       ); | ||||
|       return WebFeed.fromJson(response.data); | ||||
|       return SnWebFeed.fromJson(response.data); | ||||
|     } catch (e) { | ||||
|       rethrow; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<void> saveFeed(WebFeed feed) async { | ||||
|   Future<void> saveFeed(SnWebFeed feed) async { | ||||
|     state = const AsyncValue.loading(); | ||||
|     try { | ||||
|       final client = ref.read(apiClientProvider); | ||||
| @@ -55,7 +58,7 @@ class WebFeedNotifier | ||||
|               ? await client.post(url, data: feed.toJson()) | ||||
|               : await client.patch('$url/${feed.id}', data: feed.toJson()); | ||||
|  | ||||
|       state = AsyncValue.data(WebFeed.fromJson(response.data)); | ||||
|       state = AsyncValue.data(SnWebFeed.fromJson(response.data)); | ||||
|     } catch (error, stackTrace) { | ||||
|       state = AsyncValue.error(error, stackTrace); | ||||
|       rethrow; | ||||
| @@ -71,7 +74,7 @@ class WebFeedNotifier | ||||
|       final client = ref.read(apiClientProvider); | ||||
|       await client.delete('/publishers/${arg.pubName}/feeds/$feedId'); | ||||
|       state = AsyncValue.data( | ||||
|         WebFeed( | ||||
|         SnWebFeed( | ||||
|           id: '', | ||||
|           url: '', | ||||
|           title: '', | ||||
| @@ -94,13 +97,19 @@ class WebFeedNotifier | ||||
|     state = const AsyncValue.loading(); | ||||
|     try { | ||||
|       final client = ref.read(apiClientProvider); | ||||
|       await client.post('/publishers/${arg.pubName}/feeds/$feedId/scrap'); | ||||
|       await client.post( | ||||
|         '/publishers/${arg.pubName}/feeds/$feedId/scrap', | ||||
|         options: Options( | ||||
|           sendTimeout: const Duration(seconds: 60), | ||||
|           receiveTimeout: const Duration(seconds: 180), | ||||
|         ), | ||||
|       ); | ||||
|  | ||||
|       // Reload the feed | ||||
|       final response = await client.get( | ||||
|         '/publishers/${arg.pubName}/feeds/$feedId', | ||||
|       ); | ||||
|       state = AsyncValue.data(WebFeed.fromJson(response.data)); | ||||
|       state = AsyncValue.data(SnWebFeed.fromJson(response.data)); | ||||
|     } catch (error, stackTrace) { | ||||
|       state = AsyncValue.error(error, stackTrace); | ||||
|       rethrow; | ||||
| @@ -109,6 +118,6 @@ class WebFeedNotifier | ||||
| } | ||||
|  | ||||
| final webFeedNotifierProvider = AsyncNotifierProvider.autoDispose | ||||
|     .family<WebFeedNotifier, WebFeed, ({String pubName, String? feedId})>( | ||||
|     .family<WebFeedNotifier, SnWebFeed, ({String pubName, String? feedId})>( | ||||
|       WebFeedNotifier.new, | ||||
|     ); | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import 'package:island/widgets/app_wrapper.dart'; | ||||
| import 'package:island/screens/tabs.dart'; | ||||
|  | ||||
| import 'package:island/screens/explore.dart'; | ||||
| import 'package:island/screens/article_detail_screen.dart'; | ||||
| import 'package:island/screens/account.dart'; | ||||
| import 'package:island/screens/notification.dart'; | ||||
| import 'package:island/screens/wallet.dart'; | ||||
| @@ -242,6 +243,18 @@ final routerProvider = Provider<GoRouter>((ref) { | ||||
|               return TabsScreen(child: child); | ||||
|             }, | ||||
|             routes: [ | ||||
|               // Article detail route | ||||
|               GoRoute( | ||||
|                 path: '/articles/:id', | ||||
|                 pageBuilder: (context, state) { | ||||
|                   final id = state.pathParameters['id']!; | ||||
|                   return MaterialPage( | ||||
|                     key: state.pageKey, | ||||
|                     child: ArticleDetailScreen(articleId: id), | ||||
|                   ); | ||||
|                 }, | ||||
|               ), | ||||
|  | ||||
|               // Explore tab | ||||
|               ShellRoute( | ||||
|                 builder: | ||||
|   | ||||
							
								
								
									
										105
									
								
								lib/screens/article_detail_screen.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								lib/screens/article_detail_screen.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:gap/gap.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:island/widgets/content/markdown.dart'; | ||||
| import 'package:url_launcher/url_launcher_string.dart'; | ||||
| import 'package:island/models/webfeed.dart'; | ||||
| import 'package:island/pods/article_detail.dart'; | ||||
| import 'package:island/widgets/app_scaffold.dart'; | ||||
| import 'package:island/widgets/loading_indicator.dart'; | ||||
| import 'package:html2md/html2md.dart' as html2md; | ||||
|  | ||||
| class ArticleDetailScreen extends ConsumerWidget { | ||||
|   final String articleId; | ||||
|  | ||||
|   const ArticleDetailScreen({super.key, required this.articleId}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final articleAsync = ref.watch(articleDetailProvider(articleId)); | ||||
|  | ||||
|     return AppScaffold( | ||||
|       body: articleAsync.when( | ||||
|         data: | ||||
|             (article) => AppScaffold( | ||||
|               appBar: AppBar( | ||||
|                 leading: const BackButton(), | ||||
|                 title: Text(article.title), | ||||
|               ), | ||||
|               body: _ArticleDetailContent(article: article), | ||||
|             ), | ||||
|         loading: () => const Center(child: LoadingIndicator()), | ||||
|         error: | ||||
|             (error, stackTrace) => | ||||
|                 Center(child: Text('Failed to load article: $error')), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class _ArticleDetailContent extends HookConsumerWidget { | ||||
|   final SnWebArticle article; | ||||
|  | ||||
|   const _ArticleDetailContent({required this.article}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final markdownContent = useMemoized( | ||||
|       () => html2md.convert(article.content ?? ''), | ||||
|       [article], | ||||
|     ); | ||||
|  | ||||
|     return SingleChildScrollView( | ||||
|       child: Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.stretch, | ||||
|         children: [ | ||||
|           if (article.preview?.imageUrl != null) | ||||
|             Image.network( | ||||
|               article.preview!.imageUrl!, | ||||
|               width: double.infinity, | ||||
|               height: 200, | ||||
|               fit: BoxFit.cover, | ||||
|             ), | ||||
|           Padding( | ||||
|             padding: const EdgeInsets.all(16.0), | ||||
|             child: Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 Text( | ||||
|                   article.title, | ||||
|                   style: Theme.of(context).textTheme.headlineSmall, | ||||
|                 ), | ||||
|                 const SizedBox(height: 8), | ||||
|                 if (article.feed?.title != null) | ||||
|                   Text( | ||||
|                     article.feed!.title, | ||||
|                     style: Theme.of(context).textTheme.bodyMedium?.copyWith( | ||||
|                       color: Theme.of(context).colorScheme.onSurfaceVariant, | ||||
|                     ), | ||||
|                   ), | ||||
|                 const Divider(height: 32), | ||||
|                 if (article.content != null) | ||||
|                   ...MarkdownTextContent.buildGenerator( | ||||
|                     isDark: Theme.of(context).brightness == Brightness.dark, | ||||
|                   ).buildWidgets(markdownContent) | ||||
|                 else if (article.preview?.description != null) | ||||
|                   Text(article.preview!.description!), | ||||
|                 const Gap(24), | ||||
|                 FilledButton( | ||||
|                   onPressed: | ||||
|                       () => launchUrlString( | ||||
|                         article.url, | ||||
|                         mode: LaunchMode.externalApplication, | ||||
|                       ), | ||||
|                   child: const Text('Read Full Article'), | ||||
|                 ), | ||||
|                 Gap(MediaQuery.of(context).padding.bottom), | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -40,12 +40,12 @@ class WebFeedEditScreen extends HookConsumerWidget { | ||||
|       isLoading.value = true; | ||||
|  | ||||
|       try { | ||||
|         final feed = WebFeed( | ||||
|         final feed = SnWebFeed( | ||||
|           id: feedId ?? '', | ||||
|           title: titleController.text, | ||||
|           url: urlController.text, | ||||
|           description: descriptionController.text, | ||||
|           config: WebFeedConfig(scrapPage: isScrapEnabled.value), | ||||
|           config: SnWebFeedConfig(scrapPage: isScrapEnabled.value), | ||||
|           publisherId: pubName, | ||||
|           createdAt: DateTime.now(), | ||||
|           updatedAt: DateTime.now(), | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:island/models/activity.dart'; | ||||
| import 'package:island/models/publisher.dart'; | ||||
| import 'package:island/models/realm.dart'; | ||||
| import 'package:island/models/webfeed.dart'; | ||||
| import 'package:island/pods/userinfo.dart'; | ||||
| import 'package:island/services/responsive.dart'; | ||||
| import 'package:island/widgets/app_scaffold.dart'; | ||||
| @@ -21,6 +22,7 @@ import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; | ||||
| import 'package:island/pods/network.dart'; | ||||
| import 'package:island/widgets/realm/realm_card.dart'; | ||||
| import 'package:island/widgets/publisher/publisher_card.dart'; | ||||
| import 'package:island/widgets/web_article_card.dart'; | ||||
| import 'package:styled_widget/styled_widget.dart'; | ||||
|  | ||||
| part 'explore.g.dart'; | ||||
| @@ -196,6 +198,7 @@ class _DiscoveryActivityItem extends StatelessWidget { | ||||
|               (switch (type) { | ||||
|                 'realm' => 'discoverRealms', | ||||
|                 'publisher' => 'discoverPublishers', | ||||
|                 'article' => 'discoverWebArticles', | ||||
|                 _ => 'unknown', | ||||
|               }).tr(), | ||||
|               style: Theme.of(context).textTheme.titleMedium, | ||||
| @@ -221,6 +224,11 @@ class _DiscoveryActivityItem extends StatelessWidget { | ||||
|                     publisher: SnPublisher.fromJson(item['data']), | ||||
|                     maxWidth: 280, | ||||
|                   ); | ||||
|                 case 'article': | ||||
|                   return WebArticleCard( | ||||
|                     article: SnWebArticle.fromJson(item['data']), | ||||
|                     maxWidth: 280, | ||||
|                   ); | ||||
|                 default: | ||||
|                   return Placeholder(); | ||||
|               } | ||||
| @@ -342,7 +350,7 @@ class ActivityListNotifier extends _$ActivityListNotifier | ||||
|       if (cursor != null) 'cursor': cursor, | ||||
|       'take': take, | ||||
|       if (filter != null) 'filter': filter, | ||||
|       if (kDebugMode) 'debugInclude': 'realms,publishers', | ||||
|       if (kDebugMode) 'debugInclude': 'realms,publishers,articles', | ||||
|     }; | ||||
|  | ||||
|     final response = await client.get( | ||||
|   | ||||
| @@ -7,7 +7,7 @@ part of 'explore.dart'; | ||||
| // ************************************************************************** | ||||
|  | ||||
| String _$activityListNotifierHash() => | ||||
|     r'57e9dcec944a9f88f8508b69fc91342592f5b349'; | ||||
|     r'98b62fb9b958023d2c9e320af7ec1f1244836f49'; | ||||
|  | ||||
| /// Copied from Dart SDK | ||||
| class _SystemHash { | ||||
|   | ||||
							
								
								
									
										1
									
								
								lib/widgets/article/article_list.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								lib/widgets/article/article_list.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
|  | ||||
| @@ -74,9 +74,7 @@ class MarkdownTextContent extends HookConsumerWidget { | ||||
|               final url = Uri.tryParse(href); | ||||
|               if (url != null) { | ||||
|                 if (url.scheme == 'solian') { | ||||
|                   context.push( | ||||
|                     ['', url.host, ...url.pathSegments].join('/'), | ||||
|                   ); | ||||
|                   context.push(['', url.host, ...url.pathSegments].join('/')); | ||||
|                   return; | ||||
|                 } | ||||
|                 final whitelistDomains = ['solian.app', 'solsynth.dev']; | ||||
| @@ -143,17 +141,27 @@ class MarkdownTextContent extends HookConsumerWidget { | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|       generator: MarkdownGenerator( | ||||
|         generators: [latexGenerator], | ||||
|         inlineSyntaxList: [ | ||||
|           _UserNameCardInlineSyntax(), | ||||
|           _StickerInlineSyntax(), | ||||
|           LatexSyntax(isDark), | ||||
|         ], | ||||
|         linesMargin: linesMargin ?? EdgeInsets.symmetric(vertical: 4), | ||||
|       generator: MarkdownTextContent.buildGenerator( | ||||
|         isDark: isDark, | ||||
|         linesMargin: linesMargin, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   static MarkdownGenerator buildGenerator({ | ||||
|     bool isDark = false, | ||||
|     EdgeInsets? linesMargin, | ||||
|   }) { | ||||
|     return MarkdownGenerator( | ||||
|       generators: [latexGenerator], | ||||
|       inlineSyntaxList: [ | ||||
|         _UserNameCardInlineSyntax(), | ||||
|         _StickerInlineSyntax(), | ||||
|         LatexSyntax(isDark), | ||||
|       ], | ||||
|       linesMargin: linesMargin ?? EdgeInsets.symmetric(vertical: 4), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class _UserNameCardInlineSyntax extends markdown.InlineSyntax { | ||||
|   | ||||
							
								
								
									
										33
									
								
								lib/widgets/loading_indicator.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								lib/widgets/loading_indicator.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| /// A simple loading indicator widget that can be used throughout the app | ||||
| class LoadingIndicator extends StatelessWidget { | ||||
|   /// The size of the loading indicator | ||||
|   final double size; | ||||
|  | ||||
|   /// The color of the loading indicator | ||||
|   final Color? color; | ||||
|  | ||||
|   /// Creates a loading indicator | ||||
|   const LoadingIndicator({ | ||||
|     super.key, | ||||
|     this.size = 24.0, | ||||
|     this.color, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return SizedBox( | ||||
|       width: size, | ||||
|       height: size, | ||||
|       child: CircularProgressIndicator( | ||||
|         strokeWidth: 2.0, | ||||
|         valueColor: color != null | ||||
|             ? AlwaysStoppedAnimation<Color>( | ||||
|                 color!, | ||||
|               ) | ||||
|             : null, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										106
									
								
								lib/widgets/web_article_card.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								lib/widgets/web_article_card.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| import 'package:cached_network_image/cached_network_image.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:island/models/webfeed.dart'; | ||||
|  | ||||
| class WebArticleCard extends StatelessWidget { | ||||
|   final SnWebArticle article; | ||||
|   final double? maxWidth; | ||||
|  | ||||
|   const WebArticleCard({super.key, required this.article, this.maxWidth}); | ||||
|  | ||||
|   void _onTap(BuildContext context) { | ||||
|     context.push('/articles/${article.id}'); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final theme = Theme.of(context); | ||||
|     final colorScheme = theme.colorScheme; | ||||
|  | ||||
|     return ConstrainedBox( | ||||
|       constraints: BoxConstraints(maxWidth: maxWidth ?? double.infinity), | ||||
|       child: Card( | ||||
|         clipBehavior: Clip.antiAlias, | ||||
|         child: InkWell( | ||||
|           onTap: () => _onTap(context), | ||||
|           child: AspectRatio( | ||||
|             aspectRatio: 16 / 9, | ||||
|             child: Stack( | ||||
|               fit: StackFit.expand, | ||||
|               children: [ | ||||
|                 // Image or fallback | ||||
|                 article.preview?.imageUrl != null | ||||
|                     ? CachedNetworkImage( | ||||
|                       imageUrl: article.preview!.imageUrl!, | ||||
|                       fit: BoxFit.cover, | ||||
|                       width: double.infinity, | ||||
|                       height: double.infinity, | ||||
|                     ) | ||||
|                     : ColoredBox( | ||||
|                       color: colorScheme.secondaryContainer, | ||||
|                       child: const Center( | ||||
|                         child: Icon( | ||||
|                           Icons.article_outlined, | ||||
|                           size: 48, | ||||
|                           color: Colors.white, | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                 // Gradient overlay | ||||
|                 Container( | ||||
|                   decoration: BoxDecoration( | ||||
|                     gradient: LinearGradient( | ||||
|                       begin: Alignment.topCenter, | ||||
|                       end: Alignment.bottomCenter, | ||||
|                       colors: [ | ||||
|                         Colors.transparent, | ||||
|                         Colors.black.withOpacity(0.7), | ||||
|                       ], | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|                 // Title | ||||
|                 Align( | ||||
|                   alignment: Alignment.bottomLeft, | ||||
|                   child: Container( | ||||
|                     padding: const EdgeInsets.only( | ||||
|                       left: 12, | ||||
|                       right: 12, | ||||
|                       bottom: 8, | ||||
|                     ), | ||||
|                     child: Column( | ||||
|                       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                       mainAxisAlignment: MainAxisAlignment.end, | ||||
|                       mainAxisSize: MainAxisSize.min, | ||||
|                       children: [ | ||||
|                         Text( | ||||
|                           article.title, | ||||
|                           style: theme.textTheme.titleSmall?.copyWith( | ||||
|                             color: Colors.white, | ||||
|                             fontWeight: FontWeight.bold, | ||||
|                             height: 1.3, | ||||
|                           ), | ||||
|                           maxLines: 2, | ||||
|                           overflow: TextOverflow.ellipsis, | ||||
|                         ), | ||||
|                         const SizedBox(height: 2), | ||||
|                         Text( | ||||
|                           article.feed?.title ?? 'Unknown Source', | ||||
|                           style: const TextStyle( | ||||
|                             fontSize: 9, | ||||
|                             color: Colors.white70, | ||||
|                           ), | ||||
|                         ), | ||||
|                       ], | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1093,6 +1093,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.15.6" | ||||
|   html2md: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: html2md | ||||
|       sha256: "465cf8ffa1b510fe0e97941579bf5b22e2d575f2cecb500a9c0254efe33a8036" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.3.2" | ||||
|   http: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -127,6 +127,7 @@ dependencies: | ||||
|       url: https://github.com/lionelmennig/textfield_tags.git | ||||
|       ref: fixes/allow-controller-re-registration | ||||
|   mime: ^2.0.0 | ||||
|   html2md: ^1.3.2 | ||||
|  | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user