Add updatePost to get all comments on post

main
Hank Grabowski 2022-11-18 18:31:28 -05:00
rodzic 1524cc217a
commit 37857a96d6
6 zmienionych plików z 102 dodań i 23 usunięć

Wyświetl plik

@ -1,6 +1,8 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.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: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 'package:url_launcher/url_launcher.dart';
import '../../globals.dart'; import '../../globals.dart';
@ -15,12 +17,24 @@ import '../../utils/snackbar_builder.dart';
import '../padding.dart'; import '../padding.dart';
import 'interactions_bar_control.dart'; import 'interactions_bar_control.dart';
class StatusControl extends StatelessWidget { class StatusControl extends StatefulWidget {
final EntryTreeItem item; final EntryTreeItem originalItem;
const StatusControl({super.key, required this.originalItem});
@override
State<StatusControl> createState() => _StatusControlState();
}
class _StatusControlState extends State<StatusControl> {
late EntryTreeItem item;
TimelineEntry get entry => item.entry; TimelineEntry get entry => item.entry;
const StatusControl({super.key, required this.item}); @override
void initState() {
super.initState();
item = widget.originalItem;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -166,7 +180,7 @@ class StatusControl extends StatelessWidget {
} }
Widget buildChildComments(BuildContext context) { Widget buildChildComments(BuildContext context) {
final comments = item.children; final comments = widget.originalItem.children;
if (comments.isEmpty) { if (comments.isEmpty) {
return Text('No comments'); return Text('No comments');
} }
@ -176,10 +190,26 @@ class StatusControl extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (entry.parentId.isEmpty)
ElevatedButton(
onPressed: () async {
await getIt<EntryManagerService>()
.refreshPost(widget.originalItem)
.andThenSuccessAsync((newItem) async => setState(() {
print('Updated item');
item = newItem;
}));
},
child: Center(
child: Text('Load All'),
),
),
Icon(Icons.subdirectory_arrow_right), Icon(Icons.subdirectory_arrow_right),
Expanded( Expanded(
child: Column( child: Column(
children: comments.map((c) => StatusControl(item: c)).toList(), children: comments
.map((c) => StatusControl(originalItem: c))
.toList(),
), ),
), ),
], ],

Wyświetl plik

@ -44,14 +44,18 @@ class FriendicaClient {
} }
FutureResult<List<TimelineEntry>, ExecError> getTimeline( FutureResult<List<TimelineEntry>, ExecError> getTimeline(
{required TimelineIdentifiers type, int limit = 20}) async { {required TimelineIdentifiers type,
_logger.finest(() => 'Getting home timeline limit $limit'); int sinceId = 0,
int limit = 100}) async {
final String timelinePath = _typeToTimelinePath(type); final String timelinePath = _typeToTimelinePath(type);
final String timelineQPs = _typeToTimelineQueryParameters(type); final String timelineQPs = _typeToTimelineQueryParameters(type);
final baseUrl = 'https://$serverName/api/v1/timelines/$timelinePath'; 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 url = '$baseUrl?$pagingData&$timelineQPs';
final request = Uri.parse(url); final request = Uri.parse(url);
_logger.finest(
() => 'Getting home timeline limit $limit since $sinceId : $url');
return (await _getApiListRequest(request).andThenSuccessAsync( return (await _getApiListRequest(request).andThenSuccessAsync(
(postsJson) async => postsJson (postsJson) async => postsJson
.map((json) => TimelineEntryMastodonExtensions.fromJson(json)) .map((json) => TimelineEntryMastodonExtensions.fromJson(json))

Wyświetl plik

@ -8,6 +8,8 @@ class Timeline {
int _lowestStatusId = 0; int _lowestStatusId = 0;
int _highestStatusId = 0; int _highestStatusId = 0;
int get highestStatusId => _highestStatusId;
Timeline(this.id, {List<EntryTreeItem>? initialPosts}) { Timeline(this.id, {List<EntryTreeItem>? initialPosts}) {
if (initialPosts != null) { if (initialPosts != null) {
addPosts(initialPosts); addPosts(initialPosts);

Wyświetl plik

@ -75,7 +75,7 @@ class _HomeScreenState extends State<HomeScreen> {
child: ListView.separated( child: ListView.separated(
itemBuilder: (context, index) { itemBuilder: (context, index) {
print('Building item: $index'); print('Building item: $index');
return StatusControl(item: items[index]); return StatusControl(originalItem: items[index]);
}, },
separatorBuilder: (context, index) => Divider(), separatorBuilder: (context, index) => Divider(),
itemCount: items.length, itemCount: items.length,

Wyświetl plik

@ -17,7 +17,7 @@ class EntryManagerService extends ChangeNotifier {
final Map<String, EntryTreeItem> _allComments = {}; final Map<String, EntryTreeItem> _allComments = {};
FutureResult<List<EntryTreeItem>, ExecError> updateTimeline( FutureResult<List<EntryTreeItem>, ExecError> updateTimeline(
TimelineIdentifiers type) async { TimelineIdentifiers type, int highestId) async {
_logger.fine(() => 'Updating timeline'); _logger.fine(() => 'Updating timeline');
final auth = getIt<AuthService>(); final auth = getIt<AuthService>();
final clientResult = auth.currentClient; final clientResult = auth.currentClient;
@ -27,7 +27,8 @@ class EntryManagerService extends ChangeNotifier {
} }
final client = clientResult.value; 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; final myHandle = client.credentials.username;
if (itemsResult.isFailure) { if (itemsResult.isFailure) {
_logger.severe('Error getting timeline: ${itemsResult.error}'); _logger.severe('Error getting timeline: ${itemsResult.error}');
@ -50,21 +51,30 @@ class EntryManagerService extends ChangeNotifier {
FriendicaClient? client, FriendicaClient? client,
) async { ) async {
final updatedPosts = <EntryTreeItem>[]; final updatedPosts = <EntryTreeItem>[];
for (final t in items) { for (final item in items) {
late EntryTreeItem entry; late EntryTreeItem entry;
final isMine = t.author == myHandle; final isMine = item.author == myHandle;
if (t.parentAuthor.isEmpty) { if (item.parentAuthor.isEmpty) {
entry = _posts.putIfAbsent( entry = _posts.putIfAbsent(item.id,
t.id, () => EntryTreeItem(t, isMine: isMine, isOrphaned: false)); () => EntryTreeItem(item, isMine: isMine, isOrphaned: false));
updatedPosts.add(entry); updatedPosts.add(entry);
} else { } else {
final parent = _posts[t.parentId]; final parentPost = _posts[item.parentId];
bool newEntry = false; bool newEntry = false;
entry = _allComments.putIfAbsent(t.id, () { entry = _allComments.putIfAbsent(item.id, () {
newEntry = true; 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) { if (newEntry && entry.isOrphaned) {
_orphanedComments.add(entry); _orphanedComments.add(entry);
} }
@ -77,6 +87,7 @@ class EntryManagerService extends ChangeNotifier {
if (post != null) { if (post != null) {
c.isOrphaned = false; c.isOrphaned = false;
post.addChild(c); post.addChild(c);
updatedPosts.add(post);
continue; continue;
} }
@ -105,6 +116,37 @@ class EntryManagerService extends ChangeNotifier {
return updatedPosts; return updatedPosts;
} }
FutureResult<EntryTreeItem, ExecError> refreshPost(EntryTreeItem item) async {
_logger.finest('Refreshing post: ${item.id}');
final auth = getIt<AuthService>();
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<EntryTreeItem, ExecError> toggleFavorited( FutureResult<EntryTreeItem, ExecError> toggleFavorited(
String id, bool newStatus, String id, bool newStatus,
{bool notify = false}) async { {bool notify = false}) async {

Wyświetl plik

@ -27,9 +27,10 @@ class TimelineManager extends ChangeNotifier {
} }
Future<void> refreshTimeline(TimelineIdentifiers type) async { Future<void> refreshTimeline(TimelineIdentifiers type) async {
(await getIt<EntryManagerService>().updateTimeline(type)).match( final timeline = cachedTimelines.putIfAbsent(type, () => Timeline(type));
onSuccess: (posts) { (await getIt<EntryManagerService>()
final timeline = cachedTimelines.putIfAbsent(type, () => Timeline(type)); .updateTimeline(type, timeline.highestStatusId))
.match(onSuccess: (posts) {
_logger.finest('Posts returned for adding to $type: ${posts.length}'); _logger.finest('Posts returned for adding to $type: ${posts.length}');
timeline.addPosts(posts); timeline.addPosts(posts);
notifyListeners(); notifyListeners();