mirror of
https://gitlab.com/mysocialportal/relatica
synced 2024-10-18 21:43:31 +00:00
390 lines
12 KiB
Dart
390 lines
12 KiB
Dart
import 'dart:math';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:result_monad/result_monad.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');
|
|
final _connectionsById = <String, Connection>{};
|
|
final _connectionsByName = <String, Connection>{};
|
|
final _connectionsByProfileUrl = <Uri, Connection>{};
|
|
final _groupsForConnection = <String, List<GroupData>>{};
|
|
final _myGroups = <GroupData>{};
|
|
final _myContacts = <Connection>[];
|
|
var _myContactsInitialized = false;
|
|
|
|
int get length => _connectionsById.length;
|
|
|
|
void clearCaches() {
|
|
_connectionsById.clear();
|
|
_connectionsByName.clear();
|
|
_connectionsByProfileUrl.clear();
|
|
_groupsForConnection.clear();
|
|
_myGroups.clear();
|
|
_myContacts.clear();
|
|
}
|
|
|
|
bool addConnection(Connection connection) {
|
|
if (_connectionsById.containsKey(connection.id)) {
|
|
return false;
|
|
}
|
|
return updateConnection(connection);
|
|
}
|
|
|
|
List<Connection> getKnownUsersByName(String name) {
|
|
return _connectionsByName.values.where((it) {
|
|
final normalizedHandle = it.handle.toLowerCase();
|
|
final normalizedName = it.name.toLowerCase();
|
|
final normalizedQuery = name.toLowerCase();
|
|
return normalizedHandle.contains(normalizedQuery) ||
|
|
normalizedName.contains(normalizedQuery);
|
|
}).toList();
|
|
}
|
|
|
|
bool updateConnection(Connection connection) {
|
|
_connectionsById[connection.id] = connection;
|
|
_connectionsByName[connection.name] = connection;
|
|
_connectionsByProfileUrl[connection.profileUrl] = connection;
|
|
int index = _myContacts.indexWhere((c) => c.id == connection.id);
|
|
if (index >= 0) {
|
|
_myContacts.removeAt(index);
|
|
}
|
|
switch (connection.status) {
|
|
case ConnectionStatus.youFollowThem:
|
|
case ConnectionStatus.theyFollowYou:
|
|
case ConnectionStatus.mutual:
|
|
if (index > 0) {
|
|
_myContacts.insert(index, connection);
|
|
} else {
|
|
_myContacts.add(connection);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool addAllConnections(Iterable<Connection> newConnections) {
|
|
bool result = true;
|
|
|
|
for (final connection in newConnections) {
|
|
result &= addConnection(connection);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Future<void> acceptFollowRequest(Connection connection) async {
|
|
_logger.finest(
|
|
'Attempting to accept follow request ${connection.name}: ${connection.status}');
|
|
await getIt<AuthService>()
|
|
.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<void> rejectFollowRequest(Connection connection) async {
|
|
_logger.finest(
|
|
'Attempting to accept follow request ${connection.name}: ${connection.status}');
|
|
await getIt<AuthService>()
|
|
.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<void> ignoreFollowRequest(Connection connection) async {
|
|
_logger.finest(
|
|
'Attempting to accept follow request ${connection.name}: ${connection.status}');
|
|
await getIt<AuthService>()
|
|
.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<void> follow(Connection connection) async {
|
|
_logger.finest(
|
|
'Attempting to follow ${connection.name}: ${connection.status}');
|
|
await getIt<AuthService>()
|
|
.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<void> unfollow(Connection connection) async {
|
|
_logger.finest(
|
|
'Attempting to unfollow ${connection.name}: ${connection.status}');
|
|
await getIt<AuthService>()
|
|
.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<Connection> getMyContacts() {
|
|
if (!_myContactsInitialized) {
|
|
updateAllContacts();
|
|
_myContactsInitialized = true;
|
|
}
|
|
|
|
return _myContacts.toList(growable: false);
|
|
}
|
|
|
|
Future<void> updateAllContacts() async {
|
|
_logger.fine('Updating all contacts');
|
|
final clientResult = getIt<AuthService>().currentClient;
|
|
if (clientResult.isFailure) {
|
|
_logger.severe(
|
|
'Unable to update contacts due to client error: ${clientResult.error}');
|
|
return;
|
|
}
|
|
final client = clientResult.value;
|
|
final results = <String, Connection>{};
|
|
var moreResults = true;
|
|
var maxId = -1;
|
|
const limit = 1000;
|
|
while (moreResults) {
|
|
await client.getMyFollowers(sinceId: maxId, limit: limit).match(
|
|
onSuccess: (followers) {
|
|
for (final f in followers) {
|
|
results[f.id] = f.copy(status: ConnectionStatus.theyFollowYou);
|
|
int id = int.parse(f.id);
|
|
maxId = max(maxId, id);
|
|
}
|
|
moreResults = followers.length >= limit;
|
|
}, onError: (error) {
|
|
_logger.severe('Error getting followers data: $error');
|
|
});
|
|
}
|
|
|
|
moreResults = true;
|
|
maxId = -1;
|
|
while (moreResults) {
|
|
await client.getMyFollowing(sinceId: maxId, limit: limit).match(
|
|
onSuccess: (following) {
|
|
for (final f in following) {
|
|
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);
|
|
}
|
|
moreResults = following.length >= limit;
|
|
}, onError: (error) {
|
|
_logger.severe('Error getting followers data: $error');
|
|
});
|
|
}
|
|
|
|
_myContacts.clear();
|
|
_myContacts.addAll(results.values);
|
|
addAllConnections(results.values);
|
|
_myContacts.sort((c1, c2) => c1.name.compareTo(c2.name));
|
|
_logger.finest('# Contacts:${_myContacts.length}');
|
|
notifyListeners();
|
|
}
|
|
|
|
List<GroupData> getMyGroups() {
|
|
if (_myGroups.isNotEmpty) {
|
|
return _myGroups.toList(growable: false);
|
|
}
|
|
_updateMyGroups(true);
|
|
return [];
|
|
}
|
|
|
|
Result<List<GroupData>, ExecError> getGroupsForUser(String id) {
|
|
if (!_groupsForConnection.containsKey(id)) {
|
|
_refreshGroupListData(id, true);
|
|
return Result.ok([]);
|
|
}
|
|
|
|
return Result.ok(_groupsForConnection[id]!);
|
|
}
|
|
|
|
FutureResult<bool, ExecError> addUserToGroup(
|
|
GroupData group, Connection connection) async {
|
|
_logger.finest('Adding ${connection.name} to group: ${group.name}');
|
|
final result = await getIt<AuthService>().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<bool, ExecError> removeUserFromGroup(
|
|
GroupData group, Connection connection) async {
|
|
_logger.finest('Removing ${connection.name} from group: ${group.name}');
|
|
final result = await getIt<AuthService>().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<Connection, String> getById(String id) {
|
|
final result = _connectionsById[id];
|
|
if (result == null) {
|
|
return Result.error('$id not found');
|
|
}
|
|
if (result.status == ConnectionStatus.unknown) {
|
|
_refreshConnection(result, true);
|
|
}
|
|
return Result.ok(result);
|
|
}
|
|
|
|
Result<Connection, String> getByName(String name) {
|
|
final result = _connectionsByName[name];
|
|
if (result == null) {
|
|
Result.error('$name not found');
|
|
}
|
|
if (result!.status == ConnectionStatus.unknown) {
|
|
_refreshConnection(result, true);
|
|
}
|
|
return Result.ok(result);
|
|
}
|
|
|
|
Result<Connection, String> getByProfileUrl(Uri url) {
|
|
final result = _connectionsByProfileUrl[url];
|
|
if (result == null) {
|
|
Result.error('$url not found');
|
|
}
|
|
if (result!.status == ConnectionStatus.unknown) {
|
|
_refreshConnection(result, true);
|
|
}
|
|
return Result.ok(result);
|
|
}
|
|
|
|
Future<void> fullRefresh(Connection connection) async {
|
|
await _updateMyGroups(false);
|
|
await _refreshGroupListData(connection.id, false);
|
|
await _refreshConnection(connection, false);
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> _refreshGroupListData(String id, bool withNotification) async {
|
|
_logger.finest('Refreshing member list data for Connection $id');
|
|
await getIt<AuthService>()
|
|
.currentClient
|
|
.andThenAsync((client) => client.getMemberGroupsForConnection(id))
|
|
.match(
|
|
onSuccess: (lists) {
|
|
_groupsForConnection[id] = lists;
|
|
if (withNotification) {
|
|
notifyListeners();
|
|
}
|
|
},
|
|
onError: (error) {
|
|
_logger.severe('Error getting list data for $id: $error');
|
|
},
|
|
);
|
|
}
|
|
|
|
Future<void> _refreshConnection(
|
|
Connection connection, bool withNotification) async {
|
|
_logger.finest('Refreshing connection data for ${connection.name}');
|
|
await getIt<AuthService>()
|
|
.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<void> _updateMyGroups(bool withNotification) async {
|
|
_logger.finest('Refreshing my groups list');
|
|
await getIt<AuthService>()
|
|
.currentClient
|
|
.andThenAsync((client) => client.getGroups())
|
|
.match(
|
|
onSuccess: (groups) {
|
|
_logger.finest('Got updated groups:${groups.map((e) => e.name)}');
|
|
_myGroups.clear();
|
|
_myGroups.addAll(groups);
|
|
if (withNotification) {
|
|
notifyListeners();
|
|
}
|
|
},
|
|
onError: (error) {
|
|
_logger.severe('Error getting my groups: $error');
|
|
},
|
|
);
|
|
}
|
|
}
|