import 'dart:collection'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:result_monad/result_monad.dart'; import '../data/interfaces/connections_repo_intf.dart'; import '../data/interfaces/groups_repo.intf.dart'; import '../friendica_client/paging_data.dart'; import '../globals.dart'; import '../models/connection.dart'; import '../models/exec_error.dart'; import '../models/group_data.dart'; import 'auth_service.dart'; class ConnectionsManager extends ChangeNotifier { static final _logger = Logger('$ConnectionsManager'); late final IConnectionsRepo conRepo; late final IGroupsRepo groupsRepo; ConnectionsManager() { conRepo = getIt(); groupsRepo = getIt(); } bool addConnection(Connection connection) { return conRepo.addConnection(connection); } List getKnownUsersByName(String name) { return conRepo.getKnownUsersByName(name); } bool updateConnection(Connection connection) { return conRepo.updateConnection(connection); } bool addAllConnections(Iterable newConnections) { return conRepo.addAllConnections(newConnections); } Future acceptFollowRequest(Connection connection) async { _logger.finest( 'Attempting to accept follow request ${connection.name}: ${connection.status}'); await getIt() .currentClient .andThenAsync((client) => client.acceptFollow(connection)) .match( onSuccess: (update) { _logger .finest('Successfully followed ${update.name}: ${update.status}'); updateConnection(update); notifyListeners(); }, onError: (error) { _logger.severe('Error following ${connection.name}'); }, ); } Future rejectFollowRequest(Connection connection) async { _logger.finest( 'Attempting to accept follow request ${connection.name}: ${connection.status}'); await getIt() .currentClient .andThenAsync((client) => client.rejectFollow(connection)) .match( onSuccess: (update) { _logger .finest('Successfully followed ${update.name}: ${update.status}'); updateConnection(update); notifyListeners(); }, onError: (error) { _logger.severe('Error following ${connection.name}'); }, ); } Future ignoreFollowRequest(Connection connection) async { _logger.finest( 'Attempting to accept follow request ${connection.name}: ${connection.status}'); await getIt() .currentClient .andThenAsync((client) => client.ignoreFollow(connection)) .match( onSuccess: (update) { _logger .finest('Successfully followed ${update.name}: ${update.status}'); updateConnection(update); notifyListeners(); }, onError: (error) { _logger.severe('Error following ${connection.name}'); }, ); } Future follow(Connection connection) async { _logger.finest( 'Attempting to follow ${connection.name}: ${connection.status}'); await getIt() .currentClient .andThenAsync((client) => client.followConnection(connection)) .match( onSuccess: (update) { _logger .finest('Successfully followed ${update.name}: ${update.status}'); updateConnection(update); notifyListeners(); }, onError: (error) { _logger.severe('Error following ${connection.name}'); }, ); } Future unfollow(Connection connection) async { _logger.finest( 'Attempting to unfollow ${connection.name}: ${connection.status}'); await getIt() .currentClient .andThenAsync((client) => client.unFollowConnection(connection)) .match( onSuccess: (update) { _logger .finest('Successfully unfollowed ${update.name}: ${update.status}'); updateConnection(update); notifyListeners(); }, onError: (error) { _logger.severe('Error following ${connection.name}'); }, ); } List getMyContacts() { return conRepo.getMyContacts(); } Future updateAllContacts() async { _logger.fine('Updating all contacts'); final clientResult = getIt().currentClient; if (clientResult.isFailure) { _logger.severe( 'Unable to update contacts due to client error: ${clientResult.error}'); return; } final client = clientResult.value; final results = {}; var moreResults = true; var maxId = -1; const limit = 200; var currentPage = PagingData(limit: limit); while (moreResults) { await client.getMyFollowers(currentPage).match(onSuccess: (followers) { for (final f in followers.data) { results[f.id] = f.copy(status: ConnectionStatus.theyFollowYou); int id = int.parse(f.id); maxId = max(maxId, id); } if (followers.next != null) { currentPage = followers.next!; } moreResults = followers.next != null; }, onError: (error) { _logger.severe('Error getting followers data: $error'); }); } moreResults = true; currentPage = PagingData(limit: limit); while (moreResults) { await client.getMyFollowing(currentPage).match(onSuccess: (following) { for (final f in following.data) { if (results.containsKey(f.id)) { results[f.id] = f.copy(status: ConnectionStatus.mutual); } else { results[f.id] = f.copy(status: ConnectionStatus.youFollowThem); } int id = int.parse(f.id); maxId = max(maxId, id); } if (following.next != null) { currentPage = following.next!; } moreResults = following.next != null; }, onError: (error) { _logger.severe('Error getting followers data: $error'); }); } addAllConnections(results.values); final myContacts = conRepo.getMyContacts().toList(); myContacts.sort((c1, c2) => c1.name.compareTo(c2.name)); _logger.fine('# Contacts:${myContacts.length}'); notifyListeners(); } List getMyGroups() { final myGroups = groupsRepo.getMyGroups(); if (myGroups.isEmpty) { _updateMyGroups(true); } return myGroups; } Result, ExecError> getGroupsForUser(String id) { final result = groupsRepo.getGroupsForUser(id); if (result.isSuccess) { return result; } if (result.isFailure && result.error.type != ErrorType.notFound) { return result; } _refreshGroupListData(id, true); return Result.ok(UnmodifiableListView([])); } FutureResult addUserToGroup( GroupData group, Connection connection) async { _logger.finest('Adding ${connection.name} to group: ${group.name}'); final result = await getIt().currentClient.andThenAsync( (client) => client.addConnectionToGroup(group, connection)); result.match( onSuccess: (_) => _refreshGroupListData(connection.id, true), onError: (error) { _logger .severe('Error adding ${connection.name} to group: ${group.name}'); }, ); return result.execErrorCast(); } FutureResult removeUserFromGroup( GroupData group, Connection connection) async { _logger.finest('Removing ${connection.name} from group: ${group.name}'); final result = await getIt().currentClient.andThenAsync( (client) => client.removeConnectionFromGroup(group, connection)); result.match( onSuccess: (_) => _refreshGroupListData(connection.id, true), onError: (error) { _logger.severe( 'Error removing ${connection.name} from group: ${group.name}'); }, ); return result.execErrorCast(); } Result getById(String id) { return conRepo.getById(id).andThenSuccess((c) { if (c.status == ConnectionStatus.unknown) { _refreshConnection(c, true); } return c; }).execErrorCast(); } Result getByName(String name) { return conRepo.getByName(name).andThenSuccess((c) { if (c.status == ConnectionStatus.unknown) { _refreshConnection(c, true); } return c; }).execErrorCast(); } Future fullRefresh(Connection connection) async { await _updateMyGroups(false); await _refreshGroupListData(connection.id, false); await _refreshConnection(connection, false); notifyListeners(); } Future _refreshGroupListData(String id, bool withNotification) async { _logger.finest('Refreshing member list data for Connection $id'); await getIt() .currentClient .andThenAsync((client) => client.getMemberGroupsForConnection(id)) .match( onSuccess: (groups) { groupsRepo.updateConnectionGroupData(id, groups); if (withNotification) { notifyListeners(); } }, onError: (error) { _logger.severe('Error getting list data for $id: $error'); }, ); } Future _refreshConnection( Connection connection, bool withNotification) async { _logger.finest('Refreshing connection data for ${connection.name}'); await getIt() .currentClient .andThenAsync((client) => client.getConnectionWithStatus(connection)) .match( onSuccess: (update) { updateConnection(update); if (withNotification) { notifyListeners(); } }, onError: (error) { _logger.severe('Error getting updates for ${connection.name}: $error'); }, ); } Future _updateMyGroups(bool withNotification) async { _logger.finest('Refreshing my groups list'); await getIt() .currentClient .andThenAsync((client) => client.getGroups()) .match( onSuccess: (groups) { _logger.finest('Got updated groups:${groups.map((e) => e.name)}'); groupsRepo.clearGroups(); groupsRepo.addAllGroups(groups); if (withNotification) { notifyListeners(); } }, onError: (error) { _logger.severe('Error getting my groups: $error'); }, ); } }