Refactor notifications manager so updates and sorting happen in async operations

codemagic-setup
Hank Grabowski 2023-04-28 21:06:21 -04:00
rodzic 3c944378ae
commit c2ab53b65e
3 zmienionych plików z 78 dodań i 68 usunięć

Wyświetl plik

@ -7,7 +7,7 @@ import 'paged_response.dart';
import 'paging_data.dart';
class PagesManager<TResult, TID> {
final _pages = <PagedResponse<List<TID>>>[];
final _pages = <PagedResponse<bool>>[];
final List<TID> Function(TResult) idMapper;
final FutureResult<PagedResponse<TResult>, ExecError> Function(PagingData)
onRequest;
@ -15,7 +15,14 @@ class PagesManager<TResult, TID> {
PagesManager({
required this.idMapper,
required this.onRequest,
});
List<PagedResponse>? initialPages,
}) {
if (initialPages == null) {
return;
}
initialPages.map((op) => _fromResultPage(op)).forEach(_pages.add);
}
UnmodifiableListView<PagedResponse> get pages => UnmodifiableListView(_pages);
@ -23,14 +30,13 @@ class PagesManager<TResult, TID> {
_pages.clear();
}
Result<PagedResponse<List<TID>>, ExecError> pageFromId(TID id) {
for (final p in _pages) {
if (p.data.contains(id)) {
return Result.ok(p);
}
}
return buildErrorResult(
type: ErrorType.notFound, message: 'ID $id not in any page');
PagedResponse<bool> _fromResultPage(PagedResponse newPage) {
return PagedResponse(
true,
id: newPage.id,
previous: newPage.previous,
next: newPage.next,
);
}
FutureResult<PagedResponse<TResult>, ExecError> initialize(int limit) async {
@ -43,7 +49,7 @@ class PagesManager<TResult, TID> {
if (result.isSuccess) {
if (result.value.previous != null || result.value.next != null) {
final newPage = result.value.map((data) => idMapper(data));
_pages.add(newPage);
_pages.add(_fromResultPage(newPage));
}
}
return result;
@ -107,9 +113,9 @@ class PagesManager<TResult, TID> {
if (result.isSuccess && result.value.hasMorePages) {
final newPage = result.value.map((data) => idMapper(data));
if (asPrevious) {
_pages.insert(currentIndex, newPage);
_pages.insert(currentIndex, _fromResultPage(newPage));
} else {
_pages.insert(currentIndex + 1, newPage);
_pages.insert(currentIndex + 1, _fromResultPage(newPage));
}
}
return result;

Wyświetl plik

@ -20,7 +20,12 @@ import 'network_status_service.dart';
class NotificationsManager extends ChangeNotifier {
static final _logger = Logger('NotificationManager');
final _notifications = <String, UserNotification>{};
final dms = <UserNotification>[];
final connectionRequests = <UserNotification>[];
final unread = <UserNotification>[];
final read = <UserNotification>[];
final _pm = PagesManager<List<UserNotification>, String>(
idMapper: (nn) => nn.map((n) => n.id).toList(),
onRequest: _clientGetNotificationsRequest,
@ -28,43 +33,20 @@ class NotificationsManager extends ChangeNotifier {
var _firstLoad = true;
List<UserNotification> get notifications {
if (_notifications.isEmpty && _firstLoad) {
if (_firstLoad) {
updateNotifications();
_firstLoad = false;
}
final dms = <UserNotification>[];
final connectionRequests = <UserNotification>[];
final unread = <UserNotification>[];
final read = <UserNotification>[];
for (final n in _notifications.values) {
if (n.dismissed) {
read.add(n);
continue;
}
switch (n.type) {
case NotificationType.direct_message:
dms.add(n);
break;
case NotificationType.follow:
case NotificationType.follow_request:
connectionRequests.add(n);
break;
default:
unread.add(n);
}
}
dms.sort();
connectionRequests.sort();
unread.sort();
read.sort();
return [...connectionRequests, ...dms, ...unread, ...read];
}
void clear() {
_notifications.clear();
_pm.clear();
dms.clear();
connectionRequests.clear();
unread.clear();
read.clear();
_firstLoad = true;
notifyListeners();
}
@ -166,34 +148,38 @@ class NotificationsManager extends ChangeNotifier {
.andThenSuccessAsync((fm) async => fm.update());
}
_notifications.clear();
final notifications = <String, UserNotification>{};
notificationsFromRefresh.removeWhere((n) =>
n.type == NotificationType.direct_message ||
(useActualRequests && n.type == NotificationType.follow_request));
for (final n in notificationsFromRefresh) {
_notifications[n.id] = n;
notifications[n.id] = n;
}
getIt<NetworkStatusService>().finishNotificationUpdate();
for (final n in buildUnreadMessageNotifications(useActualRequests)) {
_notifications[n.id] = n;
notifications[n.id] = n;
}
_processNewNotifications(notifications.values, clearAtStart: true);
notifyListeners();
return Result.ok(notifications);
return Result.ok(notifications.values.toList());
}
FutureResult<List<UserNotification>, ExecError>
loadNewerNotifications() async {
final notifications = <String, UserNotification>{};
final result = await _pm.previousFromBeginning();
result.match(onSuccess: (response) {
if (response.data.isEmpty) {
return;
}
for (final n in response.data) {
_notifications[n.id] = n;
notifications[n.id] = n;
}
_processNewNotifications(notifications.values);
notifyListeners();
}, onError: (error) {
_logger.info('Error getting more updates: $error');
@ -204,16 +190,18 @@ class NotificationsManager extends ChangeNotifier {
FutureResult<List<UserNotification>, ExecError>
loadOlderNotifications() async {
final notifications = <String, UserNotification>{};
final result = await _pm.nextFromEnd();
result.match(onSuccess: (response) {
for (final n in response.data) {
_notifications[n.id] = n;
notifications[n.id] = n;
}
notifyListeners();
}, onError: (error) {
_logger.info('Error getting more updates: $error');
});
_processNewNotifications(notifications.values);
return result.mapValue((response) => response.data);
}
@ -237,8 +225,6 @@ class NotificationsManager extends ChangeNotifier {
return result.errorCast();
}
_pm.clear();
_notifications.clear();
return updateNotifications();
}
@ -277,6 +263,42 @@ class NotificationsManager extends ChangeNotifier {
return [...dmsResult, ...followRequestResult];
}
Future<void> _processNewNotifications(
Iterable<UserNotification> notifications, {
bool clearAtStart = false,
}) async {
if (clearAtStart) {
dms.clear();
connectionRequests.clear();
unread.clear();
read.clear();
}
for (final n in notifications) {
if (n.dismissed) {
read.add(n);
continue;
}
switch (n.type) {
case NotificationType.direct_message:
dms.add(n);
break;
case NotificationType.follow:
case NotificationType.follow_request:
connectionRequests.add(n);
break;
default:
unread.add(n);
}
}
dms.sort();
connectionRequests.sort();
unread.sort();
read.sort();
}
static FutureResult<PagedResponse<List<UserNotification>>, ExecError>
_clientGetNotificationsRequest(PagingData page) async {
final result =

Wyświetl plik

@ -60,24 +60,6 @@ void main() async {
expect(numbers, equals(elements.map((e) => e.id)));
_checkPagesOrder(pm.pages);
});
test('Can find page by index', () async {
final pm = _buildPagesManager();
final initial = await pm.initialize(10);
final next = await pm.nextWithResult(initial.value);
final previous = await pm.previousWithResult(initial.value);
final initialFromQuery = pm.pageFromId(initial.value.data.first.id);
expect(initialFromQuery.value.id, equals(initial.value.id));
final nextFromQuery = pm.pageFromId(next.value.data.first.id);
expect(nextFromQuery.value.id, equals(next.value.id));
final previousFromQuery = pm.pageFromId(previous.value.data.first.id);
expect(previousFromQuery.value.id, equals(previous.value.id));
expect(pm.pageFromId(elements.last.id).isFailure, true);
});
}
void _checkPagesOrder(UnmodifiableListView<PagedResponse> pages) {