From b1e4dbf4ccde5558ecb901a1e70861c14b655aa5 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Mon, 16 Jan 2023 15:31:17 -0600 Subject: [PATCH] Initial Connections Repo implementation with memory version ported into MemoryConnectionsRepo --- .../interfaces/connections_repo_intf.dart | 61 +++++++ lib/data/memory/memory_connections_repo.dart | 149 ++++++++++++++++ lib/main.dart | 3 + lib/services/connections_manager.dart | 161 ++++++------------ pubspec.lock | 7 + pubspec.yaml | 1 + 6 files changed, 275 insertions(+), 107 deletions(-) create mode 100644 lib/data/interfaces/connections_repo_intf.dart create mode 100644 lib/data/memory/memory_connections_repo.dart diff --git a/lib/data/interfaces/connections_repo_intf.dart b/lib/data/interfaces/connections_repo_intf.dart new file mode 100644 index 0000000..74642ad --- /dev/null +++ b/lib/data/interfaces/connections_repo_intf.dart @@ -0,0 +1,61 @@ +import 'dart:collection'; + +import 'package:result_monad/result_monad.dart'; + +import '../../models/connection.dart'; +import '../../models/exec_error.dart'; +import '../../models/group_data.dart'; + +class IConnectionsRepo { + void addAllGroups(List groups) { + throw UnimplementedError(); + } + + bool updateConnectionGroupData(String id, List currentGroups) { + throw UnimplementedError(); + } + + void clearGroups() { + throw UnimplementedError(); + } + + UnmodifiableListView getMyGroups() { + throw UnimplementedError(); + } + + Result, ExecError> getGroupsForUser(String id) { + throw UnimplementedError(); + } + + bool addConnection(Connection connection) { + throw UnimplementedError(); + } + + bool addAllConnections(Iterable newConnections) { + throw UnimplementedError(); + } + + bool updateConnection(Connection connection) { + throw UnimplementedError(); + } + + Result getById(String id) { + throw UnimplementedError(); + } + + Result getByName(String name) { + throw UnimplementedError(); + } + + Result getByProfileUrl(String url) { + throw UnimplementedError(); + } + + UnmodifiableListView getMyContacts() { + throw UnimplementedError(); + } + + UnmodifiableListView getKnownUsersByName(String name) { + throw UnimplementedError(); + } +} diff --git a/lib/data/memory/memory_connections_repo.dart b/lib/data/memory/memory_connections_repo.dart new file mode 100644 index 0000000..56389ea --- /dev/null +++ b/lib/data/memory/memory_connections_repo.dart @@ -0,0 +1,149 @@ +import 'dart:collection'; + +import 'package:result_monad/result_monad.dart'; + +import '../../models/connection.dart'; +import '../../models/exec_error.dart'; +import '../../models/group_data.dart'; +import '../interfaces/connections_repo_intf.dart'; + +class MemoryConnectionsRepo implements IConnectionsRepo { + final _connectionsById = {}; + final _connectionsByName = {}; + final _connectionsByProfileUrl = {}; + final _groupsForConnection = >{}; + final _myGroups = {}; + final _myContacts = []; + + @override + bool addAllConnections(Iterable newConnections) { + bool result = true; + + for (final connection in newConnections) { + result &= addConnection(connection); + } + + return result; + } + + @override + bool addConnection(Connection connection) { + if (_connectionsById.containsKey(connection.id)) { + return false; + } + return updateConnection(connection); + } + + @override + UnmodifiableListView getKnownUsersByName(String name) { + return UnmodifiableListView(_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); + })); + } + + @override + bool updateConnection(Connection connection) { + _connectionsById[connection.id] = connection; + _connectionsByName[connection.name] = connection; + _connectionsByProfileUrl[connection.profileUrl.toString()] = 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; + } + + @override + UnmodifiableListView getMyContacts() { + return UnmodifiableListView(_myContacts); + } + + @override + Result, ExecError> getGroupsForUser(String id) { + if (!_groupsForConnection.containsKey(id)) { + return Result.error(ExecError( + type: ErrorType.notFound, + message: '$id not a known user ID', + )); + } + + return Result.ok(UnmodifiableListView(_groupsForConnection[id]!)); + } + + @override + Result getById(String id) { + final result = _connectionsById[id]; + if (result == null) { + return Result.error(ExecError( + type: ErrorType.notFound, + message: '$id not found', + )); + } + + return Result.ok(result); + } + + @override + Result getByName(String name) { + final result = _connectionsByName[name]; + if (result == null) { + return Result.error(ExecError( + type: ErrorType.notFound, + message: '$name not found', + )); + } + + return Result.ok(result); + } + + @override + UnmodifiableListView getMyGroups() { + return UnmodifiableListView(_myGroups); + } + + @override + Result getByProfileUrl(String url) { + final result = _connectionsByProfileUrl[url]; + if (result == null) { + return Result.error(ExecError( + type: ErrorType.notFound, + message: '$url not found', + )); + } + return Result.ok(result); + } + + @override + void clearGroups() { + _myGroups.clear(); + } + + @override + void addAllGroups(List groups) { + _myGroups.addAll(groups); + } + + @override + bool updateConnectionGroupData(String id, List currentGroups) { + _groupsForConnection[id] = currentGroups; + return true; + } +} diff --git a/lib/main.dart b/lib/main.dart index 3bc46fd..37eb08c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,8 @@ import 'package:multi_trigger_autocomplete/multi_trigger_autocomplete.dart'; import 'package:provider/provider.dart'; import 'package:result_monad/result_monad.dart'; +import 'data/interfaces/connections_repo_intf.dart'; +import 'data/memory/memory_connections_repo.dart'; import 'globals.dart'; import 'models/TimelineIdentifiers.dart'; import 'routes.dart'; @@ -44,6 +46,7 @@ void main() async { await service.initialize(); return service; }); + getIt.registerSingleton(MemoryConnectionsRepo()); getIt.registerLazySingleton(() => ConnectionsManager()); getIt.registerLazySingleton(() => HashtagService()); getIt.registerSingleton(galleryService); diff --git a/lib/services/connections_manager.dart b/lib/services/connections_manager.dart index 9e7ca19..e50c9d1 100644 --- a/lib/services/connections_manager.dart +++ b/lib/services/connections_manager.dart @@ -1,9 +1,11 @@ +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 '../globals.dart'; import '../models/connection.dart'; import '../models/exec_error.dart'; @@ -12,75 +14,26 @@ import 'auth_service.dart'; class ConnectionsManager extends ChangeNotifier { static final _logger = Logger('$ConnectionsManager'); - final _connectionsById = {}; - final _connectionsByName = {}; - final _connectionsByProfileUrl = {}; - final _groupsForConnection = >{}; - final _myGroups = {}; - final _myContacts = []; - var _myContactsInitialized = false; + late final IConnectionsRepo repo; - int get length => _connectionsById.length; - - void clearCaches() { - _connectionsById.clear(); - _connectionsByName.clear(); - _connectionsByProfileUrl.clear(); - _groupsForConnection.clear(); - _myGroups.clear(); - _myContacts.clear(); + ConnectionsManager() { + repo = getIt(); } bool addConnection(Connection connection) { - if (_connectionsById.containsKey(connection.id)) { - return false; - } - return updateConnection(connection); + return repo.addConnection(connection); } List 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(); + return repo.getKnownUsersByName(name); } 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; + return repo.updateConnection(connection); } bool addAllConnections(Iterable newConnections) { - bool result = true; - - for (final connection in newConnections) { - result &= addConnection(connection); - } - - return result; + return repo.addAllConnections(newConnections); } Future acceptFollowRequest(Connection connection) async { @@ -178,13 +131,8 @@ class ConnectionsManager extends ChangeNotifier { ); } - List getMyContacts() { - if (!_myContactsInitialized) { - updateAllContacts(); - _myContactsInitialized = true; - } - - return _myContacts.toList(growable: false); + UnmodifiableListView getMyContacts() { + return repo.getMyContacts(); } Future updateAllContacts() async { @@ -234,29 +182,34 @@ class ConnectionsManager extends ChangeNotifier { }); } - _myContacts.clear(); - _myContacts.addAll(results.values); addAllConnections(results.values); - _myContacts.sort((c1, c2) => c1.name.compareTo(c2.name)); - _logger.finest('# Contacts:${_myContacts.length}'); + final myContacts = repo.getMyContacts().toList(); + myContacts.sort((c1, c2) => c1.name.compareTo(c2.name)); + _logger.finest('# Contacts:${myContacts.length}'); notifyListeners(); } - List getMyGroups() { - if (_myGroups.isNotEmpty) { - return _myGroups.toList(growable: false); + UnmodifiableListView getMyGroups() { + final myGroups = repo.getMyGroups(); + if (myGroups.isEmpty) { + _updateMyGroups(true); } - _updateMyGroups(true); - return []; + + return myGroups; } Result, ExecError> getGroupsForUser(String id) { - if (!_groupsForConnection.containsKey(id)) { - _refreshGroupListData(id, true); - return Result.ok([]); + final result = repo.getGroupsForUser(id); + if (result.isSuccess) { + return result; } - return Result.ok(_groupsForConnection[id]!); + if (result.isFailure && result.error.type != ErrorType.notFound) { + return result; + } + + _refreshGroupListData(id, true); + return Result.ok(UnmodifiableListView([])); } FutureResult addUserToGroup( @@ -291,37 +244,31 @@ class ConnectionsManager extends ChangeNotifier { return result.execErrorCast(); } - Result 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 getById(String id) { + return repo.getById(id).andThenSuccess((c) { + if (c.status == ConnectionStatus.unknown) { + _refreshConnection(c, true); + } + return c; + }).execErrorCast(); } - Result 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 getByName(String name) { + return repo.getByName(name).andThenSuccess((c) { + if (c.status == ConnectionStatus.unknown) { + _refreshConnection(c, true); + } + return c; + }).execErrorCast(); } - Result 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); + Result getByProfileUrl(Uri url) { + return repo.getByProfileUrl(url.toString()).andThenSuccess((c) { + if (c.status == ConnectionStatus.unknown) { + _refreshConnection(c, true); + } + return c; + }).execErrorCast(); } Future fullRefresh(Connection connection) async { @@ -337,8 +284,8 @@ class ConnectionsManager extends ChangeNotifier { .currentClient .andThenAsync((client) => client.getMemberGroupsForConnection(id)) .match( - onSuccess: (lists) { - _groupsForConnection[id] = lists; + onSuccess: (groups) { + repo.updateConnectionGroupData(id, groups); if (withNotification) { notifyListeners(); } @@ -376,8 +323,8 @@ class ConnectionsManager extends ChangeNotifier { .match( onSuccess: (groups) { _logger.finest('Got updated groups:${groups.map((e) => e.name)}'); - _myGroups.clear(); - _myGroups.addAll(groups); + repo.clearGroups(); + repo.addAllGroups(groups); if (withNotification) { notifyListeners(); } diff --git a/pubspec.lock b/pubspec.lock index 50f7094..40d01e6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -644,6 +644,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.4.1" + sqlite3: + dependency: "direct main" + description: + name: sqlite3 + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.1" stack_trace: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0ff792c..03c4a6a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: result_monad: ^2.0.2 scrollable_positioned_list: ^0.3.5 shared_preferences: ^2.0.15 + sqlite3: ^1.9.1 time_machine: ^0.9.17 url_launcher: ^6.1.6 uuid: ^3.0.6