Wire profile into services when created for each profile rather than looking up "active profile" each time

This commit is contained in:
Hank Grabowski 2023-04-28 21:28:43 -04:00
parent 330a19692f
commit ae5916cc84
8 changed files with 109 additions and 106 deletions

View file

@ -25,7 +25,6 @@ import 'services/follow_requests_manager.dart';
import 'services/gallery_service.dart';
import 'services/hashtag_service.dart';
import 'services/interactions_manager.dart';
import 'services/media_upload_attachment_helper.dart';
import 'services/network_status_service.dart';
import 'services/notifications_manager.dart';
import 'services/persistent_info_service.dart';
@ -95,13 +94,14 @@ Future<void> dependencyInjectionInitialization() async {
));
getIt.registerSingleton<ActiveProfileSelector<GalleryService>>(
ActiveProfileSelector((p) => GalleryService())
ActiveProfileSelector((p) => GalleryService(p))
..subscribeToProfileSwaps());
getIt.registerSingleton<ActiveProfileSelector<EntryManagerService>>(
ActiveProfileSelector((p) => EntryManagerService())
ActiveProfileSelector((p) => EntryManagerService(p))
..subscribeToProfileSwaps());
getIt.registerSingleton<ActiveProfileSelector<TimelineManager>>(
ActiveProfileSelector((p) => TimelineManager(
p,
getIt<ActiveProfileSelector<IGroupsRepo>>().getForProfile(p).value,
getIt<ActiveProfileSelector<EntryManagerService>>()
.getForProfile(p)
@ -109,21 +109,18 @@ Future<void> dependencyInjectionInitialization() async {
))
..subscribeToProfileSwaps());
getIt.registerSingleton<ActiveProfileSelector<NotificationsManager>>(
ActiveProfileSelector((_) => NotificationsManager())
ActiveProfileSelector((p) => NotificationsManager(p))
..subscribeToProfileSwaps());
getIt.registerSingleton<ActiveProfileSelector<FollowRequestsManager>>(
ActiveProfileSelector((_) => FollowRequestsManager())
ActiveProfileSelector((p) => FollowRequestsManager(p))
..subscribeToProfileSwaps());
getIt.registerSingleton<ActiveProfileSelector<DirectMessageService>>(
ActiveProfileSelector((p) => DirectMessageService())
ActiveProfileSelector((p) => DirectMessageService(p))
..subscribeToProfileSwaps());
getIt.registerSingleton<ActiveProfileSelector<InteractionsManager>>(
ActiveProfileSelector((p) => InteractionsManager())
ActiveProfileSelector((p) => InteractionsManager(p))
..subscribeToProfileSwaps());
getIt.registerLazySingleton<MediaUploadAttachmentHelper>(
() => MediaUploadAttachmentHelper());
setupUpdateTimers();
}

View file

