Files
App/lib/pods/post/post_list.dart

160 lines
5.4 KiB
Dart

import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post.dart';
import 'package:island/pods/network.dart';
import 'package:island/pods/paging.dart';
part 'post_list.freezed.dart';
@freezed
sealed class PostListQuery with _$PostListQuery {
const factory PostListQuery({
String? pubName,
List<String>? publishers,
String? realm,
int? type,
List<String>? categories,
List<String>? tags,
bool? pinned,
@Default(false) bool shuffle,
bool? includeReplies,
bool? mediaOnly,
String? queryTerm,
String? order,
int? periodStart,
int? periodEnd,
@Default(true) bool orderDesc,
}) = _PostListQuery;
}
@freezed
sealed class PostListQueryConfig with _$PostListQueryConfig {
const factory PostListQueryConfig({
String? id,
@Default(PostListQuery()) PostListQuery initialFilter,
}) = _PostListQueryConfig;
}
final postListProvider = AsyncNotifierProvider.autoDispose.family(
PostListNotifier.new,
);
class PostListNotifier extends AsyncNotifier<List<SnPost>>
with
AsyncPaginationController<SnPost>,
AsyncPaginationFilter<PostListQuery, SnPost> {
static const int pageSize = 20;
final String? id;
final PostListQueryConfig config;
PostListNotifier(this.config) : id = config.id;
@override
late PostListQuery currentFilter;
@override
Future<List<SnPost>> build() async {
currentFilter = config.initialFilter;
return fetch();
}
@override
Future<List<SnPost>> fetch() async {
final client = ref.read(apiClientProvider);
// Handle multiple publishers by making separate requests and combining results
if (currentFilter.publishers != null &&
currentFilter.publishers!.isNotEmpty) {
final allPosts = <SnPost>[];
var totalPostsCount = 0;
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();
}
}
}