2022-11-17 16:04:14 +00:00
|
|
|
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 = {};
|
|
|
|
|
2022-11-21 03:26:49 +00:00
|
|
|
void clear() {
|
|
|
|
_posts.clear();
|
|
|
|
_orphanedComments.clear();
|
|
|
|
_allComments.clear();
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
2022-11-17 16:04:14 +00:00
|
|
|
FutureResult<List<EntryTreeItem>, ExecError> updateTimeline(
|
2022-11-18 23:31:28 +00:00
|
|
|
TimelineIdentifiers type, int highestId) async {
|
2022-11-17 16:04:14 +00:00
|
|
|
_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;
|
2022-11-18 23:31:28 +00:00
|
|
|
final itemsResult =
|
|
|
|
await client.getTimeline(type: type, sinceId: highestId);
|
2022-11-17 16:04:14 +00:00
|
|
|
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>[];
|
2022-11-18 23:31:28 +00:00
|
|
|
for (final item in items) {
|
2022-11-17 16:04:14 +00:00
|
|
|
late EntryTreeItem entry;
|
2022-11-18 23:31:28 +00:00
|
|
|
final isMine = item.author == myHandle;
|
|
|
|
if (item.parentAuthor.isEmpty) {
|
|
|
|
entry = _posts.putIfAbsent(item.id,
|
|
|
|
() => EntryTreeItem(item, isMine: isMine, isOrphaned: false));
|
2022-11-17 16:04:14 +00:00
|
|
|
updatedPosts.add(entry);
|
|
|
|
} else {
|
2022-11-18 23:31:28 +00:00
|
|
|
final parentPost = _posts[item.parentId];
|
2022-11-17 16:04:14 +00:00
|
|
|
bool newEntry = false;
|
2022-11-18 23:31:28 +00:00
|
|
|
entry = _allComments.putIfAbsent(item.id, () {
|
2022-11-17 16:04:14 +00:00
|
|
|
newEntry = true;
|
2022-11-18 23:31:28 +00:00
|
|
|
return EntryTreeItem(
|
|
|
|
item,
|
|
|
|
isMine: isMine,
|
|
|
|
isOrphaned: parentPost == null,
|
|
|
|
);
|
2022-11-17 16:04:14 +00:00
|
|
|
});
|
2022-11-18 23:31:28 +00:00
|
|
|
|
|
|
|
if (parentPost != null) {
|
|
|
|
parentPost.addChild(entry);
|
|
|
|
updatedPosts.add(parentPost);
|
|
|
|
}
|
|
|
|
|
2022-11-17 16:04:14 +00:00
|
|
|
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);
|
2022-11-18 23:31:28 +00:00
|
|
|
updatedPosts.add(post);
|
2022-11-17 16:04:14 +00:00
|
|
|
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;
|
|
|
|
}
|
2022-11-18 21:50:15 +00:00
|
|
|
|
2022-11-18 23:31:28 +00:00
|
|
|
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(),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-11-18 21:50:15 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2022-11-17 16:04:14 +00:00
|
|
|
}
|