relatica/lib/services/notifications_manager.dart

223 wiersze
7.1 KiB
Dart
Czysty Zwykły widok Historia

import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:result_monad/result_monad.dart';
import 'package:uuid/uuid.dart';
import '../friendica_client/friendica_client.dart';
import '../friendica_client/paged_response.dart';
import '../friendica_client/pages_manager.dart';
import '../friendica_client/paging_data.dart';
import '../globals.dart';
import '../models/exec_error.dart';
import '../models/user_notification.dart';
import '../utils/active_profile_selector.dart';
import 'auth_service.dart';
import 'direct_message_service.dart';
import 'network_status_service.dart';
class NotificationsManager extends ChangeNotifier {
static final _logger = Logger('NotificationManager');
final _notifications = <String, UserNotification>{};
final _pm = PagesManager<List<UserNotification>, String>(
idMapper: (nn) => nn.map((n) => n.id).toList(),
onRequest: _clientGetNotificationsRequest,
);
var _firstLoad = true;
List<UserNotification> get notifications {
if (_notifications.isEmpty && _firstLoad) {
updateNotifications();
_firstLoad = false;
}
final result = List<UserNotification>.from(_notifications.values);
result.sort((n1, n2) {
if (n1.dismissed == n2.dismissed) {
return n2.timestamp.compareTo(n1.timestamp);
}
if (n1.dismissed && !n2.dismissed) {
return 1;
}
return -1;
});
return result;
}
void clear() {
_notifications.clear();
_pm.clear();
}
FutureResult<List<UserNotification>, ExecError> updateNotifications() async {
const initialPull = 100;
final nn = <UserNotification>[];
if (_pm.pages.isEmpty) {
final result = await _pm.initialize(initialPull);
result.andThenSuccess((response) => nn.addAll(response.data));
} else {
for (var i = 0; i < _pm.pages.length; i++) {
if (i > 0 && i == _pm.pages.length - 1) {
continue;
}
final page = _pm.pages[i];
PagingData? pd;
if (i == 0) {
if (page.next == null) {
_logger.severe(
"Expected first page to have a next page so can query on this page of data but doesn't exist.");
continue;
}
final response = await _clientGetNotificationsRequest(page.next!);
response.match(
onSuccess: (response) => pd = response.previous,
onError: (error) =>
_logger.severe('Error getting previous page: $error'));
if (pd == null && page.previous == null) {
_logger.severe(
'Next page returned no results and no previous page so need to re-initalize');
} else {
final response =
await _clientGetNotificationsRequest(page.previous!);
response.match(
onSuccess: (response) => pd = response.next,
onError: (error) =>
_logger.severe('Error getting previous page: $error'));
}
if (pd == null) {
_logger.severe(
'Previous and next page both returned nulls so need to reinitialize');
_pm.clear();
final result = await _pm.initialize(initialPull);
result.andThenSuccess((response) => nn.addAll(response.data));
}
}
if (page.next == null) {
if (i != _pm.pages.length - 2) {
_logger
.severe('No forward paging data in middle page but expected');
}
continue;
}
final response = await _clientGetNotificationsRequest(page.next!);
response.match(
onSuccess: (response) => nn.addAll(response.data),
onError: (error) =>
_logger.severe('Error getting previous page: $error'));
}
}
for (final n in nn) {
_notifications[n.id] = n;
}
_notifications.removeWhere(
(key, value) => value.type == NotificationType.direct_message,
);
getIt<NetworkStatusService>().startNotificationUpdate();
await getIt<ActiveProfileSelector<DirectMessageService>>()
.activeEntry
.andThenSuccessAsync((dms) async => await dms.updateThreads());
getIt<NetworkStatusService>().finishNotificationUpdate();
for (final n in buildUnreadMessageNotifications()) {
_notifications[n.id] = n;
}
notifyListeners();
return Result.ok(notifications);
}
FutureResult<List<UserNotification>, ExecError>
loadNewerNotifications() async {
final result = await _pm.previousFromBeginning();
result.match(onSuccess: (response) {
for (final n in response.data) {
_notifications[n.id] = n;
}
2023-02-14 13:38:08 +00:00
notifyListeners();
}, onError: (error) {
2023-02-14 13:38:08 +00:00
_logger.info('Error getting more updates: $error');
});
2023-02-14 13:38:08 +00:00
return result.mapValue((response) => response.data);
}
FutureResult<List<UserNotification>, ExecError>
loadOlderNotifications() async {
final result = await _pm.nextFromEnd();
result.match(onSuccess: (response) {
for (final n in response.data) {
_notifications[n.id] = n;
}
2023-02-14 13:38:08 +00:00
notifyListeners();
}, onError: (error) {
2023-02-14 13:38:08 +00:00
_logger.info('Error getting more updates: $error');
});
2023-02-14 13:38:08 +00:00
return result.mapValue((response) => response.data);
}
FutureResult<bool, ExecError> markSeen(UserNotification notification) async {
final result =
await NotificationsClient(getIt<AccountsService>().currentProfile)
.clearNotification(notification);
if (result.isSuccess) {
notifyListeners();
}
updateNotifications();
return result;
}
FutureResult<List<UserNotification>, ExecError> markAllAsRead() async {
final result =
await NotificationsClient(getIt<AccountsService>().currentProfile)
.clearNotifications();
if (result.isFailure) {
return result.errorCast();
}
2023-02-14 13:38:08 +00:00
_pm.clear();
_notifications.clear();
return updateNotifications();
}
List<UserNotification> buildUnreadMessageNotifications() {
final myId = getIt<AccountsService>().currentProfile.userId;
final result = getIt<ActiveProfileSelector<DirectMessageService>>()
.activeEntry
.value
.getThreads(unreadyOnly: true)
.map((t) {
final fromAccount = t.participants.firstWhere((p) => p.id != myId);
final latestMessage =
t.messages.reduce((s, m) => s.createdAt > m.createdAt ? s : m);
return UserNotification(
id: const Uuid().v4(),
type: NotificationType.direct_message,
fromId: fromAccount.id,
fromName: fromAccount.name,
fromUrl: fromAccount.profileUrl,
timestamp: latestMessage.createdAt,
iid: t.parentUri,
dismissed: false,
content: '${fromAccount.name} sent you a direct message',
link: '');
}).toList();
return result;
}
static FutureResult<PagedResponse<List<UserNotification>>, ExecError>
_clientGetNotificationsRequest(PagingData page) async {
final result =
await NotificationsClient(getIt<AccountsService>().currentProfile)
.getNotifications(page);
return result;
}
}