From fafa460fe8b87d9d4fa8ac509d6a621b6295676d Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 20 Nov 2025 21:29:08 +0800 Subject: [PATCH] :sparkles: Site basis --- assets/i18n/en-US.json | 1 + lib/models/publication_site.dart | 40 ++ lib/models/publication_site.freezed.dart | 584 ++++++++++++++++++++ lib/models/publication_site.g.dart | 58 ++ lib/pods/chat/messages_notifier.g.dart | 2 +- lib/pods/file_references.g.dart | 2 +- lib/pods/sites.dart | 88 +++ lib/route.dart | 10 + lib/screens/chat/chat.g.dart | 2 +- lib/screens/creators/hub.dart | 17 +- lib/screens/creators/sites/site_edit.dart | 252 +++++++++ lib/screens/creators/sites/site_list.dart | 230 ++++++++ lib/screens/creators/sites/site_list.g.dart | 183 ++++++ 13 files changed, 1465 insertions(+), 4 deletions(-) create mode 100644 lib/models/publication_site.dart create mode 100644 lib/models/publication_site.freezed.dart create mode 100644 lib/models/publication_site.g.dart create mode 100644 lib/pods/sites.dart create mode 100644 lib/screens/creators/sites/site_edit.dart create mode 100644 lib/screens/creators/sites/site_list.dart create mode 100644 lib/screens/creators/sites/site_list.g.dart diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index d49b4c00..1a4ba149 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -180,6 +180,7 @@ "noFortuneData": "No fortune data available for this month.", "creatorHub": "Creator Hub", "creatorHubDescription": "Manage posts, analytics, and more.", + "publicationSites": "Publication Sites", "developerPortal": "Developer Portal", "developerPortalDescription": "Build with Solar Network™.", "statusCreateHint": "What's on your mind? Add a status.", diff --git a/lib/models/publication_site.dart b/lib/models/publication_site.dart new file mode 100644 index 00000000..dcaf101c --- /dev/null +++ b/lib/models/publication_site.dart @@ -0,0 +1,40 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'publication_site.freezed.dart'; +part 'publication_site.g.dart'; + +@freezed +sealed class SnPublicationSite with _$SnPublicationSite { + const factory SnPublicationSite({ + required String id, + required String slug, + required String name, + String? description, + required String publisherId, + required String accountId, + required DateTime createdAt, + required DateTime updatedAt, + required List pages, + }) = _SnPublicationSite; + + factory SnPublicationSite.fromJson(Map json) => + _$SnPublicationSiteFromJson(json); +} + +@freezed +sealed class SnPublicationPage with _$SnPublicationPage { + const factory SnPublicationPage({ + required String id, + String? preset, + String? path, + Map? config, + required String siteId, + required DateTime createdAt, + required DateTime updatedAt, + }) = _SnPublicationPage; + + factory SnPublicationPage.fromJson(Map json) => + _$SnPublicationPageFromJson(json); +} + +enum PublicationPagePreset { landing, profile, posts, custom } diff --git a/lib/models/publication_site.freezed.dart b/lib/models/publication_site.freezed.dart new file mode 100644 index 00000000..ebc50834 --- /dev/null +++ b/lib/models/publication_site.freezed.dart @@ -0,0 +1,584 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'publication_site.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$SnPublicationSite { + + String get id; String get slug; String get name; String? get description; String get publisherId; String get accountId; DateTime get createdAt; DateTime get updatedAt; List get pages; +/// Create a copy of SnPublicationSite +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SnPublicationSiteCopyWith get copyWith => _$SnPublicationSiteCopyWithImpl(this as SnPublicationSite, _$identity); + + /// Serializes this SnPublicationSite to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPublicationSite&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&const DeepCollectionEquality().equals(other.pages, pages)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,slug,name,description,publisherId,accountId,createdAt,updatedAt,const DeepCollectionEquality().hash(pages)); + +@override +String toString() { + return 'SnPublicationSite(id: $id, slug: $slug, name: $name, description: $description, publisherId: $publisherId, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, pages: $pages)'; +} + + +} + +/// @nodoc +abstract mixin class $SnPublicationSiteCopyWith<$Res> { + factory $SnPublicationSiteCopyWith(SnPublicationSite value, $Res Function(SnPublicationSite) _then) = _$SnPublicationSiteCopyWithImpl; +@useResult +$Res call({ + String id, String slug, String name, String? description, String publisherId, String accountId, DateTime createdAt, DateTime updatedAt, List pages +}); + + + + +} +/// @nodoc +class _$SnPublicationSiteCopyWithImpl<$Res> + implements $SnPublicationSiteCopyWith<$Res> { + _$SnPublicationSiteCopyWithImpl(this._self, this._then); + + final SnPublicationSite _self; + final $Res Function(SnPublicationSite) _then; + +/// Create a copy of SnPublicationSite +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? name = null,Object? description = freezed,Object? publisherId = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? pages = null,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable +as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable +as String,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,pages: null == pages ? _self.pages : pages // ignore: cast_nullable_to_non_nullable +as List, + )); +} + +} + + +/// Adds pattern-matching-related methods to [SnPublicationSite]. +extension SnPublicationSitePatterns on SnPublicationSite { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _SnPublicationSite value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _SnPublicationSite() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _SnPublicationSite value) $default,){ +final _that = this; +switch (_that) { +case _SnPublicationSite(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SnPublicationSite value)? $default,){ +final _that = this; +switch (_that) { +case _SnPublicationSite() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String slug, String name, String? description, String publisherId, String accountId, DateTime createdAt, DateTime updatedAt, List pages)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _SnPublicationSite() when $default != null: +return $default(_that.id,_that.slug,_that.name,_that.description,_that.publisherId,_that.accountId,_that.createdAt,_that.updatedAt,_that.pages);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, String slug, String name, String? description, String publisherId, String accountId, DateTime createdAt, DateTime updatedAt, List pages) $default,) {final _that = this; +switch (_that) { +case _SnPublicationSite(): +return $default(_that.id,_that.slug,_that.name,_that.description,_that.publisherId,_that.accountId,_that.createdAt,_that.updatedAt,_that.pages);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String slug, String name, String? description, String publisherId, String accountId, DateTime createdAt, DateTime updatedAt, List pages)? $default,) {final _that = this; +switch (_that) { +case _SnPublicationSite() when $default != null: +return $default(_that.id,_that.slug,_that.name,_that.description,_that.publisherId,_that.accountId,_that.createdAt,_that.updatedAt,_that.pages);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _SnPublicationSite implements SnPublicationSite { + const _SnPublicationSite({required this.id, required this.slug, required this.name, this.description, required this.publisherId, required this.accountId, required this.createdAt, required this.updatedAt, required final List pages}): _pages = pages; + factory _SnPublicationSite.fromJson(Map json) => _$SnPublicationSiteFromJson(json); + +@override final String id; +@override final String slug; +@override final String name; +@override final String? description; +@override final String publisherId; +@override final String accountId; +@override final DateTime createdAt; +@override final DateTime updatedAt; + final List _pages; +@override List get pages { + if (_pages is EqualUnmodifiableListView) return _pages; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_pages); +} + + +/// Create a copy of SnPublicationSite +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SnPublicationSiteCopyWith<_SnPublicationSite> get copyWith => __$SnPublicationSiteCopyWithImpl<_SnPublicationSite>(this, _$identity); + +@override +Map toJson() { + return _$SnPublicationSiteToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPublicationSite&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&const DeepCollectionEquality().equals(other._pages, _pages)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,slug,name,description,publisherId,accountId,createdAt,updatedAt,const DeepCollectionEquality().hash(_pages)); + +@override +String toString() { + return 'SnPublicationSite(id: $id, slug: $slug, name: $name, description: $description, publisherId: $publisherId, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, pages: $pages)'; +} + + +} + +/// @nodoc +abstract mixin class _$SnPublicationSiteCopyWith<$Res> implements $SnPublicationSiteCopyWith<$Res> { + factory _$SnPublicationSiteCopyWith(_SnPublicationSite value, $Res Function(_SnPublicationSite) _then) = __$SnPublicationSiteCopyWithImpl; +@override @useResult +$Res call({ + String id, String slug, String name, String? description, String publisherId, String accountId, DateTime createdAt, DateTime updatedAt, List pages +}); + + + + +} +/// @nodoc +class __$SnPublicationSiteCopyWithImpl<$Res> + implements _$SnPublicationSiteCopyWith<$Res> { + __$SnPublicationSiteCopyWithImpl(this._self, this._then); + + final _SnPublicationSite _self; + final $Res Function(_SnPublicationSite) _then; + +/// Create a copy of SnPublicationSite +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? name = null,Object? description = freezed,Object? publisherId = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? pages = null,}) { + return _then(_SnPublicationSite( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable +as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable +as String,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,pages: null == pages ? _self._pages : pages // ignore: cast_nullable_to_non_nullable +as List, + )); +} + + +} + + +/// @nodoc +mixin _$SnPublicationPage { + + String get id; String? get preset; String? get path; Map? get config; String get siteId; DateTime get createdAt; DateTime get updatedAt; +/// Create a copy of SnPublicationPage +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SnPublicationPageCopyWith get copyWith => _$SnPublicationPageCopyWithImpl(this as SnPublicationPage, _$identity); + + /// Serializes this SnPublicationPage to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPublicationPage&&(identical(other.id, id) || other.id == id)&&(identical(other.preset, preset) || other.preset == preset)&&(identical(other.path, path) || other.path == path)&&const DeepCollectionEquality().equals(other.config, config)&&(identical(other.siteId, siteId) || other.siteId == siteId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,preset,path,const DeepCollectionEquality().hash(config),siteId,createdAt,updatedAt); + +@override +String toString() { + return 'SnPublicationPage(id: $id, preset: $preset, path: $path, config: $config, siteId: $siteId, createdAt: $createdAt, updatedAt: $updatedAt)'; +} + + +} + +/// @nodoc +abstract mixin class $SnPublicationPageCopyWith<$Res> { + factory $SnPublicationPageCopyWith(SnPublicationPage value, $Res Function(SnPublicationPage) _then) = _$SnPublicationPageCopyWithImpl; +@useResult +$Res call({ + String id, String? preset, String? path, Map? config, String siteId, DateTime createdAt, DateTime updatedAt +}); + + + + +} +/// @nodoc +class _$SnPublicationPageCopyWithImpl<$Res> + implements $SnPublicationPageCopyWith<$Res> { + _$SnPublicationPageCopyWithImpl(this._self, this._then); + + final SnPublicationPage _self; + final $Res Function(SnPublicationPage) _then; + +/// Create a copy of SnPublicationPage +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? preset = freezed,Object? path = freezed,Object? config = freezed,Object? siteId = null,Object? createdAt = null,Object? updatedAt = null,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,preset: freezed == preset ? _self.preset : preset // ignore: cast_nullable_to_non_nullable +as String?,path: freezed == path ? _self.path : path // ignore: cast_nullable_to_non_nullable +as String?,config: freezed == config ? _self.config : config // ignore: cast_nullable_to_non_nullable +as Map?,siteId: null == siteId ? _self.siteId : siteId // ignore: cast_nullable_to_non_nullable +as String,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, + )); +} + +} + + +/// Adds pattern-matching-related methods to [SnPublicationPage]. +extension SnPublicationPagePatterns on SnPublicationPage { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _SnPublicationPage value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _SnPublicationPage() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _SnPublicationPage value) $default,){ +final _that = this; +switch (_that) { +case _SnPublicationPage(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SnPublicationPage value)? $default,){ +final _that = this; +switch (_that) { +case _SnPublicationPage() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String? preset, String? path, Map? config, String siteId, DateTime createdAt, DateTime updatedAt)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _SnPublicationPage() when $default != null: +return $default(_that.id,_that.preset,_that.path,_that.config,_that.siteId,_that.createdAt,_that.updatedAt);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, String? preset, String? path, Map? config, String siteId, DateTime createdAt, DateTime updatedAt) $default,) {final _that = this; +switch (_that) { +case _SnPublicationPage(): +return $default(_that.id,_that.preset,_that.path,_that.config,_that.siteId,_that.createdAt,_that.updatedAt);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String? preset, String? path, Map? config, String siteId, DateTime createdAt, DateTime updatedAt)? $default,) {final _that = this; +switch (_that) { +case _SnPublicationPage() when $default != null: +return $default(_that.id,_that.preset,_that.path,_that.config,_that.siteId,_that.createdAt,_that.updatedAt);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _SnPublicationPage implements SnPublicationPage { + const _SnPublicationPage({required this.id, this.preset, this.path, final Map? config, required this.siteId, required this.createdAt, required this.updatedAt}): _config = config; + factory _SnPublicationPage.fromJson(Map json) => _$SnPublicationPageFromJson(json); + +@override final String id; +@override final String? preset; +@override final String? path; + final Map? _config; +@override Map? get config { + final value = _config; + if (value == null) return null; + if (_config is EqualUnmodifiableMapView) return _config; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); +} + +@override final String siteId; +@override final DateTime createdAt; +@override final DateTime updatedAt; + +/// Create a copy of SnPublicationPage +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SnPublicationPageCopyWith<_SnPublicationPage> get copyWith => __$SnPublicationPageCopyWithImpl<_SnPublicationPage>(this, _$identity); + +@override +Map toJson() { + return _$SnPublicationPageToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPublicationPage&&(identical(other.id, id) || other.id == id)&&(identical(other.preset, preset) || other.preset == preset)&&(identical(other.path, path) || other.path == path)&&const DeepCollectionEquality().equals(other._config, _config)&&(identical(other.siteId, siteId) || other.siteId == siteId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,preset,path,const DeepCollectionEquality().hash(_config),siteId,createdAt,updatedAt); + +@override +String toString() { + return 'SnPublicationPage(id: $id, preset: $preset, path: $path, config: $config, siteId: $siteId, createdAt: $createdAt, updatedAt: $updatedAt)'; +} + + +} + +/// @nodoc +abstract mixin class _$SnPublicationPageCopyWith<$Res> implements $SnPublicationPageCopyWith<$Res> { + factory _$SnPublicationPageCopyWith(_SnPublicationPage value, $Res Function(_SnPublicationPage) _then) = __$SnPublicationPageCopyWithImpl; +@override @useResult +$Res call({ + String id, String? preset, String? path, Map? config, String siteId, DateTime createdAt, DateTime updatedAt +}); + + + + +} +/// @nodoc +class __$SnPublicationPageCopyWithImpl<$Res> + implements _$SnPublicationPageCopyWith<$Res> { + __$SnPublicationPageCopyWithImpl(this._self, this._then); + + final _SnPublicationPage _self; + final $Res Function(_SnPublicationPage) _then; + +/// Create a copy of SnPublicationPage +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? preset = freezed,Object? path = freezed,Object? config = freezed,Object? siteId = null,Object? createdAt = null,Object? updatedAt = null,}) { + return _then(_SnPublicationPage( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,preset: freezed == preset ? _self.preset : preset // ignore: cast_nullable_to_non_nullable +as String?,path: freezed == path ? _self.path : path // ignore: cast_nullable_to_non_nullable +as String?,config: freezed == config ? _self._config : config // ignore: cast_nullable_to_non_nullable +as Map?,siteId: null == siteId ? _self.siteId : siteId // ignore: cast_nullable_to_non_nullable +as String,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, + )); +} + + +} + +// dart format on diff --git a/lib/models/publication_site.g.dart b/lib/models/publication_site.g.dart new file mode 100644 index 00000000..150f3e9d --- /dev/null +++ b/lib/models/publication_site.g.dart @@ -0,0 +1,58 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'publication_site.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_SnPublicationSite _$SnPublicationSiteFromJson(Map json) => + _SnPublicationSite( + id: json['id'] as String, + slug: json['slug'] as String, + name: json['name'] as String, + description: json['description'] as String?, + publisherId: json['publisher_id'] as String, + accountId: json['account_id'] as String, + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + pages: + (json['pages'] as List) + .map((e) => SnPublicationPage.fromJson(e as Map)) + .toList(), + ); + +Map _$SnPublicationSiteToJson(_SnPublicationSite instance) => + { + 'id': instance.id, + 'slug': instance.slug, + 'name': instance.name, + 'description': instance.description, + 'publisher_id': instance.publisherId, + 'account_id': instance.accountId, + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + 'pages': instance.pages.map((e) => e.toJson()).toList(), + }; + +_SnPublicationPage _$SnPublicationPageFromJson(Map json) => + _SnPublicationPage( + id: json['id'] as String, + preset: json['preset'] as String?, + path: json['path'] as String?, + config: json['config'] as Map?, + siteId: json['site_id'] as String, + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + ); + +Map _$SnPublicationPageToJson(_SnPublicationPage instance) => + { + 'id': instance.id, + 'preset': instance.preset, + 'path': instance.path, + 'config': instance.config, + 'site_id': instance.siteId, + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + }; diff --git a/lib/pods/chat/messages_notifier.g.dart b/lib/pods/chat/messages_notifier.g.dart index 4179d39a..a469efce 100644 --- a/lib/pods/chat/messages_notifier.g.dart +++ b/lib/pods/chat/messages_notifier.g.dart @@ -6,7 +6,7 @@ part of 'messages_notifier.dart'; // RiverpodGenerator // ************************************************************************** -String _$messagesNotifierHash() => r'fc9c99024a0801efa4894f250aea8bdc6127a0b6'; +String _$messagesNotifierHash() => r'27e5d686d9204ba39adbd1838cf4a6eaea0ac85f'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/pods/file_references.g.dart b/lib/pods/file_references.g.dart index ee3646ce..03a4d059 100644 --- a/lib/pods/file_references.g.dart +++ b/lib/pods/file_references.g.dart @@ -6,7 +6,7 @@ part of 'file_references.dart'; // RiverpodGenerator // ************************************************************************** -String _$fileReferencesHash() => r'464562fbdc9452d8a5ffbd2d9d9343cdb43f1876'; +String _$fileReferencesHash() => r'd66c678c221f61978bdb242b98e6dbe31d0c204b'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/pods/sites.dart b/lib/pods/sites.dart new file mode 100644 index 00000000..ac6ee675 --- /dev/null +++ b/lib/pods/sites.dart @@ -0,0 +1,88 @@ +import 'dart:async'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:island/models/publication_site.dart'; +import 'package:island/pods/network.dart'; + +class SiteNotifier + extends + AutoDisposeFamilyAsyncNotifier< + SnPublicationSite, + ({String pubName, String? siteId}) + > { + @override + FutureOr build( + ({String pubName, String? siteId}) arg, + ) async { + if (arg.siteId == null || arg.siteId!.isEmpty) { + return SnPublicationSite( + id: '', + slug: '', + name: '', + publisherId: arg.pubName, + accountId: '', + createdAt: DateTime.now(), + updatedAt: DateTime.now(), + pages: [], + ); + } + + try { + final client = ref.read(apiClientProvider); + final response = await client.get('/sphere/sites/${arg.siteId}'); + return SnPublicationSite.fromJson(response.data); + } catch (e) { + rethrow; + } + } + + Future saveSite(SnPublicationSite site) async { + state = const AsyncValue.loading(); + try { + final client = ref.read(apiClientProvider); + final url = '/sphere/sites'; + + final response = + site.id.isEmpty + ? await client.post(url, data: site.toJson()) + : await client.patch('${url}/${site.id}', data: site.toJson()); + + state = AsyncValue.data(SnPublicationSite.fromJson(response.data)); + } catch (error, stackTrace) { + state = AsyncValue.error(error, stackTrace); + rethrow; + } + } + + Future deleteSite() async { + final siteId = arg.siteId; + if (siteId == null || siteId.isEmpty) return; + + state = const AsyncValue.loading(); + try { + final client = ref.read(apiClientProvider); + await client.delete('/sphere/sites/$siteId'); + state = AsyncValue.data( + SnPublicationSite( + id: '', + slug: '', + name: '', + publisherId: arg.pubName, + accountId: '', + createdAt: DateTime.now(), + updatedAt: DateTime.now(), + pages: [], + ), + ); + } catch (error, stackTrace) { + state = AsyncValue.error(error, stackTrace); + rethrow; + } + } +} + +final siteNotifierProvider = AsyncNotifierProvider.autoDispose.family< + SiteNotifier, + SnPublicationSite, + ({String pubName, String? siteId}) +>(SiteNotifier.new); diff --git a/lib/route.dart b/lib/route.dart index a8c8e45e..81213b96 100644 --- a/lib/route.dart +++ b/lib/route.dart @@ -43,6 +43,7 @@ import 'package:island/screens/stickers/pack_detail.dart'; import 'package:island/screens/discovery/feeds/feed_marketplace.dart'; import 'package:island/screens/discovery/feeds/feed_detail.dart'; import 'package:island/screens/creators/poll/poll_list.dart'; +import 'package:island/screens/creators/sites/site_list.dart'; import 'package:island/screens/creators/webfeed/webfeed_list.dart'; import 'package:island/screens/posts/compose.dart'; import 'package:island/screens/posts/compose_article.dart'; @@ -484,6 +485,15 @@ final routerProvider = Provider((ref) { return CreatorPollListScreen(pubName: name); }, ), + // Site list route + GoRoute( + name: 'creatorSites', + path: ':name/sites', + builder: (context, state) { + final name = state.pathParameters['name']!; + return CreatorSiteListScreen(pubName: name); + }, + ), GoRoute( name: 'creatorStickers', diff --git a/lib/screens/chat/chat.g.dart b/lib/screens/chat/chat.g.dart index 11c98dce..0c7dcdc0 100644 --- a/lib/screens/chat/chat.g.dart +++ b/lib/screens/chat/chat.g.dart @@ -6,7 +6,7 @@ part of 'chat.dart'; // RiverpodGenerator // ************************************************************************** -String _$chatroomsJoinedHash() => r'3bb6389af07e81007680484d04bf5fe6f6c10571'; +String _$chatroomsJoinedHash() => r'9523efecd1869e7dd26adfc8ec87be48db19ee1c'; /// See also [chatroomsJoined]. @ProviderFor(chatroomsJoined) diff --git a/lib/screens/creators/hub.dart b/lib/screens/creators/hub.dart index 289e47b8..270e2416 100644 --- a/lib/screens/creators/hub.dart +++ b/lib/screens/creators/hub.dart @@ -403,6 +403,21 @@ class CreatorHubScreen extends HookConsumerWidget { ); }, ), + ListTile( + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + ), + minTileHeight: 48, + title: Text('publicationSites').tr(), + trailing: const Icon(Symbols.chevron_right), + leading: const Icon(Symbols.web), + onTap: () { + context.pushNamed( + 'creatorSites', + pathParameters: {'name': currentPublisher.value!.name}, + ); + }, + ), ListTile( shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(8)), @@ -585,7 +600,7 @@ class CreatorHubScreen extends HookConsumerWidget { ).padding(horizontal: 12), buildNavigationWidget(true), ], - ) + ).padding(vertical: 24) : Column( spacing: 12, children: [ diff --git a/lib/screens/creators/sites/site_edit.dart b/lib/screens/creators/sites/site_edit.dart new file mode 100644 index 00000000..7ee2449b --- /dev/null +++ b/lib/screens/creators/sites/site_edit.dart @@ -0,0 +1,252 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/models/publication_site.dart'; +import 'package:island/pods/network.dart'; +import 'package:island/screens/creators/sites/site_list.dart'; +import 'package:island/widgets/alert.dart'; +import 'package:island/widgets/content/sheet.dart'; +import 'package:island/widgets/response.dart'; +import 'package:material_symbols_icons/symbols.dart'; +import 'package:styled_widget/styled_widget.dart'; + +class SiteForm extends HookConsumerWidget { + final String pubName; + final String? siteId; + + const SiteForm({super.key, required this.pubName, this.siteId}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final formKey = useMemoized(() => GlobalKey()); + final slugController = useTextEditingController(); + final nameController = useTextEditingController(); + final descriptionController = useTextEditingController(); + final isLoading = useState(false); + + final saveSite = useCallback(() async { + if (!formKey.currentState!.validate()) return; + + isLoading.value = true; + + try { + final client = ref.read(apiClientProvider); + final url = '/zone/sites/$pubName'; + final payload = { + 'slug': slugController.text, + 'name': nameController.text, + if (descriptionController.text.isNotEmpty) + 'description': descriptionController.text, + }; + + if (siteId != null) { + await client.patch('$url/$siteId', data: payload); + } else { + await client.post(url, data: payload); + } + + // Refresh the site list + ref.invalidate(siteListNotifierProvider(pubName)); + + if (context.mounted) { + showSnackBar('Publication site saved successfully'); + Navigator.pop(context); + } + } catch (e) { + showErrorAlert(e); + } finally { + isLoading.value = false; + } + }, [pubName, siteId, context]); + + final deleteSite = useCallback(() async { + final confirmed = await showConfirmAlert( + 'Are you sure you want to delete this publication site? This action cannot be undone.', + 'Delete Publication Site', + ); + if (confirmed != true) return; + + isLoading.value = true; + + try { + final client = ref.read(apiClientProvider); + await client.delete('/zone/sites/${siteId!}'); + + ref.invalidate(siteListNotifierProvider(pubName)); + + if (context.mounted) { + showSnackBar('Publication site deleted successfully'); + Navigator.pop(context); + } + } catch (e) { + showErrorAlert(e); + } finally { + isLoading.value = false; + } + }, [pubName, siteId, context]); + + // Handle loading and error states for editing + final isFetchLoading = useState(siteId != null); + final site = useState(null); + final errorMessage = useState(null); + + useEffect(() { + if (siteId == null) return; + + Future fetchSite() async { + try { + final client = ref.read(apiClientProvider); + final response = await client.get('/zone/sites/$siteId'); + final fetchedSite = SnPublicationSite.fromJson(response.data); + site.value = fetchedSite; + + // Initialize form fields if they're empty and we have a site + if (nameController.text.isEmpty) { + slugController.text = fetchedSite.slug; + nameController.text = fetchedSite.name; + descriptionController.text = fetchedSite.description ?? ''; + } + } catch (e) { + errorMessage.value = e.toString(); + } finally { + isFetchLoading.value = false; + } + } + + fetchSite(); + return null; + }, [siteId]); + + if (siteId != null && isFetchLoading.value) { + return const SheetScaffold( + titleText: 'Edit Publication Site', + child: Center(child: CircularProgressIndicator()), + ); + } + + if (siteId != null && errorMessage.value != null) { + return SheetScaffold( + titleText: 'Edit Publication Site', + child: ResponseErrorWidget( + error: errorMessage.value!, + onRetry: () { + isFetchLoading.value = true; + errorMessage.value = null; + // Refetch + useEffect(() { + Future fetchSite() async { + try { + final client = ref.read(apiClientProvider); + final response = await client.get('/zone/sites/$siteId'); + final fetchedSite = SnPublicationSite.fromJson(response.data); + site.value = fetchedSite; + slugController.text = fetchedSite.slug; + nameController.text = fetchedSite.name; + descriptionController.text = fetchedSite.description ?? ''; + } catch (e) { + errorMessage.value = e.toString(); + } finally { + isFetchLoading.value = false; + } + } + + fetchSite(); + return null; + }, ['$siteId-${DateTime.now().millisecondsSinceEpoch}']); + }, + ), + ); + } + + final formFields = Column( + children: [ + TextFormField( + controller: slugController, + decoration: const InputDecoration( + labelText: 'Slug', + hintText: 'my-site', + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter a slug'; + } + final slugRegex = RegExp(r'^[a-z0-9]+(?:-[a-z0-9]+)*$'); + if (!slugRegex.hasMatch(value)) { + return 'Slug can only contain lowercase letters, numbers, and dashes'; + } + return null; + }, + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), + ), + const SizedBox(height: 16), + TextFormField( + controller: nameController, + decoration: const InputDecoration( + labelText: 'Site Name', + hintText: 'My Publication Site', + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter a site name'; + } + return null; + }, + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), + ), + const SizedBox(height: 16), + TextFormField( + controller: descriptionController, + decoration: const InputDecoration( + labelText: 'Description', + alignLabelWithHint: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + ), + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), + maxLines: 3, + ), + ], + ).padding(all: 20); + + final saveButton = TextButton.icon( + onPressed: isLoading.value ? null : saveSite, + icon: const Icon(Symbols.save), + label: Text('saveChanges').tr(), + ).padding(horizontal: 20, vertical: 12); + + return SheetScaffold( + titleText: + siteId == null ? 'New Publication Site' : 'Edit Publication Site', + child: SingleChildScrollView( + child: Column( + children: [ + Form(key: formKey, child: formFields), + Row( + children: [ + if (siteId != null) ...[ + TextButton.icon( + onPressed: isLoading.value ? null : deleteSite, + icon: const Icon(Symbols.delete_forever), + label: const Text('Delete Publication Site'), + style: TextButton.styleFrom(foregroundColor: Colors.red), + ).alignment(Alignment.centerRight), + const SizedBox(height: 16), + ], + const Spacer(), + saveButton, + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/creators/sites/site_list.dart b/lib/screens/creators/sites/site_list.dart new file mode 100644 index 00000000..37609055 --- /dev/null +++ b/lib/screens/creators/sites/site_list.dart @@ -0,0 +1,230 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/models/publication_site.dart'; +import 'package:island/pods/network.dart'; +import 'package:island/screens/creators/sites/site_edit.dart'; +import 'package:island/widgets/app_scaffold.dart'; +import 'package:material_symbols_icons/symbols.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; +import 'package:island/widgets/extended_refresh_indicator.dart'; +import 'package:styled_widget/styled_widget.dart'; + +part 'site_list.g.dart'; + +@riverpod +class SiteListNotifier extends _$SiteListNotifier + with CursorPagingNotifierMixin { + static const int _pageSize = 20; + + @override + Future> build(String? pubName) { + // immediately load first page + return fetch(cursor: null); + } + + @override + Future> fetch({ + required String? cursor, + }) async { + final client = ref.read(apiClientProvider); + final offset = cursor == null ? 0 : int.parse(cursor); + + // read the current family argument passed to provider + final queryParams = {'offset': offset, 'take': _pageSize}; + + final response = await client.get( + '/zone/sites/$pubName', + queryParameters: queryParams, + ); + final total = int.parse(response.headers.value('X-Total') ?? '0'); + final List data = response.data; + final items = data.map((json) => SnPublicationSite.fromJson(json)).toList(); + + final hasMore = offset + items.length < total; + final nextCursor = hasMore ? (offset + items.length).toString() : null; + + return CursorPagingData( + items: items, + hasMore: hasMore, + nextCursor: nextCursor, + ); + } +} + +class CreatorSiteListScreen extends HookConsumerWidget { + const CreatorSiteListScreen({super.key, required this.pubName}); + + final String pubName; + + Future _createSite(BuildContext context) async { + await showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => SiteForm(pubName: pubName), + ); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return AppScaffold( + isNoBackground: false, + appBar: AppBar(title: Text('Publication Sites')), + floatingActionButton: FloatingActionButton( + onPressed: () => _createSite(context), + child: Icon(Icons.add), + ), + body: ExtendedRefreshIndicator( + onRefresh: () => ref.refresh(siteListNotifierProvider(pubName).future), + child: CustomScrollView( + slivers: [ + PagingHelperSliverView( + provider: siteListNotifierProvider(pubName), + futureRefreshable: siteListNotifierProvider(pubName).future, + notifierRefreshable: siteListNotifierProvider(pubName).notifier, + contentBuilder: + (data, widgetCount, endItemView) => SliverList.builder( + itemCount: widgetCount, + itemBuilder: (context, index) { + if (index == widgetCount - 1) { + return endItemView; + } + final site = data.items[index]; + return ConstrainedBox( + constraints: BoxConstraints(maxWidth: 640), + child: _CreatorSiteItem(site: site, pubName: pubName), + ).center(); + }, + ), + ), + ], + ), + ), + ); + } +} + +class _CreatorSiteItem extends HookConsumerWidget { + final String pubName; + const _CreatorSiteItem({required this.site, required this.pubName}); + + final SnPublicationSite site; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = Theme.of(context); + + return Card( + margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + clipBehavior: Clip.antiAlias, + child: ListTile( + title: Text(site.name), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (site.description != null && site.description!.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 4), + child: Text( + site.description!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 4), + child: Text( + 'Slug: ${site.slug} · Pages: ${site.pages.length}', + style: theme.textTheme.bodySmall, + ), + ), + ], + ), + trailing: PopupMenuButton( + itemBuilder: + (context) => [ + PopupMenuItem( + child: Row( + children: [ + const Icon(Symbols.edit), + const Gap(16), + Text('edit').tr(), + ], + ), + onTap: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: + (context) => + SiteForm(pubName: pubName, siteId: site.id), + ); + }, + ), + PopupMenuItem( + child: Row( + children: [ + const Icon(Symbols.delete, color: Colors.red), + const Gap(16), + Text('delete').tr().textColor(Colors.red), + ], + ), + onTap: () async { + final confirmed = await showDialog( + context: context, + builder: + (context) => AlertDialog( + title: Text('Delete Site'), + content: Text( + 'Are you sure you want to delete this site?', + ), + actions: [ + TextButton( + onPressed: + () => Navigator.of(context).pop(false), + child: Text('Cancel'), + ), + TextButton( + onPressed: + () => Navigator.of(context).pop(true), + child: Text('Delete'), + ), + ], + ), + ); + if (confirmed == true) { + try { + final client = ref.read(apiClientProvider); + await client.delete('/zone/sites/${site.id}'); + ref.invalidate(siteListNotifierProvider(pubName)); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Site deleted successfully'), + ), + ); + } + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Failed to delete site')), + ); + } + } + } + }, + ), + ], + ), + onTap: () { + // Open site details or pages + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text('Site details coming soon'))); + }, + ), + ); + } +} diff --git a/lib/screens/creators/sites/site_list.g.dart b/lib/screens/creators/sites/site_list.g.dart new file mode 100644 index 00000000..7aa33630 --- /dev/null +++ b/lib/screens/creators/sites/site_list.g.dart @@ -0,0 +1,183 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'site_list.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$siteListNotifierHash() => r'5cd2d75f13b6e7d4910dc66a24bbd6508fc4175d'; + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +abstract class _$SiteListNotifier + extends + BuildlessAutoDisposeAsyncNotifier> { + late final String? pubName; + + FutureOr> build(String? pubName); +} + +/// See also [SiteListNotifier]. +@ProviderFor(SiteListNotifier) +const siteListNotifierProvider = SiteListNotifierFamily(); + +/// See also [SiteListNotifier]. +class SiteListNotifierFamily + extends Family>> { + /// See also [SiteListNotifier]. + const SiteListNotifierFamily(); + + /// See also [SiteListNotifier]. + SiteListNotifierProvider call(String? pubName) { + return SiteListNotifierProvider(pubName); + } + + @override + SiteListNotifierProvider getProviderOverride( + covariant SiteListNotifierProvider provider, + ) { + return call(provider.pubName); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'siteListNotifierProvider'; +} + +/// See also [SiteListNotifier]. +class SiteListNotifierProvider + extends + AutoDisposeAsyncNotifierProviderImpl< + SiteListNotifier, + CursorPagingData + > { + /// See also [SiteListNotifier]. + SiteListNotifierProvider(String? pubName) + : this._internal( + () => SiteListNotifier()..pubName = pubName, + from: siteListNotifierProvider, + name: r'siteListNotifierProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$siteListNotifierHash, + dependencies: SiteListNotifierFamily._dependencies, + allTransitiveDependencies: + SiteListNotifierFamily._allTransitiveDependencies, + pubName: pubName, + ); + + SiteListNotifierProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.pubName, + }) : super.internal(); + + final String? pubName; + + @override + FutureOr> runNotifierBuild( + covariant SiteListNotifier notifier, + ) { + return notifier.build(pubName); + } + + @override + Override overrideWith(SiteListNotifier Function() create) { + return ProviderOverride( + origin: this, + override: SiteListNotifierProvider._internal( + () => create()..pubName = pubName, + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + pubName: pubName, + ), + ); + } + + @override + AutoDisposeAsyncNotifierProviderElement< + SiteListNotifier, + CursorPagingData + > + createElement() { + return _SiteListNotifierProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is SiteListNotifierProvider && other.pubName == pubName; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, pubName.hashCode); + + return _SystemHash.finish(hash); + } +} + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +mixin SiteListNotifierRef + on + AutoDisposeAsyncNotifierProviderRef< + CursorPagingData + > { + /// The parameter `pubName` of this provider. + String? get pubName; +} + +class _SiteListNotifierProviderElement + extends + AutoDisposeAsyncNotifierProviderElement< + SiteListNotifier, + CursorPagingData + > + with SiteListNotifierRef { + _SiteListNotifierProviderElement(super.provider); + + @override + String? get pubName => (origin as SiteListNotifierProvider).pubName; +} + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package