@ -1,23 +1,26 @@
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:relatica/models/auth/oauth_credentials.dart';
import 'package:relatica/services/feature_version_checker.dart';
import 'package:result_monad/result_monad.dart';
import '../friendica_client/friendica_client.dart';
import '../friendica_client/paging_data.dart';
import '../globals.dart';
import '../models/auth/oauth_credentials.dart';
import '../models/auth/profile.dart';
import '../models/connection.dart';
import '../models/direct_message.dart';
import '../models/direct_message_thread.dart';
import '../models/exec_error.dart';
import 'auth_service.dart';
import 'feature_version_checker.dart';
class DirectMessageService extends ChangeNotifier {
static final _logger = Logger('$DirectMessageService');
final _threads = <String, DirectMessageThread>{};
final Profile profile;
var _firstLoading = true;
DirectMessageService(this.profile);
void clear() {
_threads.clear();
_firstLoading = true;
@ -47,9 +50,7 @@ class DirectMessageService extends ChangeNotifier {
}
Future<void> updateThreads() async {
await DirectMessagingClient(getIt<AccountsService>().currentProfile)
.getDirectMessages(PagingData())
.match(
await DirectMessagingClient(profile).getDirectMessages(PagingData()).match(
onSuccess: (update) {
final newThreads = DirectMessageThread.createThreads(update);
_threads.clear();
@ -71,7 +72,6 @@ class DirectMessageService extends ChangeNotifier {
FutureResult<DirectMessage, ExecError> newThread(
Connection receiver, String text) async {
final profile = getIt<AccountsService>().currentProfile;
if (profile.credentials is OAuthCredentials) {
final result = getIt<FriendicaVersionChecker>()
.canUseFeatureResult(RelaticaFeatures.directMessageCreation);
@ -118,7 +118,6 @@ class DirectMessageService extends ChangeNotifier {
);
}
final profile = getIt<AccountsService>().currentProfile;
if (profile.credentials is OAuthCredentials) {
final result = getIt<FriendicaVersionChecker>()
.canUseFeatureResult(RelaticaFeatures.directMessageCreation);
@ -154,9 +153,7 @@ class DirectMessageService extends ChangeNotifier {
return;
}
await DirectMessagingClient(getIt<AccountsService>().currentProfile)
.markDirectMessageRead(m)
.match(
await DirectMessagingClient(profile).markDirectMessageRead(m).match(
onSuccess: (update) {
thread.messages.removeAt(oldIndex);
thread.messages.insert(oldIndex, update);

View file

@ -7,13 +7,13 @@ import '../friendica_client/friendica_client.dart';
import '../friendica_client/paging_data.dart';
import '../globals.dart';
import '../models/TimelineIdentifiers.dart';
import '../models/auth/profile.dart';
import '../models/entry_tree_item.dart';
import '../models/exec_error.dart';
import '../models/image_entry.dart';
import '../models/media_attachment_uploads/new_entry_media_items.dart';
import '../models/timeline_entry.dart';
import '../models/visibility.dart';
import 'auth_service.dart';
import 'feature_version_checker.dart';
import 'media_upload_attachment_helper.dart';
@ -22,6 +22,9 @@ class EntryManagerService extends ChangeNotifier {
final _entries = <String, TimelineEntry>{};
final _parentPostIds = <String, String>{};
final _postNodes = <String, _Node>{};
final Profile profile;
EntryManagerService(this.profile);
void clear() {
_entries.clear();
@ -46,7 +49,6 @@ class EntryManagerService extends ChangeNotifier {
Result<EntryTreeItem, ExecError> getPostTreeEntryBy(String id) {
_logger.finest('Getting post: $id');
final idForCall = mapInteractionId(id);
final currentId = getIt<AccountsService>().currentProfile.userId;
final postNode = _getPostRootNode(idForCall);
if (postNode == null) {
return Result.error(ExecError(
@ -55,7 +57,7 @@ class EntryManagerService extends ChangeNotifier {
));
}
return Result.ok(_nodeToTreeItem(postNode, currentId));
return Result.ok(_nodeToTreeItem(postNode, profile.userId));
}
Result<TimelineEntry, ExecError> getEntryById(String id) {
@ -71,8 +73,7 @@ class EntryManagerService extends ChangeNotifier {
FutureResult<bool, ExecError> deleteEntryById(String id) async {
_logger.finest('Delete entry: $id');
final result = await StatusesClient(getIt<AccountsService>().currentProfile)
.deleteEntryById(id);
final result = await StatusesClient(profile).deleteEntryById(id);
if (result.isFailure) {
return result.errorCast();
}
@ -116,8 +117,7 @@ class EntryManagerService extends ChangeNotifier {
item.localFilePath,
).andThenAsync(
(imageBytes) async =>
await RemoteFileClient(getIt<AccountsService>().currentProfile)
.uploadFileAsAttachment(
await RemoteFileClient(profile).uploadFileAsAttachment(
bytes: imageBytes,
album: mediaItems.albumName,
description: item.description,
@ -134,7 +134,7 @@ class EntryManagerService extends ChangeNotifier {
}
}
final result = await StatusesClient(getIt<AccountsService>().currentProfile)
final result = await StatusesClient(profile)
.createNewStatus(
text: text,
spoilerText: spoilerText,
@ -142,8 +142,7 @@ class EntryManagerService extends ChangeNotifier {
mediaIds: mediaIds,
visibility: visibility)
.andThenSuccessAsync((item) async {
await processNewItems(
[item], getIt<AccountsService>().currentProfile.username, null);
await processNewItems([item], profile.username, null);
return item;
}).andThenSuccessAsync((item) async {
if (inReplyToId.isNotEmpty) {
@ -208,14 +207,13 @@ class EntryManagerService extends ChangeNotifier {
await MediaUploadAttachmentHelper.getUploadableImageBytes(
item.localFilePath,
).andThenAsync(
(imageBytes) async =>
await RemoteFileClient(getIt<AccountsService>().currentProfile)
.uploadFileAsAttachment(
bytes: imageBytes,
album: mediaItems.albumName,
description: item.description,
fileName: filename,
visibility: newMediaItemVisibility),
(imageBytes) async => await RemoteFileClient(profile)
.uploadFileAsAttachment(
bytes: imageBytes,
album: mediaItems.albumName,
description: item.description,
fileName: filename,
visibility: newMediaItemVisibility),
);
if (uploadResult.isSuccess) {
mediaIds.add(uploadResult.value.scales.first.id);
@ -226,15 +224,14 @@ class EntryManagerService extends ChangeNotifier {
}
}
final result = await StatusesClient(getIt<AccountsService>().currentProfile)
final result = await StatusesClient(profile)
.editStatus(
id: idForCall,
text: text,
spoilerText: spoilerText,
mediaIds: mediaIds)
.andThenSuccessAsync((item) async {
await processNewItems(
[item], getIt<AccountsService>().currentProfile.username, null);
await processNewItems([item], profile.username, null);
return item;
}).andThenSuccessAsync((item) async {
final inReplyToId = item.parentId;
@ -267,7 +264,7 @@ class EntryManagerService extends ChangeNotifier {
FutureResult<List<EntryTreeItem>, ExecError> updateTimeline(
TimelineIdentifiers type, int maxId, int sinceId) async {
_logger.fine(() => 'Updating timeline');
final client = TimelineClient(getIt<AccountsService>().currentProfile);
final client = TimelineClient(profile);
final itemsResult = await client.getTimeline(
type: type,
page: PagingData(
@ -282,7 +279,7 @@ class EntryManagerService extends ChangeNotifier {
itemsResult.value.sort((t1, t2) => t1.id.compareTo(t2.id));
final updatedPosts =
await processNewItems(itemsResult.value, client.profile.userId, client);
await processNewItems(itemsResult.value, profile.userId, client);
_logger.finest(() {
final postCount = _entries.values.where((e) => e.parentId.isEmpty).length;
final commentCount = _entries.length - postCount;
@ -323,7 +320,7 @@ class EntryManagerService extends ChangeNotifier {
}
for (final o in orphans) {
await StatusesClient(getIt<AccountsService>().currentProfile)
await StatusesClient(profile)
.getPostOrComment(o.id, fullContext: true)
.andThenSuccessAsync((items) async {
final parentPostId = items.firstWhere((e) => e.parentId.isEmpty).id;
@ -417,13 +414,13 @@ class EntryManagerService extends ChangeNotifier {
FutureResult<EntryTreeItem, ExecError> refreshStatusChain(String id) async {
_logger.finest('Refreshing post: $id');
final client = StatusesClient(getIt<AccountsService>().currentProfile);
final client = StatusesClient(profile);
final idForCall = mapInteractionId(id);
var parentId = '';
final result = await client
.getPostOrComment(idForCall, fullContext: false)
.withResult((entries) =>
parentId = entries.isEmpty ? '' : entries.first.parentId ?? '')
parentId = entries.isEmpty ? '' : entries.first.parentId)
.andThenAsync((rootItems) async => await client
.getPostOrComment(idForCall, fullContext: true)
.andThenSuccessAsync(
@ -439,7 +436,7 @@ class EntryManagerService extends ChangeNotifier {
await client
.getPostOrComment(parentIdForCall, fullContext: false)
.withResult((entries) =>
parentId = entries.isEmpty ? '' : entries.first.parentId ?? '')
parentId = entries.isEmpty ? '' : entries.first.parentId)
.andThenAsync((rootItems) async => await client
.getPostOrComment(idForCall, fullContext: true)
.transformAsync(
@ -464,7 +461,7 @@ class EntryManagerService extends ChangeNotifier {
FutureResult<EntryTreeItem, ExecError> resharePost(String id) async {
_logger.finest('Resharing post: $id');
final client = StatusesClient(getIt<AccountsService>().currentProfile);
final client = StatusesClient(profile);
final idForCall = mapInteractionId(id);
final result =
await client.resharePost(idForCall).andThenSuccessAsync((item) async {
@ -487,7 +484,7 @@ class EntryManagerService extends ChangeNotifier {
FutureResult<bool, ExecError> unResharePost(String id) async {
_logger.finest('Unresharing post: $id');
final client = StatusesClient(getIt<AccountsService>().currentProfile);
final client = StatusesClient(profile);
final idForCall = mapInteractionId(id);
final result =
await client.unResharePost(idForCall).andThenSuccessAsync((item) async {
@ -506,7 +503,6 @@ class EntryManagerService extends ChangeNotifier {
FutureResult<EntryTreeItem, ExecError> toggleFavorited(
String id, bool newStatus) async {
final profile = getIt<AccountsService>().currentProfile;
final interactionClient = InteractionsClient(profile);
final postsClient = StatusesClient(profile);
final idForCall = mapInteractionId(id);

View file

@ -6,15 +6,17 @@ import 'package:result_monad/result_monad.dart';
import '../friendica_client/friendica_client.dart';
import '../friendica_client/paged_response.dart';
import '../friendica_client/paging_data.dart';
import '../globals.dart';
import '../models/auth/profile.dart';
import '../models/exec_error.dart';
import '../models/follow_request.dart';
import 'auth_service.dart';
class FollowRequestsManager extends ChangeNotifier {
static const maxIterations = 20;
final Profile profile;
final _requests = <String, FollowRequest>{};
FollowRequestsManager(this.profile);
List<FollowRequest> get requests => UnmodifiableListView(_requests.values);
void clear() {
@ -56,9 +58,7 @@ class FollowRequestsManager extends ChangeNotifier {
if (page == null) {
return buildErrorResult(type: ErrorType.rangeError);
}
final result =
await RelationshipsClient(getIt<AccountsService>().currentProfile)
.getFollowRequests(page);
final result = await RelationshipsClient(profile).getFollowRequests(page);
return result;
}

View file

@ -3,11 +3,10 @@ import 'package:result_monad/result_monad.dart';
import '../friendica_client/friendica_client.dart';
import '../friendica_client/paging_data.dart';
import '../globals.dart';
import '../models/auth/profile.dart';
import '../models/exec_error.dart';
import '../models/gallery_data.dart';
import '../models/image_entry.dart';
import 'auth_service.dart';
class GalleryService extends ChangeNotifier {
static const IMAGES_PER_PAGE = 50;
@ -16,6 +15,10 @@ class GalleryService extends ChangeNotifier {
final _images = <String, Set<ImageEntry>>{};
var _loaded = false;
final Profile profile;
GalleryService(this.profile);
bool get loaded => _loaded;
void clear() {
@ -48,8 +51,7 @@ class GalleryService extends ChangeNotifier {
}
FutureResult<List<GalleryData>, ExecError> updateGalleries() async {
final result = await GalleryClient(getIt<AccountsService>().currentProfile)
.getGalleryData();
final result = await GalleryClient(profile).getGalleryData();
if (result.isFailure) {
return result.errorCast();
}
@ -99,8 +101,7 @@ class GalleryService extends ChangeNotifier {
final pagesToUse = nextPageOnly ? [pages.last] : pages;
for (final page in pagesToUse) {
final result =
await GalleryClient(getIt<AccountsService>().currentProfile)
.getGalleryImages(galleryName, page);
await GalleryClient(profile).getGalleryImages(galleryName, page);
if (result.isFailure) {
return result.errorCast();
}

View file

@ -3,16 +3,20 @@ import 'package:result_monad/result_monad.dart';
import '../friendica_client/friendica_client.dart';
import '../globals.dart';
import '../models/auth/profile.dart';
import '../models/connection.dart';
import '../models/exec_error.dart';
import '../utils/active_profile_selector.dart';
import 'auth_service.dart';
import 'entry_manager_service.dart';
class InteractionsManager extends ChangeNotifier {
final _likesByStatusId = <String, List<Connection>>{};
final _resharesByStatusId = <String, List<Connection>>{};
final Profile profile;
InteractionsManager(this.profile);
void clear() {
_likesByStatusId.clear();
_resharesByStatusId.clear();
@ -40,9 +44,7 @@ class InteractionsManager extends ChangeNotifier {
FutureResult<List<Connection>, ExecError> updateLikesForStatus(
String statusId) async {
final idForCall = _mapStatusId(statusId);
final likesResult =
await InteractionsClient(getIt<AccountsService>().currentProfile)
.getLikes(idForCall);
final likesResult = await InteractionsClient(profile).getLikes(idForCall);
if (likesResult.isSuccess) {
_likesByStatusId[statusId] = likesResult.value;
notifyListeners();
@ -54,8 +56,7 @@ class InteractionsManager extends ChangeNotifier {
String statusId) async {
final idForCall = _mapStatusId(statusId);
final resharesResult =
await InteractionsClient(getIt<AccountsService>().currentProfile)
.getReshares(idForCall);
await InteractionsClient(profile).getReshares(idForCall);
if (resharesResult.isSuccess) {
_resharesByStatusId[statusId] = resharesResult.value;
notifyListeners();
@ -65,7 +66,7 @@ class InteractionsManager extends ChangeNotifier {
String _mapStatusId(String statusId) {
return getIt<ActiveProfileSelector<EntryManagerService>>()
.activeEntry
.getForProfile(profile)
.transform((m) => m.mapInteractionId(statusId))
.getValueOrElse(() => statusId);
}

View file

@ -8,6 +8,7 @@ import '../friendica_client/paged_response.dart';
import '../friendica_client/pages_manager.dart';
import '../friendica_client/paging_data.dart';
import '../globals.dart';
import '../models/auth/profile.dart';
import '../models/exec_error.dart';
import '../models/user_notification.dart';
import '../serializers/mastodon/follow_request_mastodon_extensions.dart';
@ -20,16 +21,20 @@ import 'network_status_service.dart';
class NotificationsManager extends ChangeNotifier {
static final _logger = Logger('NotificationManager');
late final PagesManager<List<UserNotification>, String> _pm;
final Profile profile;
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,
);
NotificationsManager(this.profile) {
_pm = PagesManager<List<UserNotification>, String>(
idMapper: (nn) => nn.map((n) => n.id).toList(),
onRequest: (pd) async =>
await _clientGetNotificationsRequest(profile, pd));
}
var _firstLoad = true;
List<UserNotification> get notifications {
@ -68,13 +73,19 @@ class NotificationsManager extends ChangeNotifier {
PagingData? pd;
bool initializedFirstPage = false;
if (page.next != null) {
final response = await _clientGetNotificationsRequest(page.next!);
final response = await _clientGetNotificationsRequest(
profile,
page.next!,
);
response.match(
onSuccess: (response) => pd = response.previous,
onError: (error) =>
_logger.severe('Error getting previous page: $error'));
if (pd != null) {
final response = await _clientGetNotificationsRequest(pd!);
final response = await _clientGetNotificationsRequest(
profile,
pd!,
);
response.match(
onSuccess: (response) {
initializedFirstPage = true;
@ -84,13 +95,16 @@ class NotificationsManager extends ChangeNotifier {
_logger.severe('Error getting previous page: $error'));
} else if (pd == null && page.previous != null) {
final response = await _clientGetNotificationsRequest(
page.previous!)
.andThenAsync((previousData) async => previousData.next !=
null
? await _clientGetNotificationsRequest(previousData.next!)
: buildErrorResult(
type: ErrorType.rangeError,
message: 'No "next" page from previous data either'));
profile,
page.previous!,
).andThenAsync((previousData) async => previousData.next != null
? await _clientGetNotificationsRequest(
profile,
previousData.next!,
)
: buildErrorResult(
type: ErrorType.rangeError,
message: 'No "next" page from previous data either'));
response.match(
onSuccess: (response) {
initializedFirstPage = true;
@ -125,7 +139,10 @@ class NotificationsManager extends ChangeNotifier {
continue;
}
final response = await _clientGetNotificationsRequest(page.next!);
final response = await _clientGetNotificationsRequest(
profile,
page.next!,
);
response.match(
onSuccess: (response) =>
notificationsFromRefresh.addAll(response.data),
@ -136,16 +153,16 @@ class NotificationsManager extends ChangeNotifier {
getIt<NetworkStatusService>().startNotificationUpdate();
await getIt<ActiveProfileSelector<DirectMessageService>>()
.activeEntry
.andThenSuccessAsync((dms) async => await dms.updateThreads());
.getForProfile(profile)
.transformAsync((dms) async => await dms.updateThreads());
final useActualRequests = getIt<FriendicaVersionChecker>()
.canUseFeature(RelaticaFeatures.usingActualFollowRequests);
if (useActualRequests) {
await getIt<ActiveProfileSelector<FollowRequestsManager>>()
.activeEntry
.andThenSuccessAsync((fm) async => fm.update());
.getForProfile(profile)
.transformAsync((fm) async => fm.update());
}
final notifications = <String, UserNotification>{};
@ -207,8 +224,7 @@ class NotificationsManager extends ChangeNotifier {
FutureResult<bool, ExecError> markSeen(UserNotification notification) async {
final result =
await NotificationsClient(getIt<AccountsService>().currentProfile)
.clearNotification(notification);
await NotificationsClient(profile).clearNotification(notification);
if (result.isSuccess) {
notifyListeners();
}
@ -230,10 +246,10 @@ class NotificationsManager extends ChangeNotifier {
List<UserNotification> buildUnreadMessageNotifications(
bool useActualRequests) {
final myId = getIt<AccountsService>().currentProfile.userId;
final myId = profile.userId;
final dmsResult = getIt<ActiveProfileSelector<DirectMessageService>>()
.activeEntry
.andThenSuccess((d) => d.getThreads(unreadyOnly: true).map((t) {
.getForProfile(profile)
.transform((d) => d.getThreads(unreadyOnly: true).map((t) {
final fromAccount =
t.participants.firstWhere((p) => p.id != myId);
final latestMessage = t.messages
@ -255,8 +271,8 @@ class NotificationsManager extends ChangeNotifier {
final followRequestResult = !useActualRequests
? []
: getIt<ActiveProfileSelector<FollowRequestsManager>>()
.activeEntry
.andThenSuccess(
.getForProfile(profile)
.transform(
(fm) => fm.requests.map((r) => r.toUserNotification()).toList())
.getValueOrElse(() => []);
@ -300,10 +316,7 @@ class NotificationsManager extends ChangeNotifier {
}
static FutureResult<PagedResponse<List<UserNotification>>, ExecError>
_clientGetNotificationsRequest(PagingData page) async {
final result =
await NotificationsClient(getIt<AccountsService>().currentProfile)
.getNotifications(page);
return result;
_clientGetNotificationsRequest(Profile profile, PagingData page) async {
return NotificationsClient(profile).getNotifications(page);
}
}

View file

@ -4,8 +4,8 @@ import 'package:result_monad/result_monad.dart';
import '../data/interfaces/groups_repo.intf.dart';
import '../friendica_client/friendica_client.dart';
import '../globals.dart';
import '../models/TimelineIdentifiers.dart';
import '../models/auth/profile.dart';
import '../models/entry_tree_item.dart';
import '../models/exec_error.dart';
import '../models/group_data.dart';
@ -14,7 +14,6 @@ import '../models/media_attachment_uploads/new_entry_media_items.dart';
import '../models/timeline.dart';
import '../models/timeline_entry.dart';
import '../models/visibility.dart';
import 'auth_service.dart';
import 'entry_manager_service.dart';
enum TimelineRefreshType {
@ -29,10 +28,11 @@ class TimelineManager extends ChangeNotifier {
final IGroupsRepo groupsRepo;
final EntryManagerService entryManagerService;
var groupsNotInitialized = true;
final Profile profile;
final cachedTimelines = <TimelineIdentifiers, Timeline>{};
TimelineManager(this.groupsRepo, this.entryManagerService);
TimelineManager(this.profile, this.groupsRepo, this.entryManagerService);
void clear() {
groupsNotInitialized = true;
@ -54,9 +54,7 @@ class TimelineManager extends ChangeNotifier {
Future<void> _refreshGroupData() async {
_logger.finest('Refreshing member group data ');
await GroupsClient(getIt<AccountsService>().currentProfile)
.getGroups()
.match(
await GroupsClient(profile).getGroups().match(
onSuccess: (groups) {
groupsRepo.addAllGroups(groups);
notifyListeners();