From 37857a96d6429504f9cafe0630d08112c6548736 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Fri, 18 Nov 2022 18:31:28 -0500 Subject: [PATCH] Add updatePost to get all comments on post --- lib/controls/timeline/status_control.dart | 40 ++++++++++++-- lib/friendica_client.dart | 10 ++-- lib/models/timeline.dart | 2 + lib/screens/home.dart | 2 +- lib/services/entry_manager_service.dart | 64 +++++++++++++++++++---- lib/services/timeline_manager.dart | 7 +-- 6 files changed, 102 insertions(+), 23 deletions(-) diff --git a/lib/controls/timeline/status_control.dart b/lib/controls/timeline/status_control.dart index 1432e21..7504ef9 100644 --- a/lib/controls/timeline/status_control.dart +++ b/lib/controls/timeline/status_control.dart @@ -1,6 +1,8 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_portal/services/entry_manager_service.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; +import 'package:result_monad/result_monad.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../globals.dart'; @@ -15,12 +17,24 @@ import '../../utils/snackbar_builder.dart'; import '../padding.dart'; import 'interactions_bar_control.dart'; -class StatusControl extends StatelessWidget { - final EntryTreeItem item; +class StatusControl extends StatefulWidget { + final EntryTreeItem originalItem; + const StatusControl({super.key, required this.originalItem}); + + @override + State createState() => _StatusControlState(); +} + +class _StatusControlState extends State { + late EntryTreeItem item; TimelineEntry get entry => item.entry; - const StatusControl({super.key, required this.item}); + @override + void initState() { + super.initState(); + item = widget.originalItem; + } @override Widget build(BuildContext context) { @@ -166,7 +180,7 @@ class StatusControl extends StatelessWidget { } Widget buildChildComments(BuildContext context) { - final comments = item.children; + final comments = widget.originalItem.children; if (comments.isEmpty) { return Text('No comments'); } @@ -176,10 +190,26 @@ class StatusControl extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ + if (entry.parentId.isEmpty) + ElevatedButton( + onPressed: () async { + await getIt() + .refreshPost(widget.originalItem) + .andThenSuccessAsync((newItem) async => setState(() { + print('Updated item'); + item = newItem; + })); + }, + child: Center( + child: Text('Load All'), + ), + ), Icon(Icons.subdirectory_arrow_right), Expanded( child: Column( - children: comments.map((c) => StatusControl(item: c)).toList(), + children: comments + .map((c) => StatusControl(originalItem: c)) + .toList(), ), ), ], diff --git a/lib/friendica_client.dart b/lib/friendica_client.dart index e3b0cfe..fd1f117 100644 --- a/lib/friendica_client.dart +++ b/lib/friendica_client.dart @@ -44,14 +44,18 @@ class FriendicaClient { } FutureResult, ExecError> getTimeline( - {required TimelineIdentifiers type, int limit = 20}) async { - _logger.finest(() => 'Getting home timeline limit $limit'); + {required TimelineIdentifiers type, + int sinceId = 0, + int limit = 100}) async { final String timelinePath = _typeToTimelinePath(type); final String timelineQPs = _typeToTimelineQueryParameters(type); final baseUrl = 'https://$serverName/api/v1/timelines/$timelinePath'; - final pagingData = 'limit=$limit'; + final pagingData = + sinceId == 0 ? 'limit=$limit' : 'since_id=$sinceId&limit=$limit'; final url = '$baseUrl?$pagingData&$timelineQPs'; final request = Uri.parse(url); + _logger.finest( + () => 'Getting home timeline limit $limit since $sinceId : $url'); return (await _getApiListRequest(request).andThenSuccessAsync( (postsJson) async => postsJson .map((json) => TimelineEntryMastodonExtensions.fromJson(json)) diff --git a/lib/models/timeline.dart b/lib/models/timeline.dart index d230d43..d5eb47a 100644 --- a/lib/models/timeline.dart +++ b/lib/models/timeline.dart @@ -8,6 +8,8 @@ class Timeline { int _lowestStatusId = 0; int _highestStatusId = 0; + int get highestStatusId => _highestStatusId; + Timeline(this.id, {List? initialPosts}) { if (initialPosts != null) { addPosts(initialPosts); diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 7ad7df4..233ca26 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -75,7 +75,7 @@ class _HomeScreenState extends State { child: ListView.separated( itemBuilder: (context, index) { print('Building item: $index'); - return StatusControl(item: items[index]); + return StatusControl(originalItem: items[index]); }, separatorBuilder: (context, index) => Divider(), itemCount: items.length, diff --git a/lib/services/entry_manager_service.dart b/lib/services/entry_manager_service.dart index ea8cc2c..1263369 100644 --- a/lib/services/entry_manager_service.dart +++ b/lib/services/entry_manager_service.dart @@ -17,7 +17,7 @@ class EntryManagerService extends ChangeNotifier { final Map _allComments = {}; FutureResult, ExecError> updateTimeline( - TimelineIdentifiers type) async { + TimelineIdentifiers type, int highestId) async { _logger.fine(() => 'Updating timeline'); final auth = getIt(); final clientResult = auth.currentClient; @@ -27,7 +27,8 @@ class EntryManagerService extends ChangeNotifier { } final client = clientResult.value; - final itemsResult = await client.getTimeline(type: type); + final itemsResult = + await client.getTimeline(type: type, sinceId: highestId); final myHandle = client.credentials.username; if (itemsResult.isFailure) { _logger.severe('Error getting timeline: ${itemsResult.error}'); @@ -50,21 +51,30 @@ class EntryManagerService extends ChangeNotifier { FriendicaClient? client, ) async { final updatedPosts = []; - for (final t in items) { + for (final item in items) { late EntryTreeItem entry; - final isMine = t.author == myHandle; - if (t.parentAuthor.isEmpty) { - entry = _posts.putIfAbsent( - t.id, () => EntryTreeItem(t, isMine: isMine, isOrphaned: false)); + final isMine = item.author == myHandle; + if (item.parentAuthor.isEmpty) { + entry = _posts.putIfAbsent(item.id, + () => EntryTreeItem(item, isMine: isMine, isOrphaned: false)); updatedPosts.add(entry); } else { - final parent = _posts[t.parentId]; + final parentPost = _posts[item.parentId]; bool newEntry = false; - entry = _allComments.putIfAbsent(t.id, () { + entry = _allComments.putIfAbsent(item.id, () { newEntry = true; - return EntryTreeItem(t, isMine: isMine, isOrphaned: parent == null); + return EntryTreeItem( + item, + isMine: isMine, + isOrphaned: parentPost == null, + ); }); - parent?.addChild(entry); + + if (parentPost != null) { + parentPost.addChild(entry); + updatedPosts.add(parentPost); + } + if (newEntry && entry.isOrphaned) { _orphanedComments.add(entry); } @@ -77,6 +87,7 @@ class EntryManagerService extends ChangeNotifier { if (post != null) { c.isOrphaned = false; post.addChild(c); + updatedPosts.add(post); continue; } @@ -105,6 +116,37 @@ class EntryManagerService extends ChangeNotifier { return updatedPosts; } + FutureResult refreshPost(EntryTreeItem item) async { + _logger.finest('Refreshing post: ${item.id}'); + final auth = getIt(); + final clientResult = auth.currentClient; + if (clientResult.isFailure) { + _logger.severe('Error getting Friendica client: ${clientResult.error}'); + return clientResult.errorCast(); + } + + final client = clientResult.value; + final result = await client + .getPostOrComment(item.id, fullContext: true) + .andThenSuccessAsync((items) async { + await processNewItems(items, client.credentials.username, null); + }); + + return result.mapValue((_) { + _logger.finest('${item.id} post updated'); + notifyListeners(); + return _posts[item.id]!; + }).mapError( + (error) { + _logger.finest('${item.id} error updating: $error'); + return ExecError( + type: ErrorType.localError, + message: error.toString(), + ); + }, + ); + } + FutureResult toggleFavorited( String id, bool newStatus, {bool notify = false}) async { diff --git a/lib/services/timeline_manager.dart b/lib/services/timeline_manager.dart index 6ab07a5..5005bd4 100644 --- a/lib/services/timeline_manager.dart +++ b/lib/services/timeline_manager.dart @@ -27,9 +27,10 @@ class TimelineManager extends ChangeNotifier { } Future refreshTimeline(TimelineIdentifiers type) async { - (await getIt().updateTimeline(type)).match( - onSuccess: (posts) { - final timeline = cachedTimelines.putIfAbsent(type, () => Timeline(type)); + final timeline = cachedTimelines.putIfAbsent(type, () => Timeline(type)); + (await getIt() + .updateTimeline(type, timeline.highestStatusId)) + .match(onSuccess: (posts) { _logger.finest('Posts returned for adding to $type: ${posts.length}'); timeline.addPosts(posts); notifyListeners();