kopia lustrzana https://gitlab.com/mysocialportal/relatica
212 wiersze
6.8 KiB
Dart
212 wiersze
6.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:relatica/services/network_status_service.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 'auth_service.dart';
|
|
import 'direct_message_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,
|
|
);
|
|
|
|
List<UserNotification> get notifications {
|
|
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 = 25;
|
|
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<DirectMessageService>().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;
|
|
}
|
|
notifyListeners();
|
|
}, onError: (error) {
|
|
_logger.info('Error getting more updates: $error');
|
|
});
|
|
|
|
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;
|
|
}
|
|
notifyListeners();
|
|
}, onError: (error) {
|
|
_logger.info('Error getting more updates: $error');
|
|
});
|
|
|
|
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();
|
|
}
|
|
|
|
_pm.clear();
|
|
_notifications.clear();
|
|
return updateNotifications();
|
|
}
|
|
|
|
List<UserNotification> buildUnreadMessageNotifications() {
|
|
final myId = getIt<AccountsService>().currentProfile.userId;
|
|
final result =
|
|
getIt<DirectMessageService>().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;
|
|
}
|
|
}
|