From f4d298e4e89e403c4b956a239c2256863467cea7 Mon Sep 17 00:00:00 2001 From: olorin99 Date: Thu, 9 Jul 2026 16:45:14 +1000 Subject: [PATCH] Switch to mbin combined API rather than merging threads/microblogs client side. --- lib/src/api/threads.dart | 23 +++++--- lib/src/models/post.dart | 15 ++++++ lib/src/screens/feed/feed_agregator.dart | 68 ++++-------------------- 3 files changed, 41 insertions(+), 65 deletions(-) diff --git a/lib/src/api/threads.dart b/lib/src/api/threads.dart index 58bd2374..421f1d34 100644 --- a/lib/src/api/threads.dart +++ b/lib/src/api/threads.dart @@ -40,17 +40,24 @@ class APIThreads { int? sourceId, String? page, FeedSort? sort, + bool combined = false, + bool includeBoosts = false, }) async { switch (client.software) { case ServerSoftware.mbin: final path = switch (source) { - FeedSource.all => '/entries', - FeedSource.local => '/entries', - FeedSource.subscribed => '/entries/subscribed', - FeedSource.moderated => '/entries/moderated', - FeedSource.favorited => '/entries/favourited', - FeedSource.community => '/magazine/${sourceId!}/entries', - FeedSource.user => '/users/${sourceId!}/entries', + FeedSource.all => '/${combined ? 'combined' : 'entries'}', + FeedSource.local => '/${combined ? 'combined' : 'entries'}', + FeedSource.subscribed => + '/${combined ? 'combined' : 'entries'}/subscribed', + FeedSource.moderated => + '/${combined ? 'combined' : 'entries'}/moderated', + FeedSource.favorited => + '/${combined ? 'combined' : 'entries'}/favourited', + FeedSource.community => + '/magazine/${sourceId!}/${combined ? 'combined' : 'entries'}', + FeedSource.user => + '/users/${sourceId!}/${combined ? 'content' : 'entries'}', FeedSource.domain => '/domain/${sourceId!}/entries', FeedSource.feed => throw Exception( 'Feeds source not allowed for mbin', @@ -64,10 +71,12 @@ class APIThreads { 'sort': mbinGetSort(sort)?.name, 'time': mbinGetSortTime(sort), if (source == FeedSource.local) 'federation': 'local', + if (combined && includeBoosts) 'includeBoosts': 'true', }; final response = await client.get(path, queryParams: query); + if (combined) return PostListModel.fromMbinCombined(response.bodyJson); return PostListModel.fromMbinEntries(response.bodyJson); case ServerSoftware.lemmy: diff --git a/lib/src/models/post.dart b/lib/src/models/post.dart index 3048884d..8ba34633 100644 --- a/lib/src/models/post.dart +++ b/lib/src/models/post.dart @@ -39,6 +39,21 @@ abstract class PostListModel with _$PostListModel { nextPage: mbinCalcNextPaginationPage(json['pagination']! as JsonMap), ); + factory PostListModel.fromMbinCombined(JsonMap json) => PostListModel( + items: (json['items']! as List) + .map((content) { + if (content['entry'] != null) { + return PostModel.fromMbinEntry(content['entry'] as JsonMap); + } + if (content['post'] != null) { + return PostModel.fromMbinPost(content['post'] as JsonMap); + } + }) + .nonNulls + .toList(), + nextPage: mbinCalcNextPaginationPage(json['pagination']! as JsonMap), + ); + factory PostListModel.fromLemmy( JsonMap json, { required List<(String, int)> langCodeIdPairs, diff --git a/lib/src/screens/feed/feed_agregator.dart b/lib/src/screens/feed/feed_agregator.dart index ccdacdfc..8a16583c 100644 --- a/lib/src/screens/feed/feed_agregator.dart +++ b/lib/src/screens/feed/feed_agregator.dart @@ -200,10 +200,7 @@ class FeedInputState { final FeedSource source; final int? sourceId; List _leftover = []; - List _combinedThreadsLeftover = []; - List _combinedMicroblogsLeftover = []; String? _nextPage = ''; - String? _combinedPage = ''; Future<(List, String?)> fetchPage( AppController ac, @@ -235,57 +232,15 @@ class FeedInputState { _nextPage = postListModel.nextPage; return ([..._leftover, ...postListModel.items], postListModel.nextPage); case FeedView.combined: - final threadFuture = - _nextPage != null && _combinedThreadsLeftover.length < 25 - ? ac.api.threads.list( - source, - sourceId: sourceId, - page: nullIfEmpty(_nextPage!), - sort: sort, - ) - : Future.value(); - final microblogFuture = - _combinedPage != null && _combinedMicroblogsLeftover.length < 25 - ? ac.api.microblogs.list( - source, - sourceId: sourceId, - page: nullIfEmpty(_combinedPage!), - sort: sort, - ) - : Future.value(); - final results = await Future.wait([threadFuture, microblogFuture]); - - final postLists = results - .map((postListModel) => postListModel?.items ?? []) - .toList(); - final merged = merge(ac.serverSoftware, [ - [..._combinedThreadsLeftover, ...postLists[0]], - [..._combinedMicroblogsLeftover, ...postLists[1]], - ], sort); - - // get next page if new request was sent - if (_combinedMicroblogsLeftover.length < 25) { - _combinedPage = results.last?.nextPage; - } - if (_combinedThreadsLeftover.length < 25) { - _nextPage = results.first?.nextPage; - } - - _combinedThreadsLeftover = merged.$2.isNotEmpty ? merged.$2.first : []; - _combinedMicroblogsLeftover = merged.$2.length > 1 - ? merged.$2.last - : []; - - // if final page of input also return leftover posts - final result = [..._leftover, ...merged.$1]; - if (_nextPage == null) { - result.addAll(_combinedThreadsLeftover); - } - if (_combinedPage == null) { - result.addAll(_combinedMicroblogsLeftover); - } - - return (result, _nextPage ?? _combinedPage); + final postListModel = await ac.api.threads.list( + source, + sourceId: sourceId, + page: nullIfEmpty(_nextPage!), + sort: sort, + combined: true, + ); + _nextPage = postListModel.nextPage; + return ([..._leftover, ...postListModel.items], postListModel.nextPage); } } @@ -381,10 +336,7 @@ class FeedAggregator { for (final input in inputs) { input .._leftover = [] - .._nextPage = '' - .._combinedPage = '' - .._combinedThreadsLeftover = [] - .._combinedMicroblogsLeftover = []; + .._nextPage = ''; } }