mirror of
https://gitlab.com/mysocialportal/relatica
synced 2024-10-18 19:23:31 +00:00
168 lines
5.3 KiB
Dart
168 lines
5.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:result_monad/result_monad.dart';
|
|
|
|
import '../globals.dart';
|
|
import '../models/TimelineIdentifiers.dart';
|
|
import '../models/entry_tree_item.dart';
|
|
import '../models/exec_error.dart';
|
|
import '../models/timeline.dart';
|
|
import '../models/timeline_entry.dart';
|
|
import 'entry_manager_service.dart';
|
|
|
|
enum TimelineRefreshType {
|
|
refresh,
|
|
loadOlder,
|
|
loadNewer,
|
|
}
|
|
|
|
class TimelineManager extends ChangeNotifier {
|
|
static final _logger = Logger('$TimelineManager');
|
|
|
|
final cachedTimelines = <TimelineIdentifiers, Timeline>{};
|
|
|
|
void clear() {
|
|
cachedTimelines.clear();
|
|
getIt<EntryManagerService>().clear();
|
|
notifyListeners();
|
|
}
|
|
|
|
FutureResult<bool, ExecError> createNewStatus(String text,
|
|
{String spoilerText = '', String inReplyToId = ''}) async {
|
|
final result = await getIt<EntryManagerService>().createNewStatus(
|
|
text,
|
|
spoilerText: spoilerText,
|
|
inReplyToId: inReplyToId,
|
|
);
|
|
if (result.isSuccess) {
|
|
_logger.finest('Notifying listeners of new status created');
|
|
notifyListeners();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Result<TimelineEntry, ExecError> getEntryById(String id) {
|
|
_logger.finest('Getting entry for $id');
|
|
return getIt<EntryManagerService>().getEntryById(id);
|
|
}
|
|
|
|
Result<EntryTreeItem, ExecError> getPostTreeEntryBy(String id) {
|
|
_logger.finest('Getting post for $id');
|
|
return getIt<EntryManagerService>().getPostTreeEntryBy(id);
|
|
}
|
|
|
|
// refresh timeline gets statuses newer than the newest in that timeline
|
|
Result<List<EntryTreeItem>, ExecError> getTimeline(TimelineIdentifiers type) {
|
|
_logger.finest('Getting timeline $type');
|
|
final posts = cachedTimelines[type]?.posts;
|
|
if (posts != null) {
|
|
return Result.ok(posts);
|
|
}
|
|
|
|
updateTimeline(type, TimelineRefreshType.refresh);
|
|
|
|
return Result.ok([]);
|
|
}
|
|
|
|
///
|
|
/// id is the id of a post or comment in the chain, including the original post.
|
|
FutureResult<EntryTreeItem, ExecError> refreshStatusChain(String id) async {
|
|
_logger.finest('Refreshing post $id');
|
|
final result = await getIt<EntryManagerService>().refreshStatusChain(id);
|
|
if (result.isSuccess) {
|
|
for (final t in cachedTimelines.values) {
|
|
t.addOrUpdate([result.value]);
|
|
}
|
|
notifyListeners();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
FutureResult<EntryTreeItem, ExecError> resharePost(String id) async {
|
|
final result = await getIt<EntryManagerService>().resharePost(id);
|
|
if (result.isSuccess) {
|
|
for (final t in cachedTimelines.values) {
|
|
t.addOrUpdate([result.value]);
|
|
}
|
|
notifyListeners();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
FutureResult<EntryTreeItem, ExecError> unResharePost(String id) async {
|
|
final result = await getIt<EntryManagerService>().unResharePost(id);
|
|
if (result.isSuccess) {
|
|
for (final t in cachedTimelines.values) {
|
|
t.addOrUpdate([result.value]);
|
|
}
|
|
notifyListeners();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Future<void> updateTimeline(
|
|
TimelineIdentifiers type,
|
|
TimelineRefreshType refreshType,
|
|
) async {
|
|
_logger.finest('Updating w/$refreshType for timeline $type ');
|
|
final timeline = cachedTimelines.putIfAbsent(type, () => Timeline(type));
|
|
late final int lowestId;
|
|
late final int highestId;
|
|
switch (refreshType) {
|
|
case TimelineRefreshType.refresh:
|
|
lowestId = timeline.highestStatusId + 1;
|
|
highestId = 0;
|
|
break;
|
|
case TimelineRefreshType.loadOlder:
|
|
lowestId = timeline.lowestStatusId;
|
|
highestId = 0;
|
|
break;
|
|
case TimelineRefreshType.loadNewer:
|
|
lowestId = 0;
|
|
highestId = timeline.highestStatusId;
|
|
break;
|
|
}
|
|
(await getIt<EntryManagerService>()
|
|
.updateTimeline(type, lowestId, highestId))
|
|
.match(onSuccess: (posts) {
|
|
_logger.finest('Posts returned for adding to $type: ${posts.length}');
|
|
timeline.addOrUpdate(posts);
|
|
notifyListeners();
|
|
}, onError: (error) {
|
|
_logger.severe('Error getting timeline: $type}');
|
|
});
|
|
}
|
|
|
|
FutureResult<EntryTreeItem, ExecError> toggleFavorited(
|
|
String id, bool newStatus) async {
|
|
_logger.finest('Attempting toggling favorite $id to $newStatus');
|
|
final result =
|
|
await getIt<EntryManagerService>().toggleFavorited(id, newStatus);
|
|
if (result.isFailure) {
|
|
_logger.finest('Error toggling favorite $id: ${result.error}');
|
|
return result;
|
|
}
|
|
|
|
final update = result.value;
|
|
for (final timeline in cachedTimelines.values) {
|
|
update.entry.parentId.isEmpty
|
|
? timeline.addOrUpdate([update])
|
|
: timeline.tryUpdateComment(update);
|
|
}
|
|
|
|
notifyListeners();
|
|
return result;
|
|
}
|
|
// All statuses get dumped into the entity mangager and get full assembled posts out of it
|
|
// Timeline keeps track of posts level only so can query timeline manager for those
|
|
// Should put backing store on timelines and entity manager so can recover from restart faster
|
|
// Have a purge caches button to start that over from scratch
|
|
// Should have a contacts manager with backing store as well
|
|
// Timeline view is new control that knows how to load timeline, scrolling around with refresh and get more
|
|
// Timeline Item view displays itself and children
|
|
// Has "Add Comment" value
|
|
// Has like/dislke
|
|
// Has reshare/quote reshare (if can get that working somehow)
|
|
// If our own has delete
|
|
|
|
}
|