kopia lustrzana https://gitlab.com/mysocialportal/relatica
Add initial contacts screen
rodzic
b862360f99
commit
93d08dcf82
|
@ -44,7 +44,7 @@ class AppBottomNavBar extends StatelessWidget {
|
|||
// TODO: Handle this case.
|
||||
break;
|
||||
case NavBarButtons.contacts:
|
||||
// TODO: Handle this case.
|
||||
context.pushNamed(ScreenPaths.contacts);
|
||||
break;
|
||||
case NavBarButtons.profile:
|
||||
context.pushNamed(ScreenPaths.profile);
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'models/group_data.dart';
|
|||
import 'models/timeline_entry.dart';
|
||||
import 'models/user_notification.dart';
|
||||
import 'serializers/friendica/connection_friendica_extensions.dart';
|
||||
import 'serializers/mastodon/connection_mastodon_extensions.dart';
|
||||
import 'serializers/mastodon/group_data_mastodon_extensions.dart';
|
||||
import 'serializers/mastodon/notification_mastodon_extension.dart';
|
||||
import 'serializers/mastodon/timeline_entry_mastodon_extensions.dart';
|
||||
|
@ -109,6 +110,36 @@ class FriendicaClient {
|
|||
return (await _deleteUrl(request, requestData)).mapValue((_) => true);
|
||||
}
|
||||
|
||||
FutureResult<List<Connection>, ExecError> getMyFollowing(
|
||||
{int sinceId = -1, int maxId = -1, int limit = 50}) async {
|
||||
_logger.finest(() =>
|
||||
'Getting following data since $sinceId, maxId $maxId, limit $limit');
|
||||
final myId = getIt<AuthService>().currentId;
|
||||
final paging =
|
||||
_buildPagingData(sinceId: sinceId, maxId: maxId, limit: limit);
|
||||
final baseUrl = 'https://$serverName/api/v1/accounts/$myId';
|
||||
return (await _getApiListRequest(Uri.parse('$baseUrl/following&$paging'))
|
||||
.andThenSuccessAsync((listJson) async => listJson
|
||||
.map((json) => ConnectionMastodonExtensions.fromJson(json))
|
||||
.toList()))
|
||||
.execErrorCast();
|
||||
}
|
||||
|
||||
FutureResult<List<Connection>, ExecError> getMyFollowers(
|
||||
{int sinceId = -1, int maxId = -1, int limit = 50}) async {
|
||||
_logger.finest(() =>
|
||||
'Getting followers data since $sinceId, maxId $maxId, limit $limit');
|
||||
final myId = getIt<AuthService>().currentId;
|
||||
final paging =
|
||||
_buildPagingData(sinceId: sinceId, maxId: maxId, limit: limit);
|
||||
final baseUrl = 'https://$serverName/api/v1/accounts/$myId';
|
||||
return (await _getApiListRequest(Uri.parse('$baseUrl/followers&$paging'))
|
||||
.andThenSuccessAsync((listJson) async => listJson
|
||||
.map((json) => ConnectionMastodonExtensions.fromJson(json))
|
||||
.toList()))
|
||||
.execErrorCast();
|
||||
}
|
||||
|
||||
FutureResult<Connection, ExecError> getConnectionWithStatus(
|
||||
Connection connection) async {
|
||||
_logger.finest(() => 'Getting group (Mastodon List) data');
|
||||
|
@ -179,14 +210,8 @@ class FriendicaClient {
|
|||
final String timelinePath = _typeToTimelinePath(type);
|
||||
final String timelineQPs = _typeToTimelineQueryParameters(type);
|
||||
final baseUrl = 'https://$serverName/api/v1/$timelinePath';
|
||||
var pagingData = 'limit=$limit';
|
||||
if (maxId > 0) {
|
||||
pagingData = '$pagingData&max_id=$maxId';
|
||||
}
|
||||
|
||||
if (sinceId > 0) {
|
||||
pagingData = '&since_id=$sinceId';
|
||||
}
|
||||
final pagingData =
|
||||
_buildPagingData(sinceId: sinceId, maxId: maxId, limit: limit);
|
||||
|
||||
final url = '$baseUrl?exclude_replies=true&$pagingData&$timelineQPs';
|
||||
final request = Uri.parse(url);
|
||||
|
@ -464,4 +489,18 @@ class FriendicaClient {
|
|||
throw UnimplementedError('These types are not supported yet');
|
||||
}
|
||||
}
|
||||
|
||||
String _buildPagingData(
|
||||
{required int sinceId, required int maxId, required int limit}) {
|
||||
var pagingData = 'limit=$limit';
|
||||
if (maxId > 0) {
|
||||
pagingData = '$pagingData&max_id=$maxId';
|
||||
}
|
||||
|
||||
if (sinceId > 0) {
|
||||
pagingData = '&since_id=$sinceId';
|
||||
}
|
||||
|
||||
return pagingData;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'globals.dart';
|
||||
import 'screens/contacts_screen.dart';
|
||||
import 'screens/editor.dart';
|
||||
import 'screens/home.dart';
|
||||
import 'screens/notifications_screen.dart';
|
||||
|
@ -13,6 +14,7 @@ import 'screens/user_profile_screen.dart';
|
|||
import 'services/auth_service.dart';
|
||||
|
||||
class ScreenPaths {
|
||||
static String contacts = '/contacts';
|
||||
static String splash = '/splash';
|
||||
static String timelines = '/';
|
||||
static String profile = '/profile';
|
||||
|
@ -58,6 +60,14 @@ final appRouter = GoRouter(
|
|||
name: ScreenPaths.signin,
|
||||
builder: (context, state) => SignInScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: ScreenPaths.contacts,
|
||||
name: ScreenPaths.contacts,
|
||||
pageBuilder: (context, state) => NoTransitionPage(
|
||||
name: ScreenPaths.contacts,
|
||||
child: ContactsScreen(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: ScreenPaths.timelines,
|
||||
name: ScreenPaths.timelines,
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../controls/app_bottom_nav_bar.dart';
|
||||
import '../models/connection.dart';
|
||||
import '../routes.dart';
|
||||
import '../services/connections_manager.dart';
|
||||
|
||||
class ContactsScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final manager = context.watch<ConnectionsManager>();
|
||||
final contacts = manager.getMyContacts();
|
||||
contacts.sort((c1, c2) => c1.name.compareTo(c2.name));
|
||||
late Widget body;
|
||||
if (contacts.isEmpty) {
|
||||
body = const SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: Text('No Contacts'),
|
||||
);
|
||||
} else {
|
||||
body = ListView.separated(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
final contact = contacts[index];
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
context.pushNamed(ScreenPaths.userProfile,
|
||||
params: {'id': contact.id});
|
||||
},
|
||||
title: Text(contact.name),
|
||||
trailing: Text(contact.status.label()),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const Divider(),
|
||||
itemCount: contacts.length);
|
||||
}
|
||||
return Scaffold(
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await manager.updateAllContacts();
|
||||
},
|
||||
child: Center(
|
||||
child: body,
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: AppBottomNavBar(
|
||||
currentButton: NavBarButtons.contacts,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:result_monad/result_monad.dart';
|
||||
|
@ -15,6 +17,8 @@ class ConnectionsManager extends ChangeNotifier {
|
|||
final _connectionsByProfileUrl = <Uri, Connection>{};
|
||||
final _groupsForConnection = <String, List<GroupData>>{};
|
||||
final _myGroups = <GroupData>{};
|
||||
final _myContacts = <Connection>[];
|
||||
var _myContactsInitialized = false;
|
||||
|
||||
int get length => _connectionsById.length;
|
||||
|
||||
|
@ -36,6 +40,23 @@ class ConnectionsManager extends ChangeNotifier {
|
|||
_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;
|
||||
}
|
||||
|
@ -88,6 +109,73 @@ class ConnectionsManager extends ChangeNotifier {
|
|||
);
|
||||
}
|
||||
|
||||
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 = 100;
|
||||
while (moreResults) {
|
||||
await client.getMyFollowers(sinceId: maxId, limit: limit).match(
|
||||
onSuccess: (followers) {
|
||||
if (followers.length < limit) {
|
||||
moreResults = false;
|
||||
for (final f in followers) {
|
||||
results[f.id] = f.copy(status: ConnectionStatus.theyFollowYou);
|
||||
int id = int.parse(f.id);
|
||||
maxId = max(maxId, id);
|
||||
}
|
||||
}
|
||||
}, 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) {
|
||||
if (following.length < limit) {
|
||||
moreResults = false;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}, 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));
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<GroupData> getMyGroups() {
|
||||
if (_myGroups.isNotEmpty) {
|
||||
return _myGroups.toList(growable: false);
|
||||
|
@ -140,9 +228,9 @@ class ConnectionsManager extends ChangeNotifier {
|
|||
Result<Connection, String> getById(String id) {
|
||||
final result = _connectionsById[id];
|
||||
if (result == null) {
|
||||
Result.error('$id not found');
|
||||
return Result.error('$id not found');
|
||||
}
|
||||
if (result!.status == ConnectionStatus.unknown) {
|
||||
if (result.status == ConnectionStatus.unknown) {
|
||||
_refreshConnection(result, true);
|
||||
}
|
||||
return Result.ok(result);
|
||||
|
|
Ładowanie…
Reference in New Issue