diff --git a/lib/pods/post/post_list.dart b/lib/pods/post/post_list.dart index 28049e0d..2d82b654 100644 --- a/lib/pods/post/post_list.dart +++ b/lib/pods/post/post_list.dart @@ -26,6 +26,14 @@ sealed class PostListQuery with _$PostListQuery { }) = _PostListQuery; } +@freezed +sealed class PostListQueryConfig with _$PostListQueryConfig { + const factory PostListQueryConfig({ + String? id, + @Default(PostListQuery()) PostListQuery initialFilter, + }) = _PostListQueryConfig; +} + final postListProvider = AsyncNotifierProvider.autoDispose.family( PostListNotifier.new, ); @@ -37,10 +45,17 @@ class PostListNotifier extends AsyncNotifier> static const int pageSize = 20; final String? id; - PostListNotifier(this.id); + final PostListQueryConfig config; + PostListNotifier(this.config) : id = config.id; @override - PostListQuery currentFilter = PostListQuery(); + late PostListQuery currentFilter; + + @override + Future> build() async { + currentFilter = config.initialFilter; + return fetch(); + } @override Future> fetch() async { @@ -72,7 +87,9 @@ class PostListNotifier extends AsyncNotifier> queryParameters: queryParams, ); totalCount = int.parse(response.headers.value('X-Total') ?? '0'); - final List data = response.data; - return data.map((json) => SnPost.fromJson(json)).toList(); + return response.data + .map((json) => SnPost.fromJson(json)) + .cast() + .toList(); } } diff --git a/lib/pods/post/post_list.freezed.dart b/lib/pods/post/post_list.freezed.dart index ab37dde2..d632c095 100644 --- a/lib/pods/post/post_list.freezed.dart +++ b/lib/pods/post/post_list.freezed.dart @@ -317,4 +317,276 @@ as bool, } +/// @nodoc +mixin _$PostListQueryConfig { + + String? get id; PostListQuery get initialFilter; +/// Create a copy of PostListQueryConfig +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$PostListQueryConfigCopyWith get copyWith => _$PostListQueryConfigCopyWithImpl(this as PostListQueryConfig, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is PostListQueryConfig&&(identical(other.id, id) || other.id == id)&&(identical(other.initialFilter, initialFilter) || other.initialFilter == initialFilter)); +} + + +@override +int get hashCode => Object.hash(runtimeType,id,initialFilter); + +@override +String toString() { + return 'PostListQueryConfig(id: $id, initialFilter: $initialFilter)'; +} + + +} + +/// @nodoc +abstract mixin class $PostListQueryConfigCopyWith<$Res> { + factory $PostListQueryConfigCopyWith(PostListQueryConfig value, $Res Function(PostListQueryConfig) _then) = _$PostListQueryConfigCopyWithImpl; +@useResult +$Res call({ + String? id, PostListQuery initialFilter +}); + + +$PostListQueryCopyWith<$Res> get initialFilter; + +} +/// @nodoc +class _$PostListQueryConfigCopyWithImpl<$Res> + implements $PostListQueryConfigCopyWith<$Res> { + _$PostListQueryConfigCopyWithImpl(this._self, this._then); + + final PostListQueryConfig _self; + final $Res Function(PostListQueryConfig) _then; + +/// Create a copy of PostListQueryConfig +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = freezed,Object? initialFilter = null,}) { + return _then(_self.copyWith( +id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String?,initialFilter: null == initialFilter ? _self.initialFilter : initialFilter // ignore: cast_nullable_to_non_nullable +as PostListQuery, + )); +} +/// Create a copy of PostListQueryConfig +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$PostListQueryCopyWith<$Res> get initialFilter { + + return $PostListQueryCopyWith<$Res>(_self.initialFilter, (value) { + return _then(_self.copyWith(initialFilter: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [PostListQueryConfig]. +extension PostListQueryConfigPatterns on PostListQueryConfig { +/// 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( _PostListQueryConfig value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _PostListQueryConfig() 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( _PostListQueryConfig value) $default,){ +final _that = this; +switch (_that) { +case _PostListQueryConfig(): +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( _PostListQueryConfig value)? $default,){ +final _that = this; +switch (_that) { +case _PostListQueryConfig() 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, PostListQuery initialFilter)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _PostListQueryConfig() when $default != null: +return $default(_that.id,_that.initialFilter);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, PostListQuery initialFilter) $default,) {final _that = this; +switch (_that) { +case _PostListQueryConfig(): +return $default(_that.id,_that.initialFilter);} +} +/// 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, PostListQuery initialFilter)? $default,) {final _that = this; +switch (_that) { +case _PostListQueryConfig() when $default != null: +return $default(_that.id,_that.initialFilter);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _PostListQueryConfig implements PostListQueryConfig { + const _PostListQueryConfig({this.id, this.initialFilter = const PostListQuery()}); + + +@override final String? id; +@override@JsonKey() final PostListQuery initialFilter; + +/// Create a copy of PostListQueryConfig +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$PostListQueryConfigCopyWith<_PostListQueryConfig> get copyWith => __$PostListQueryConfigCopyWithImpl<_PostListQueryConfig>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _PostListQueryConfig&&(identical(other.id, id) || other.id == id)&&(identical(other.initialFilter, initialFilter) || other.initialFilter == initialFilter)); +} + + +@override +int get hashCode => Object.hash(runtimeType,id,initialFilter); + +@override +String toString() { + return 'PostListQueryConfig(id: $id, initialFilter: $initialFilter)'; +} + + +} + +/// @nodoc +abstract mixin class _$PostListQueryConfigCopyWith<$Res> implements $PostListQueryConfigCopyWith<$Res> { + factory _$PostListQueryConfigCopyWith(_PostListQueryConfig value, $Res Function(_PostListQueryConfig) _then) = __$PostListQueryConfigCopyWithImpl; +@override @useResult +$Res call({ + String? id, PostListQuery initialFilter +}); + + +@override $PostListQueryCopyWith<$Res> get initialFilter; + +} +/// @nodoc +class __$PostListQueryConfigCopyWithImpl<$Res> + implements _$PostListQueryConfigCopyWith<$Res> { + __$PostListQueryConfigCopyWithImpl(this._self, this._then); + + final _PostListQueryConfig _self; + final $Res Function(_PostListQueryConfig) _then; + +/// Create a copy of PostListQueryConfig +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = freezed,Object? initialFilter = null,}) { + return _then(_PostListQueryConfig( +id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String?,initialFilter: null == initialFilter ? _self.initialFilter : initialFilter // ignore: cast_nullable_to_non_nullable +as PostListQuery, + )); +} + +/// Create a copy of PostListQueryConfig +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$PostListQueryCopyWith<$Res> get initialFilter { + + return $PostListQueryCopyWith<$Res>(_self.initialFilter, (value) { + return _then(_self.copyWith(initialFilter: value)); + }); +} +} + // dart format on diff --git a/lib/screens/posts/post_search.dart b/lib/screens/posts/post_search.dart index d12f9601..6ab96e20 100644 --- a/lib/screens/posts/post_search.dart +++ b/lib/screens/posts/post_search.dart @@ -34,7 +34,9 @@ class PostSearchScreen extends HookConsumerWidget { // Single query state final queryState = useState(const PostListQuery()); - final noti = ref.read(postListProvider(kSearchPostListId).notifier); + final noti = ref.read( + postListProvider(PostListQueryConfig(id: kSearchPostListId)).notifier, + ); useEffect(() { return () { @@ -116,7 +118,9 @@ class PostSearchScreen extends HookConsumerWidget { ), body: Consumer( builder: (context, ref, child) { - final searchState = ref.watch(postListProvider(kSearchPostListId)); + final searchState = ref.watch( + postListProvider(PostListQueryConfig(id: kSearchPostListId)), + ); return isWideScreen(context) ? Row( @@ -153,9 +157,11 @@ class PostSearchScreen extends HookConsumerWidget { SliverToBoxAdapter(child: buildFilterPanel()), // Use PaginationList with isSliver=true PaginationList( - provider: postListProvider(kSearchPostListId), + provider: postListProvider( + PostListQueryConfig(id: kSearchPostListId), + ), notifier: postListProvider( - kSearchPostListId, + PostListQueryConfig(id: kSearchPostListId), ).notifier, isSliver: true, isRefreshable: false, @@ -251,8 +257,12 @@ class PostSearchScreen extends HookConsumerWidget { ), // Use PaginationList with isSliver=true PaginationList( - provider: postListProvider(kSearchPostListId), - notifier: postListProvider(kSearchPostListId).notifier, + provider: postListProvider( + PostListQueryConfig(id: kSearchPostListId), + ), + notifier: postListProvider( + PostListQueryConfig(id: kSearchPostListId), + ).notifier, isSliver: true, isRefreshable: false, itemBuilder: (context, index, post) { diff --git a/lib/widgets/post/post_list.dart b/lib/widgets/post/post_list.dart index c65e13bd..3080b31e 100644 --- a/lib/widgets/post/post_list.dart +++ b/lib/widgets/post/post_list.dart @@ -43,17 +43,26 @@ class SliverPostList extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final provider = postListProvider(queryKey); - final notifier = provider.notifier; + final provider = postListProvider( + PostListQueryConfig( + id: queryKey, + initialFilter: query ?? PostListQuery(), + ), + ); + final notifier = ref.watch(provider.notifier); + + final currentFilter = useState(query ?? PostListQuery()); useEffect(() { - ref.read(notifier).applyFilter(query!); + if (currentFilter.value != query) { + notifier.applyFilter(query ?? PostListQuery()); + } return null; - }, [query]); + }, [query, queryKey]); return PaginationList( provider: provider, - notifier: notifier, + notifier: provider.notifier, isRefreshable: false, isSliver: true, footerSkeletonChild: const PostItemSkeleton(), diff --git a/lib/widgets/post/post_shuffle.dart b/lib/widgets/post/post_shuffle.dart index 0be8d51c..688b1708 100644 --- a/lib/widgets/post/post_shuffle.dart +++ b/lib/widgets/post/post_shuffle.dart @@ -17,15 +17,16 @@ class PostShuffleScreen extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { const query = PostListQuery(shuffle: true); - final postListState = ref.watch(postListProvider(kShufflePostListId)); - final postListNotifier = ref.watch( - postListProvider(kShufflePostListId).notifier, + final cfg = PostListQueryConfig( + id: kShufflePostListId, + initialFilter: query, ); + final postListState = ref.watch(postListProvider(cfg)); + final postListNotifier = ref.watch(postListProvider(cfg).notifier); final cardSwiperController = useMemoized(() => CardSwiperController(), []); useEffect(() { - postListNotifier.applyFilter(query); return cardSwiperController.dispose; }, []);