relatica/lib/services/entry_manager_service.dart

156 wiersze
4.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:result_monad/result_monad.dart';
import '../friendica_client.dart';
import '../globals.dart';
import '../models/TimelineIdentifiers.dart';
import '../models/entry_tree_item.dart';
import '../models/exec_error.dart';
import '../models/timeline_entry.dart';
import 'auth_service.dart';
class EntryManagerService extends ChangeNotifier {
static final _logger = Logger('$EntryManagerService');
final Map<String, EntryTreeItem> _posts = {};
final List<EntryTreeItem> _orphanedComments = [];
final Map<String, EntryTreeItem> _allComments = {};
FutureResult<List<EntryTreeItem>, ExecError> updateTimeline(
TimelineIdentifiers type) async {
_logger.fine(() => 'Updating timeline');
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 itemsResult = await client.getTimeline(type: type);
final myHandle = client.credentials.username;
if (itemsResult.isFailure) {
_logger.severe('Error getting timeline: ${itemsResult.error}');
return itemsResult.errorCast();
}
itemsResult.value.sort((t1, t2) => t1.id.compareTo(t2.id));
final updatedPosts =
await processNewItems(itemsResult.value, myHandle, client);
_orphanedComments.removeWhere((element) => !element.isOrphaned);
_logger.finest(() =>
'End of update # posts: ${_posts.length}, #comments: ${_allComments.length}, #orphans: ${_orphanedComments.length}');
notifyListeners();
return Result.ok(updatedPosts);
}
Future<List<EntryTreeItem>> processNewItems(
List<TimelineEntry> items,
String myHandle,
FriendicaClient? client,
) async {
final updatedPosts = <EntryTreeItem>[];
for (final t 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));
updatedPosts.add(entry);
} else {
final parent = _posts[t.parentId];
bool newEntry = false;
entry = _allComments.putIfAbsent(t.id, () {
newEntry = true;
return EntryTreeItem(t, isMine: isMine, isOrphaned: parent == null);
});
parent?.addChild(entry);
if (newEntry && entry.isOrphaned) {
_orphanedComments.add(entry);
}
}
}
final orphansToProcess = [];
for (final c in _orphanedComments) {
final post = _posts[c.entry.parentId];
if (post != null) {
c.isOrphaned = false;
post.addChild(c);
continue;
}
final comment = _allComments[c.entry.parentId];
if (comment != null) {
c.isOrphaned = false;
comment.addChild(c);
continue;
}
if (client != null) {
orphansToProcess.add(c);
}
}
for (final o in orphansToProcess) {
await client
?.getPostOrComment(o.id, fullContext: true)
.andThenSuccessAsync((items) async {
final moreUpdatedPosts = await processNewItems(items, myHandle, null);
updatedPosts.addAll(moreUpdatedPosts);
});
}
_logger.finest(
'Completed processing new items ${client == null ? 'sub level' : 'top level'}');
return updatedPosts;
}
FutureResult<EntryTreeItem, ExecError> toggleFavorited(
String id, bool newStatus,
{bool notify = false}) async {
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.changeFavoriteStatus(id, newStatus);
if (result.isFailure) {
return result.errorCast();
}
final update = result.value;
late EntryTreeItem rval;
if (_posts.containsKey(update.id)) {
rval = _posts[update.id]!.copy(entry: update);
_posts[update.id] = rval;
_updateChildrenEntities(rval, update);
}
if (_allComments.containsKey(update.id)) {
rval = _allComments[update.id]!.copy(entry: update);
_allComments[update.id] = rval;
_updateChildrenEntities(rval, update);
}
if (notify) {
notifyListeners();
}
return Result.ok(rval);
}
void _updateChildrenEntities(EntryTreeItem item, TimelineEntry entry) {
final updates = item.children.where((element) => element.id == entry.id);
for (final u in updates) {
final newItem = u.copy(entry: entry);
item.children.remove(u);
item.children.add(newItem);
}
for (final c in item.children) {
_updateChildrenEntities(c, entry);
}
}
}