✨ Explore subscription filter card
This commit is contained in:
@@ -10,6 +10,7 @@ part 'post_list.freezed.dart';
|
||||
sealed class PostListQuery with _$PostListQuery {
|
||||
const factory PostListQuery({
|
||||
String? pubName,
|
||||
List<String>? publishers,
|
||||
String? realm,
|
||||
int? type,
|
||||
List<String>? categories,
|
||||
@@ -61,35 +62,98 @@ class PostListNotifier extends AsyncNotifier<List<SnPost>>
|
||||
Future<List<SnPost>> fetch() async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
|
||||
final queryParams = {
|
||||
'offset': fetchedCount,
|
||||
'take': pageSize,
|
||||
'replies': currentFilter.includeReplies,
|
||||
'orderDesc': currentFilter.orderDesc,
|
||||
if (currentFilter.shuffle) 'shuffle': currentFilter.shuffle,
|
||||
if (currentFilter.pubName != null) 'pub': currentFilter.pubName,
|
||||
if (currentFilter.realm != null) 'realm': currentFilter.realm,
|
||||
if (currentFilter.type != null) 'type': currentFilter.type,
|
||||
if (currentFilter.tags != null) 'tags': currentFilter.tags,
|
||||
if (currentFilter.categories != null)
|
||||
'categories': currentFilter.categories,
|
||||
if (currentFilter.pinned != null) 'pinned': currentFilter.pinned,
|
||||
if (currentFilter.order != null) 'order': currentFilter.order,
|
||||
if (currentFilter.periodStart != null)
|
||||
'periodStart': currentFilter.periodStart,
|
||||
if (currentFilter.periodEnd != null) 'periodEnd': currentFilter.periodEnd,
|
||||
if (currentFilter.queryTerm != null) 'query': currentFilter.queryTerm,
|
||||
if (currentFilter.mediaOnly != null) 'media': currentFilter.mediaOnly,
|
||||
};
|
||||
// Handle multiple publishers by making separate requests and combining results
|
||||
if (currentFilter.publishers != null &&
|
||||
currentFilter.publishers!.isNotEmpty) {
|
||||
final allPosts = <SnPost>[];
|
||||
var totalPostsCount = 0;
|
||||
|
||||
final response = await client.get(
|
||||
'/sphere/posts',
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
totalCount = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
return response.data
|
||||
.map((json) => SnPost.fromJson(json))
|
||||
.cast<SnPost>()
|
||||
.toList();
|
||||
for (final publisherName in currentFilter.publishers!) {
|
||||
final queryParams = {
|
||||
'offset': fetchedCount,
|
||||
'take': pageSize,
|
||||
'replies': currentFilter.includeReplies,
|
||||
'orderDesc': currentFilter.orderDesc,
|
||||
if (currentFilter.shuffle) 'shuffle': currentFilter.shuffle,
|
||||
'pub': publisherName,
|
||||
if (currentFilter.realm != null) 'realm': currentFilter.realm,
|
||||
if (currentFilter.type != null) 'type': currentFilter.type,
|
||||
if (currentFilter.tags != null) 'tags': currentFilter.tags,
|
||||
if (currentFilter.categories != null)
|
||||
'categories': currentFilter.categories,
|
||||
if (currentFilter.pinned != null) 'pinned': currentFilter.pinned,
|
||||
if (currentFilter.order != null) 'order': currentFilter.order,
|
||||
if (currentFilter.periodStart != null)
|
||||
'periodStart': currentFilter.periodStart,
|
||||
if (currentFilter.periodEnd != null)
|
||||
'periodEnd': currentFilter.periodEnd,
|
||||
if (currentFilter.queryTerm != null) 'query': currentFilter.queryTerm,
|
||||
if (currentFilter.mediaOnly != null) 'media': currentFilter.mediaOnly,
|
||||
};
|
||||
|
||||
final response = await client.get(
|
||||
'/sphere/posts',
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
|
||||
final posts = response.data
|
||||
.map((json) => SnPost.fromJson(json))
|
||||
.cast<SnPost>()
|
||||
.toList();
|
||||
|
||||
allPosts.addAll(posts);
|
||||
totalPostsCount += int.parse(response.headers.value('X-Total') ?? '0');
|
||||
}
|
||||
|
||||
// Sort combined results by creation date (newest first)
|
||||
allPosts.sort(
|
||||
(a, b) => (b.createdAt ?? DateTime.now()).compareTo(
|
||||
a.createdAt ?? DateTime.now(),
|
||||
),
|
||||
);
|
||||
|
||||
// Apply pagination to combined results
|
||||
final startIndex = fetchedCount;
|
||||
final endIndex = (fetchedCount + pageSize).clamp(0, allPosts.length);
|
||||
final paginatedPosts = startIndex < allPosts.length
|
||||
? allPosts.sublist(startIndex, endIndex)
|
||||
: <SnPost>[];
|
||||
|
||||
totalCount = totalPostsCount;
|
||||
return paginatedPosts;
|
||||
} else {
|
||||
// Single publisher or no publisher filter
|
||||
final queryParams = {
|
||||
'offset': fetchedCount,
|
||||
'take': pageSize,
|
||||
'replies': currentFilter.includeReplies,
|
||||
'orderDesc': currentFilter.orderDesc,
|
||||
if (currentFilter.shuffle) 'shuffle': currentFilter.shuffle,
|
||||
if (currentFilter.pubName != null) 'pub': currentFilter.pubName,
|
||||
if (currentFilter.realm != null) 'realm': currentFilter.realm,
|
||||
if (currentFilter.type != null) 'type': currentFilter.type,
|
||||
if (currentFilter.tags != null) 'tags': currentFilter.tags,
|
||||
if (currentFilter.categories != null)
|
||||
'categories': currentFilter.categories,
|
||||
if (currentFilter.pinned != null) 'pinned': currentFilter.pinned,
|
||||
if (currentFilter.order != null) 'order': currentFilter.order,
|
||||
if (currentFilter.periodStart != null)
|
||||
'periodStart': currentFilter.periodStart,
|
||||
if (currentFilter.periodEnd != null)
|
||||
'periodEnd': currentFilter.periodEnd,
|
||||
if (currentFilter.queryTerm != null) 'query': currentFilter.queryTerm,
|
||||
if (currentFilter.mediaOnly != null) 'media': currentFilter.mediaOnly,
|
||||
};
|
||||
|
||||
final response = await client.get(
|
||||
'/sphere/posts',
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
totalCount = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
return response.data
|
||||
.map((json) => SnPost.fromJson(json))
|
||||
.cast<SnPost>()
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$PostListQuery {
|
||||
|
||||
String? get pubName; String? get realm; int? get type; List<String>? get categories; List<String>? get tags; bool? get pinned; bool get shuffle; bool? get includeReplies; bool? get mediaOnly; String? get queryTerm; String? get order; int? get periodStart; int? get periodEnd; bool get orderDesc;
|
||||
String? get pubName; List<String>? get publishers; String? get realm; int? get type; List<String>? get categories; List<String>? get tags; bool? get pinned; bool get shuffle; bool? get includeReplies; bool? get mediaOnly; String? get queryTerm; String? get order; int? get periodStart; int? get periodEnd; bool get orderDesc;
|
||||
/// Create a copy of PostListQuery
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -25,16 +25,16 @@ $PostListQueryCopyWith<PostListQuery> get copyWith => _$PostListQueryCopyWithImp
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is PostListQuery&&(identical(other.pubName, pubName) || other.pubName == pubName)&&(identical(other.realm, realm) || other.realm == realm)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.categories, categories)&&const DeepCollectionEquality().equals(other.tags, tags)&&(identical(other.pinned, pinned) || other.pinned == pinned)&&(identical(other.shuffle, shuffle) || other.shuffle == shuffle)&&(identical(other.includeReplies, includeReplies) || other.includeReplies == includeReplies)&&(identical(other.mediaOnly, mediaOnly) || other.mediaOnly == mediaOnly)&&(identical(other.queryTerm, queryTerm) || other.queryTerm == queryTerm)&&(identical(other.order, order) || other.order == order)&&(identical(other.periodStart, periodStart) || other.periodStart == periodStart)&&(identical(other.periodEnd, periodEnd) || other.periodEnd == periodEnd)&&(identical(other.orderDesc, orderDesc) || other.orderDesc == orderDesc));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is PostListQuery&&(identical(other.pubName, pubName) || other.pubName == pubName)&&const DeepCollectionEquality().equals(other.publishers, publishers)&&(identical(other.realm, realm) || other.realm == realm)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.categories, categories)&&const DeepCollectionEquality().equals(other.tags, tags)&&(identical(other.pinned, pinned) || other.pinned == pinned)&&(identical(other.shuffle, shuffle) || other.shuffle == shuffle)&&(identical(other.includeReplies, includeReplies) || other.includeReplies == includeReplies)&&(identical(other.mediaOnly, mediaOnly) || other.mediaOnly == mediaOnly)&&(identical(other.queryTerm, queryTerm) || other.queryTerm == queryTerm)&&(identical(other.order, order) || other.order == order)&&(identical(other.periodStart, periodStart) || other.periodStart == periodStart)&&(identical(other.periodEnd, periodEnd) || other.periodEnd == periodEnd)&&(identical(other.orderDesc, orderDesc) || other.orderDesc == orderDesc));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,pubName,realm,type,const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(tags),pinned,shuffle,includeReplies,mediaOnly,queryTerm,order,periodStart,periodEnd,orderDesc);
|
||||
int get hashCode => Object.hash(runtimeType,pubName,const DeepCollectionEquality().hash(publishers),realm,type,const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(tags),pinned,shuffle,includeReplies,mediaOnly,queryTerm,order,periodStart,periodEnd,orderDesc);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PostListQuery(pubName: $pubName, realm: $realm, type: $type, categories: $categories, tags: $tags, pinned: $pinned, shuffle: $shuffle, includeReplies: $includeReplies, mediaOnly: $mediaOnly, queryTerm: $queryTerm, order: $order, periodStart: $periodStart, periodEnd: $periodEnd, orderDesc: $orderDesc)';
|
||||
return 'PostListQuery(pubName: $pubName, publishers: $publishers, realm: $realm, type: $type, categories: $categories, tags: $tags, pinned: $pinned, shuffle: $shuffle, includeReplies: $includeReplies, mediaOnly: $mediaOnly, queryTerm: $queryTerm, order: $order, periodStart: $periodStart, periodEnd: $periodEnd, orderDesc: $orderDesc)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $PostListQueryCopyWith<$Res> {
|
||||
factory $PostListQueryCopyWith(PostListQuery value, $Res Function(PostListQuery) _then) = _$PostListQueryCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String? pubName, String? realm, int? type, List<String>? categories, List<String>? tags, bool? pinned, bool shuffle, bool? includeReplies, bool? mediaOnly, String? queryTerm, String? order, int? periodStart, int? periodEnd, bool orderDesc
|
||||
String? pubName, List<String>? publishers, String? realm, int? type, List<String>? categories, List<String>? tags, bool? pinned, bool shuffle, bool? includeReplies, bool? mediaOnly, String? queryTerm, String? order, int? periodStart, int? periodEnd, bool orderDesc
|
||||
});
|
||||
|
||||
|
||||
@@ -62,10 +62,11 @@ class _$PostListQueryCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of PostListQuery
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? pubName = freezed,Object? realm = freezed,Object? type = freezed,Object? categories = freezed,Object? tags = freezed,Object? pinned = freezed,Object? shuffle = null,Object? includeReplies = freezed,Object? mediaOnly = freezed,Object? queryTerm = freezed,Object? order = freezed,Object? periodStart = freezed,Object? periodEnd = freezed,Object? orderDesc = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? pubName = freezed,Object? publishers = freezed,Object? realm = freezed,Object? type = freezed,Object? categories = freezed,Object? tags = freezed,Object? pinned = freezed,Object? shuffle = null,Object? includeReplies = freezed,Object? mediaOnly = freezed,Object? queryTerm = freezed,Object? order = freezed,Object? periodStart = freezed,Object? periodEnd = freezed,Object? orderDesc = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
pubName: freezed == pubName ? _self.pubName : pubName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,realm: freezed == realm ? _self.realm : realm // ignore: cast_nullable_to_non_nullable
|
||||
as String?,publishers: freezed == publishers ? _self.publishers : publishers // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>?,realm: freezed == realm ? _self.realm : realm // ignore: cast_nullable_to_non_nullable
|
||||
as String?,type: freezed == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||
as int?,categories: freezed == categories ? _self.categories : categories // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>?,tags: freezed == tags ? _self.tags : tags // ignore: cast_nullable_to_non_nullable
|
||||
@@ -160,10 +161,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? pubName, String? realm, int? type, List<String>? categories, List<String>? tags, bool? pinned, bool shuffle, bool? includeReplies, bool? mediaOnly, String? queryTerm, String? order, int? periodStart, int? periodEnd, bool orderDesc)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? pubName, List<String>? publishers, String? realm, int? type, List<String>? categories, List<String>? tags, bool? pinned, bool shuffle, bool? includeReplies, bool? mediaOnly, String? queryTerm, String? order, int? periodStart, int? periodEnd, bool orderDesc)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _PostListQuery() when $default != null:
|
||||
return $default(_that.pubName,_that.realm,_that.type,_that.categories,_that.tags,_that.pinned,_that.shuffle,_that.includeReplies,_that.mediaOnly,_that.queryTerm,_that.order,_that.periodStart,_that.periodEnd,_that.orderDesc);case _:
|
||||
return $default(_that.pubName,_that.publishers,_that.realm,_that.type,_that.categories,_that.tags,_that.pinned,_that.shuffle,_that.includeReplies,_that.mediaOnly,_that.queryTerm,_that.order,_that.periodStart,_that.periodEnd,_that.orderDesc);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -181,10 +182,10 @@ return $default(_that.pubName,_that.realm,_that.type,_that.categories,_that.tags
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? pubName, String? realm, int? type, List<String>? categories, List<String>? tags, bool? pinned, bool shuffle, bool? includeReplies, bool? mediaOnly, String? queryTerm, String? order, int? periodStart, int? periodEnd, bool orderDesc) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? pubName, List<String>? publishers, String? realm, int? type, List<String>? categories, List<String>? tags, bool? pinned, bool shuffle, bool? includeReplies, bool? mediaOnly, String? queryTerm, String? order, int? periodStart, int? periodEnd, bool orderDesc) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _PostListQuery():
|
||||
return $default(_that.pubName,_that.realm,_that.type,_that.categories,_that.tags,_that.pinned,_that.shuffle,_that.includeReplies,_that.mediaOnly,_that.queryTerm,_that.order,_that.periodStart,_that.periodEnd,_that.orderDesc);}
|
||||
return $default(_that.pubName,_that.publishers,_that.realm,_that.type,_that.categories,_that.tags,_that.pinned,_that.shuffle,_that.includeReplies,_that.mediaOnly,_that.queryTerm,_that.order,_that.periodStart,_that.periodEnd,_that.orderDesc);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
@@ -198,10 +199,10 @@ return $default(_that.pubName,_that.realm,_that.type,_that.categories,_that.tags
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? pubName, String? realm, int? type, List<String>? categories, List<String>? tags, bool? pinned, bool shuffle, bool? includeReplies, bool? mediaOnly, String? queryTerm, String? order, int? periodStart, int? periodEnd, bool orderDesc)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? pubName, List<String>? publishers, String? realm, int? type, List<String>? categories, List<String>? tags, bool? pinned, bool shuffle, bool? includeReplies, bool? mediaOnly, String? queryTerm, String? order, int? periodStart, int? periodEnd, bool orderDesc)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _PostListQuery() when $default != null:
|
||||
return $default(_that.pubName,_that.realm,_that.type,_that.categories,_that.tags,_that.pinned,_that.shuffle,_that.includeReplies,_that.mediaOnly,_that.queryTerm,_that.order,_that.periodStart,_that.periodEnd,_that.orderDesc);case _:
|
||||
return $default(_that.pubName,_that.publishers,_that.realm,_that.type,_that.categories,_that.tags,_that.pinned,_that.shuffle,_that.includeReplies,_that.mediaOnly,_that.queryTerm,_that.order,_that.periodStart,_that.periodEnd,_that.orderDesc);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -213,10 +214,19 @@ return $default(_that.pubName,_that.realm,_that.type,_that.categories,_that.tags
|
||||
|
||||
|
||||
class _PostListQuery implements PostListQuery {
|
||||
const _PostListQuery({this.pubName, this.realm, this.type, final List<String>? categories, final List<String>? tags, this.pinned, this.shuffle = false, this.includeReplies, this.mediaOnly, this.queryTerm, this.order, this.periodStart, this.periodEnd, this.orderDesc = true}): _categories = categories,_tags = tags;
|
||||
const _PostListQuery({this.pubName, final List<String>? publishers, this.realm, this.type, final List<String>? categories, final List<String>? tags, this.pinned, this.shuffle = false, this.includeReplies, this.mediaOnly, this.queryTerm, this.order, this.periodStart, this.periodEnd, this.orderDesc = true}): _publishers = publishers,_categories = categories,_tags = tags;
|
||||
|
||||
|
||||
@override final String? pubName;
|
||||
final List<String>? _publishers;
|
||||
@override List<String>? get publishers {
|
||||
final value = _publishers;
|
||||
if (value == null) return null;
|
||||
if (_publishers is EqualUnmodifiableListView) return _publishers;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(value);
|
||||
}
|
||||
|
||||
@override final String? realm;
|
||||
@override final int? type;
|
||||
final List<String>? _categories;
|
||||
@@ -257,16 +267,16 @@ _$PostListQueryCopyWith<_PostListQuery> get copyWith => __$PostListQueryCopyWith
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PostListQuery&&(identical(other.pubName, pubName) || other.pubName == pubName)&&(identical(other.realm, realm) || other.realm == realm)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._categories, _categories)&&const DeepCollectionEquality().equals(other._tags, _tags)&&(identical(other.pinned, pinned) || other.pinned == pinned)&&(identical(other.shuffle, shuffle) || other.shuffle == shuffle)&&(identical(other.includeReplies, includeReplies) || other.includeReplies == includeReplies)&&(identical(other.mediaOnly, mediaOnly) || other.mediaOnly == mediaOnly)&&(identical(other.queryTerm, queryTerm) || other.queryTerm == queryTerm)&&(identical(other.order, order) || other.order == order)&&(identical(other.periodStart, periodStart) || other.periodStart == periodStart)&&(identical(other.periodEnd, periodEnd) || other.periodEnd == periodEnd)&&(identical(other.orderDesc, orderDesc) || other.orderDesc == orderDesc));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PostListQuery&&(identical(other.pubName, pubName) || other.pubName == pubName)&&const DeepCollectionEquality().equals(other._publishers, _publishers)&&(identical(other.realm, realm) || other.realm == realm)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._categories, _categories)&&const DeepCollectionEquality().equals(other._tags, _tags)&&(identical(other.pinned, pinned) || other.pinned == pinned)&&(identical(other.shuffle, shuffle) || other.shuffle == shuffle)&&(identical(other.includeReplies, includeReplies) || other.includeReplies == includeReplies)&&(identical(other.mediaOnly, mediaOnly) || other.mediaOnly == mediaOnly)&&(identical(other.queryTerm, queryTerm) || other.queryTerm == queryTerm)&&(identical(other.order, order) || other.order == order)&&(identical(other.periodStart, periodStart) || other.periodStart == periodStart)&&(identical(other.periodEnd, periodEnd) || other.periodEnd == periodEnd)&&(identical(other.orderDesc, orderDesc) || other.orderDesc == orderDesc));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,pubName,realm,type,const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_tags),pinned,shuffle,includeReplies,mediaOnly,queryTerm,order,periodStart,periodEnd,orderDesc);
|
||||
int get hashCode => Object.hash(runtimeType,pubName,const DeepCollectionEquality().hash(_publishers),realm,type,const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_tags),pinned,shuffle,includeReplies,mediaOnly,queryTerm,order,periodStart,periodEnd,orderDesc);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PostListQuery(pubName: $pubName, realm: $realm, type: $type, categories: $categories, tags: $tags, pinned: $pinned, shuffle: $shuffle, includeReplies: $includeReplies, mediaOnly: $mediaOnly, queryTerm: $queryTerm, order: $order, periodStart: $periodStart, periodEnd: $periodEnd, orderDesc: $orderDesc)';
|
||||
return 'PostListQuery(pubName: $pubName, publishers: $publishers, realm: $realm, type: $type, categories: $categories, tags: $tags, pinned: $pinned, shuffle: $shuffle, includeReplies: $includeReplies, mediaOnly: $mediaOnly, queryTerm: $queryTerm, order: $order, periodStart: $periodStart, periodEnd: $periodEnd, orderDesc: $orderDesc)';
|
||||
}
|
||||
|
||||
|
||||
@@ -277,7 +287,7 @@ abstract mixin class _$PostListQueryCopyWith<$Res> implements $PostListQueryCopy
|
||||
factory _$PostListQueryCopyWith(_PostListQuery value, $Res Function(_PostListQuery) _then) = __$PostListQueryCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String? pubName, String? realm, int? type, List<String>? categories, List<String>? tags, bool? pinned, bool shuffle, bool? includeReplies, bool? mediaOnly, String? queryTerm, String? order, int? periodStart, int? periodEnd, bool orderDesc
|
||||
String? pubName, List<String>? publishers, String? realm, int? type, List<String>? categories, List<String>? tags, bool? pinned, bool shuffle, bool? includeReplies, bool? mediaOnly, String? queryTerm, String? order, int? periodStart, int? periodEnd, bool orderDesc
|
||||
});
|
||||
|
||||
|
||||
@@ -294,10 +304,11 @@ class __$PostListQueryCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of PostListQuery
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? pubName = freezed,Object? realm = freezed,Object? type = freezed,Object? categories = freezed,Object? tags = freezed,Object? pinned = freezed,Object? shuffle = null,Object? includeReplies = freezed,Object? mediaOnly = freezed,Object? queryTerm = freezed,Object? order = freezed,Object? periodStart = freezed,Object? periodEnd = freezed,Object? orderDesc = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? pubName = freezed,Object? publishers = freezed,Object? realm = freezed,Object? type = freezed,Object? categories = freezed,Object? tags = freezed,Object? pinned = freezed,Object? shuffle = null,Object? includeReplies = freezed,Object? mediaOnly = freezed,Object? queryTerm = freezed,Object? order = freezed,Object? periodStart = freezed,Object? periodEnd = freezed,Object? orderDesc = null,}) {
|
||||
return _then(_PostListQuery(
|
||||
pubName: freezed == pubName ? _self.pubName : pubName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,realm: freezed == realm ? _self.realm : realm // ignore: cast_nullable_to_non_nullable
|
||||
as String?,publishers: freezed == publishers ? _self._publishers : publishers // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>?,realm: freezed == realm ? _self.realm : realm // ignore: cast_nullable_to_non_nullable
|
||||
as String?,type: freezed == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||
as int?,categories: freezed == categories ? _self._categories : categories // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>?,tags: freezed == tags ? _self._tags : tags // ignore: cast_nullable_to_non_nullable
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/post.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
|
||||
final subscriptionsProvider = FutureProvider<List<SnPublisherSubscription>>((
|
||||
ref,
|
||||
) async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
|
||||
final response = await client.get('/sphere/subscriptions');
|
||||
|
||||
return response.data
|
||||
.map((json) => SnPublisherSubscription.fromJson(json))
|
||||
.cast<SnPublisherSubscription>()
|
||||
.toList();
|
||||
});
|
||||
@@ -23,6 +23,7 @@ import 'package:island/widgets/navigation/fab_menu.dart';
|
||||
import 'package:island/widgets/paging/pagination_list.dart';
|
||||
import 'package:island/widgets/post/post_item.dart';
|
||||
import 'package:island/widgets/post/post_item_skeleton.dart';
|
||||
import 'package:island/widgets/post/post_list.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:island/widgets/realm/realm_card.dart';
|
||||
import 'package:island/widgets/publisher/publisher_card.dart';
|
||||
@@ -31,6 +32,8 @@ import 'package:island/services/event_bus.dart';
|
||||
import 'package:island/widgets/share/share_sheet.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||
import 'package:island/widgets/posts/post_subscription_filter.dart';
|
||||
import 'package:island/pods/post/post_list.dart';
|
||||
|
||||
class ExploreScreen extends HookConsumerWidget {
|
||||
const ExploreScreen({super.key});
|
||||
@@ -38,6 +41,7 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final currentFilter = useState<String?>(null);
|
||||
final selectedPublisherNames = useState<List<String>>([]);
|
||||
final notifier = ref.watch(activityListProvider.notifier);
|
||||
|
||||
useEffect(() {
|
||||
@@ -87,6 +91,8 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
|
||||
final isWide = isWideScreen(context);
|
||||
|
||||
final hasSubscriptionsSelected = selectedPublisherNames.value.isNotEmpty;
|
||||
|
||||
final filterBar = Card(
|
||||
margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
||||
child: Row(
|
||||
@@ -95,7 +101,9 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
spacing: 8,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () => handleFilterChange(null),
|
||||
onPressed: hasSubscriptionsSelected
|
||||
? null
|
||||
: () => handleFilterChange(null),
|
||||
icon: Icon(
|
||||
Symbols.explore,
|
||||
fill: currentFilter.value == null ? 1 : null,
|
||||
@@ -107,7 +115,9 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
: null,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => handleFilterChange('subscriptions'),
|
||||
onPressed: hasSubscriptionsSelected
|
||||
? null
|
||||
: () => handleFilterChange('subscriptions'),
|
||||
icon: Icon(
|
||||
Symbols.subscriptions,
|
||||
fill: currentFilter.value == 'subscriptions' ? 1 : null,
|
||||
@@ -119,7 +129,9 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
: null,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => handleFilterChange('friends'),
|
||||
onPressed: hasSubscriptionsSelected
|
||||
? null
|
||||
: () => handleFilterChange('friends'),
|
||||
icon: Icon(
|
||||
Symbols.people,
|
||||
fill: currentFilter.value == 'friends' ? 1 : null,
|
||||
@@ -188,7 +200,12 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
|
||||
final appBar = isWide
|
||||
? null
|
||||
: _buildAppBar(currentFilter.value, handleFilterChange, context);
|
||||
: _buildAppBar(
|
||||
currentFilter.value,
|
||||
handleFilterChange,
|
||||
context,
|
||||
hasSubscriptionsSelected,
|
||||
);
|
||||
|
||||
final dragging = useState(false);
|
||||
|
||||
@@ -221,6 +238,8 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
query,
|
||||
events,
|
||||
selectedDay,
|
||||
currentFilter.value,
|
||||
selectedPublisherNames,
|
||||
)
|
||||
: _buildNarrowBody(context, ref, currentFilter.value),
|
||||
),
|
||||
@@ -273,6 +292,19 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPostList(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
List<String> selectedPublisherIds,
|
||||
) {
|
||||
return SliverPostList(
|
||||
queryKey: 'explore_filtered',
|
||||
query: PostListQuery(publishers: selectedPublisherIds),
|
||||
padding: EdgeInsets.zero,
|
||||
itemPadding: EdgeInsets.zero,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWideBody(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
@@ -282,10 +314,18 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
ValueNotifier<EventCalendarQuery> query,
|
||||
AsyncValue<List<dynamic>> events,
|
||||
ValueNotifier<DateTime> selectedDay,
|
||||
String? currentFilter,
|
||||
ValueNotifier<List<String>> selectedPublisherNames,
|
||||
) {
|
||||
final bodyView = _buildActivityList(context, ref);
|
||||
// Use post list when subscription filter is active and publishers are selected
|
||||
final usePostList = selectedPublisherNames.value.isNotEmpty;
|
||||
final bodyView = usePostList
|
||||
? _buildPostList(context, ref, selectedPublisherNames.value)
|
||||
: _buildActivityList(context, ref);
|
||||
|
||||
final notifier = ref.watch(activityListProvider.notifier);
|
||||
final notifier = usePostList
|
||||
? null // Post list handles its own refreshing
|
||||
: ref.watch(activityListProvider.notifier);
|
||||
|
||||
return Row(
|
||||
spacing: 12,
|
||||
@@ -293,7 +333,9 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
Flexible(
|
||||
flex: 3,
|
||||
child: ExtendedRefreshIndicator(
|
||||
onRefresh: notifier.refresh,
|
||||
onRefresh: () async {
|
||||
await notifier?.refresh();
|
||||
},
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
const SliverGap(12),
|
||||
@@ -310,7 +352,19 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(spacing: 8, children: [const Gap(4)]),
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Gap(4 + MediaQuery.paddingOf(context).top),
|
||||
PostSubscriptionFilterWidget(
|
||||
initialSelectedPublisherNames:
|
||||
selectedPublisherNames.value,
|
||||
onSelectedPublishersChanged: (names) {
|
||||
selectedPublisherNames.value = names;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -358,6 +412,7 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
String? currentFilter,
|
||||
void Function(String?) handleFilterChange,
|
||||
BuildContext context,
|
||||
bool hasSubscriptionsSelected,
|
||||
) {
|
||||
final foregroundColor = Theme.of(context).appBarTheme.foregroundColor;
|
||||
|
||||
@@ -376,7 +431,9 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
spacing: 8,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () => handleFilterChange(null),
|
||||
onPressed: hasSubscriptionsSelected
|
||||
? null
|
||||
: () => handleFilterChange(null),
|
||||
icon: Icon(
|
||||
Symbols.explore,
|
||||
color: foregroundColor,
|
||||
@@ -387,7 +444,9 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
color: currentFilter == null ? foregroundColor : null,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => handleFilterChange('subscriptions'),
|
||||
onPressed: hasSubscriptionsSelected
|
||||
? null
|
||||
: () => handleFilterChange('subscriptions'),
|
||||
icon: Icon(
|
||||
Symbols.subscriptions,
|
||||
color: foregroundColor,
|
||||
@@ -397,7 +456,9 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
isSelected: currentFilter == 'subscriptions',
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => handleFilterChange('friends'),
|
||||
onPressed: hasSubscriptionsSelected
|
||||
? null
|
||||
: () => handleFilterChange('friends'),
|
||||
icon: Icon(
|
||||
Symbols.people,
|
||||
color: foregroundColor,
|
||||
@@ -477,12 +538,7 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
child: ExtendedRefreshIndicator(
|
||||
onRefresh: notifier.refresh,
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverGap(8 + MediaQuery.paddingOf(context).top),
|
||||
bodyView,
|
||||
],
|
||||
),
|
||||
child: CustomScrollView(slivers: [SliverGap(8), bodyView]),
|
||||
),
|
||||
).padding(horizontal: 8),
|
||||
);
|
||||
|
||||
@@ -22,6 +22,7 @@ class SliverPostList extends HookConsumerWidget {
|
||||
final PostItemType itemType;
|
||||
final Color? backgroundColor;
|
||||
final EdgeInsets? padding;
|
||||
final EdgeInsets? itemPadding;
|
||||
final bool isOpenable;
|
||||
final Function? onRefresh;
|
||||
final Function(SnPost)? onUpdate;
|
||||
@@ -34,6 +35,7 @@ class SliverPostList extends HookConsumerWidget {
|
||||
this.itemType = PostItemType.regular,
|
||||
this.backgroundColor,
|
||||
this.padding,
|
||||
this.itemPadding,
|
||||
this.isOpenable = true,
|
||||
this.onRefresh,
|
||||
this.onUpdate,
|
||||
@@ -74,17 +76,17 @@ class SliverPostList extends HookConsumerWidget {
|
||||
return Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: maxWidth!),
|
||||
child: _buildPostItem(post),
|
||||
child: _buildPostItem(post, itemPadding),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return _buildPostItem(post);
|
||||
return _buildPostItem(post, itemPadding);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPostItem(SnPost post) {
|
||||
Widget _buildPostItem(SnPost post, EdgeInsets? padding) {
|
||||
switch (itemType) {
|
||||
case PostItemType.creator:
|
||||
return PostItemCreator(
|
||||
@@ -97,7 +99,8 @@ class SliverPostList extends HookConsumerWidget {
|
||||
);
|
||||
case PostItemType.regular:
|
||||
return Card(
|
||||
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
margin:
|
||||
itemPadding ?? EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: PostActionableItem(item: post, borderRadius: 8),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,153 +3,129 @@ 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/pods/post/post_subscriptions.dart';
|
||||
import 'package:island/models/post.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
final subscriptionsProvider = FutureProvider<List<SnPublisherSubscription>>((
|
||||
ref,
|
||||
) async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
|
||||
final response = await client.get('/sphere/publishers/subscriptions');
|
||||
|
||||
return response.data
|
||||
.map((json) => SnPublisherSubscription.fromJson(json))
|
||||
.cast<SnPublisherSubscription>()
|
||||
.toList();
|
||||
});
|
||||
|
||||
class PostSubscriptionFilterWidget extends HookConsumerWidget {
|
||||
final List<String> initialSelectedPublisherIds;
|
||||
final List<String> initialSelectedPublisherNames;
|
||||
final ValueChanged<List<String>> onSelectedPublishersChanged;
|
||||
final bool hideSearch;
|
||||
|
||||
const PostSubscriptionFilterWidget({
|
||||
super.key,
|
||||
required this.initialSelectedPublisherIds,
|
||||
required this.initialSelectedPublisherNames,
|
||||
required this.onSelectedPublishersChanged,
|
||||
this.hideSearch = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedPublisherIds = useState<List<String>>(
|
||||
initialSelectedPublisherIds,
|
||||
final selectedPublisherNames = useState<List<String>>(
|
||||
initialSelectedPublisherNames,
|
||||
);
|
||||
final showSubscriptions = useState<bool>(false);
|
||||
|
||||
final subscriptionsAsync = ref.watch(subscriptionsProvider);
|
||||
|
||||
void updateSelection() {
|
||||
onSelectedPublishersChanged(selectedPublisherIds.value);
|
||||
onSelectedPublishersChanged(selectedPublisherNames.value);
|
||||
}
|
||||
|
||||
return Card(
|
||||
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
margin: EdgeInsets.zero,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text('filterBySubscriptions'.tr()),
|
||||
leading: const Icon(Symbols.subscriptions),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(const Radius.circular(8)),
|
||||
),
|
||||
trailing: Icon(
|
||||
showSubscriptions.value
|
||||
? Symbols.expand_less
|
||||
: Symbols.expand_more,
|
||||
),
|
||||
onTap: () {
|
||||
showSubscriptions.value = !showSubscriptions.value;
|
||||
},
|
||||
),
|
||||
if (showSubscriptions.value) ...[
|
||||
const Divider(height: 1),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
subscriptionsAsync.when(
|
||||
data: (subscriptions) {
|
||||
if (subscriptions.isEmpty) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text('noSubscriptions'.tr()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: subscriptions.map((subscription) {
|
||||
final isSelected = selectedPublisherIds.value
|
||||
.contains(subscription.publisherId);
|
||||
final publisher = subscription.publisher;
|
||||
|
||||
return CheckboxListTile(
|
||||
title: Text(publisher.name),
|
||||
subtitle:
|
||||
publisher.nick.isNotEmpty &&
|
||||
publisher.nick != publisher.name
|
||||
? Text(publisher.nick)
|
||||
: null,
|
||||
value: isSelected,
|
||||
onChanged: (value) {
|
||||
if (value == true) {
|
||||
selectedPublisherIds.value = [
|
||||
...selectedPublisherIds.value,
|
||||
subscription.publisherId,
|
||||
];
|
||||
} else {
|
||||
selectedPublisherIds.value =
|
||||
selectedPublisherIds.value
|
||||
.where(
|
||||
(id) =>
|
||||
id != subscription.publisherId,
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
updateSelection();
|
||||
},
|
||||
dense: true,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
secondary: const Icon(Symbols.person),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
},
|
||||
loading: () => const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
error: (error, stack) => Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text('errorLoadingSubscriptions'.tr()),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
spacing: 16,
|
||||
children: [
|
||||
const Icon(Symbols.subscriptions, size: 20),
|
||||
Text(
|
||||
'exploreFilterSubscriptions'.tr(),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
).padding(horizontal: 16, top: 12),
|
||||
const Gap(12),
|
||||
subscriptionsAsync.when(
|
||||
data: (subscriptions) {
|
||||
if (subscriptions.isEmpty) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text('noSubscriptions'.tr()),
|
||||
),
|
||||
if (subscriptionsAsync.hasValue &&
|
||||
subscriptionsAsync.value!.isNotEmpty) ...[
|
||||
const Gap(12),
|
||||
Row(
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
selectedPublisherIds.value = subscriptionsAsync
|
||||
.value!
|
||||
.map((s) => s.publisherId)
|
||||
.toList();
|
||||
updateSelection();
|
||||
},
|
||||
child: Text('selectAll'.tr()),
|
||||
),
|
||||
const Gap(8),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
selectedPublisherIds.value = [];
|
||||
updateSelection();
|
||||
},
|
||||
child: Text('selectNone'.tr()),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: subscriptions.map((subscription) {
|
||||
final isSelected = selectedPublisherNames.value.contains(
|
||||
subscription.publisher.name,
|
||||
);
|
||||
final publisher = subscription.publisher;
|
||||
|
||||
return CheckboxListTile(
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
title: Text(publisher.nick),
|
||||
subtitle: Text('@${publisher.name}'),
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
],
|
||||
],
|
||||
value: isSelected,
|
||||
onChanged: (value) {
|
||||
if (value == true) {
|
||||
selectedPublisherNames.value = [
|
||||
...selectedPublisherNames.value,
|
||||
subscription.publisher.name,
|
||||
];
|
||||
} else {
|
||||
selectedPublisherNames.value = selectedPublisherNames
|
||||
.value
|
||||
.where(
|
||||
(name) => name != subscription.publisher.name,
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
updateSelection();
|
||||
},
|
||||
dense: true,
|
||||
secondary: ProfilePictureWidget(
|
||||
file: subscription.publisher.picture,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
},
|
||||
loading: () => const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
],
|
||||
error: (error, stack) => Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text('errorLoadingSubscriptions'.tr()),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user