kopia lustrzana https://gitlab.com/mysocialportal/relatica
Initial connection adjudication screen
rodzic
fbab2dcae1
commit
c3ee438f1c
|
@ -54,12 +54,10 @@ class NotificationControl extends StatelessWidget {
|
|||
onTap: () async {
|
||||
switch (notification.type) {
|
||||
case NotificationType.follow:
|
||||
buildSnackbar(
|
||||
context, 'Want to follow ${notification.fromName}?');
|
||||
context.push('/connect/${notification.fromId}');
|
||||
break;
|
||||
case NotificationType.follow_request:
|
||||
buildSnackbar(
|
||||
context, 'Want to accept follow ${notification.fromName}?');
|
||||
context.push('/connect/${notification.fromId}');
|
||||
break;
|
||||
case NotificationType.unknown:
|
||||
buildSnackbar(context, 'Unknown message type, nothing to do');
|
||||
|
|
|
@ -336,15 +336,55 @@ class FriendicaClient {
|
|||
});
|
||||
}
|
||||
|
||||
FutureResult<Connection, ExecError> acceptFollow(
|
||||
Connection connection) async {
|
||||
final id = connection.id;
|
||||
final url =
|
||||
Uri.parse('https://$serverName/api/v1/follow_requests/$id/authorize');
|
||||
final result =
|
||||
await _postUrl(url, {}).andThenSuccessAsync((jsonString) async {
|
||||
return _updateConnectionFromFollowRequestResult(connection, jsonString);
|
||||
});
|
||||
return result.mapError((error) => error is ExecError
|
||||
? error
|
||||
: ExecError(type: ErrorType.localError, message: error.toString()));
|
||||
}
|
||||
|
||||
FutureResult<Connection, ExecError> rejectFollow(
|
||||
Connection connection) async {
|
||||
final id = connection.id;
|
||||
final url =
|
||||
Uri.parse('https://$serverName/api/v1/follow_requests/$id/reject');
|
||||
final result =
|
||||
await _postUrl(url, {}).andThenSuccessAsync((jsonString) async {
|
||||
return _updateConnectionFromFollowRequestResult(connection, jsonString);
|
||||
});
|
||||
return result.mapError((error) => error is ExecError
|
||||
? error
|
||||
: ExecError(type: ErrorType.localError, message: error.toString()));
|
||||
}
|
||||
|
||||
FutureResult<Connection, ExecError> ignoreFollow(
|
||||
Connection connection) async {
|
||||
final id = connection.id;
|
||||
final url =
|
||||
Uri.parse('https://$serverName/api/v1/follow_requests/$id/ignore');
|
||||
final result =
|
||||
await _postUrl(url, {}).andThenSuccessAsync((jsonString) async {
|
||||
return _updateConnectionFromFollowRequestResult(connection, jsonString);
|
||||
});
|
||||
return result.mapError((error) => error is ExecError
|
||||
? error
|
||||
: ExecError(type: ErrorType.localError, message: error.toString()));
|
||||
}
|
||||
|
||||
FutureResult<Connection, ExecError> followConnection(
|
||||
Connection connection) async {
|
||||
final id = connection.id;
|
||||
final url = Uri.parse('https://$serverName/api/v1/accounts/$id/follow');
|
||||
final result = await _postUrl(url, {}).andThenSuccessAsync((_) async {
|
||||
final newStatus = connection.status == ConnectionStatus.theyFollowYou
|
||||
? ConnectionStatus.mutual
|
||||
: ConnectionStatus.youFollowThem;
|
||||
return connection.copy(status: newStatus);
|
||||
final result =
|
||||
await _postUrl(url, {}).andThenSuccessAsync((jsonString) async {
|
||||
return _updateConnectionFromFollowRequestResult(connection, jsonString);
|
||||
});
|
||||
return result.mapError((error) => error is ExecError
|
||||
? error
|
||||
|
@ -355,11 +395,9 @@ class FriendicaClient {
|
|||
Connection connection) async {
|
||||
final id = connection.id;
|
||||
final url = Uri.parse('https://$serverName/api/v1/accounts/$id/unfollow');
|
||||
final result = await _postUrl(url, {}).andThenSuccessAsync((_) async {
|
||||
final newStatus = connection.status == ConnectionStatus.mutual
|
||||
? ConnectionStatus.theyFollowYou
|
||||
: ConnectionStatus.none;
|
||||
return connection.copy(status: newStatus);
|
||||
final result =
|
||||
await _postUrl(url, {}).andThenSuccessAsync((jsonString) async {
|
||||
return _updateConnectionFromFollowRequestResult(connection, jsonString);
|
||||
});
|
||||
return result.mapError((error) => error is ExecError
|
||||
? error
|
||||
|
@ -503,4 +541,22 @@ class FriendicaClient {
|
|||
|
||||
return pagingData;
|
||||
}
|
||||
|
||||
Connection _updateConnectionFromFollowRequestResult(
|
||||
Connection connection, String jsonString) {
|
||||
final json = jsonDecode(jsonString) as Map<String, dynamic>;
|
||||
final theyFollowYou = json['followed_by'] ?? 'false';
|
||||
final youFollowThem = json['following'] ?? 'false';
|
||||
late final ConnectionStatus newStatus;
|
||||
if (theyFollowYou && youFollowThem) {
|
||||
newStatus = ConnectionStatus.mutual;
|
||||
} else if (theyFollowYou) {
|
||||
newStatus = ConnectionStatus.theyFollowYou;
|
||||
} else if (youFollowThem) {
|
||||
newStatus = ConnectionStatus.youFollowThem;
|
||||
} else {
|
||||
newStatus = ConnectionStatus.none;
|
||||
}
|
||||
return connection.copy(status: newStatus);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart';
|
|||
import 'globals.dart';
|
||||
import 'screens/contacts_screen.dart';
|
||||
import 'screens/editor.dart';
|
||||
import 'screens/follow_request_adjudication_screen.dart';
|
||||
import 'screens/home.dart';
|
||||
import 'screens/notifications_screen.dart';
|
||||
import 'screens/post_screen.dart';
|
||||
|
@ -14,6 +15,7 @@ import 'screens/user_profile_screen.dart';
|
|||
import 'services/auth_service.dart';
|
||||
|
||||
class ScreenPaths {
|
||||
static String connectHandle = '/connect';
|
||||
static String contacts = '/contacts';
|
||||
static String splash = '/splash';
|
||||
static String timelines = '/';
|
||||
|
@ -68,6 +70,12 @@ final appRouter = GoRouter(
|
|||
child: ContactsScreen(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/connect/:id',
|
||||
name: ScreenPaths.connectHandle,
|
||||
builder: (context, state) =>
|
||||
FollowRequestAdjudicationScreen(userId: state.params['id']!),
|
||||
),
|
||||
GoRoute(
|
||||
path: ScreenPaths.timelines,
|
||||
name: ScreenPaths.timelines,
|
||||
|
|
|
@ -1,16 +1,164 @@
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../controls/padding.dart';
|
||||
import '../models/connection.dart';
|
||||
import '../services/connections_manager.dart';
|
||||
|
||||
class FollowRequestAdjudicationScreen extends StatefulWidget {
|
||||
final String userId;
|
||||
|
||||
const FollowRequestAdjudicationScreen({super.key, required this.userId});
|
||||
|
||||
@override
|
||||
State<FollowRequestAdjudicationScreen> createState() =>
|
||||
_FollowRequestAdjudicationScreenState();
|
||||
}
|
||||
|
||||
class _FollowRequestAdjudicationScreenState
|
||||
extends State<FollowRequestAdjudicationScreen> {
|
||||
var processing = false;
|
||||
|
||||
class FollowRequestAdjudicationScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// with ID, get contact
|
||||
final manager = context.watch<ConnectionsManager>();
|
||||
final connResult = manager.getById(widget.userId);
|
||||
late final Widget body;
|
||||
if (connResult.isFailure) {
|
||||
body = Text('Error getting contact information: ${connResult.error}');
|
||||
}
|
||||
|
||||
final contact = connResult.value;
|
||||
switch (contact.status) {
|
||||
case ConnectionStatus.mutual:
|
||||
case ConnectionStatus.theyFollowYou:
|
||||
case ConnectionStatus.youFollowThem:
|
||||
case ConnectionStatus.none:
|
||||
body = _buildMainPanel(context, manager, contact);
|
||||
break;
|
||||
case ConnectionStatus.you:
|
||||
case ConnectionStatus.unknown:
|
||||
body = Text('Invalid state, nothing to do here: ${contact.status}');
|
||||
break;
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
'Accept Request?',
|
||||
)),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Center(child: body),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMainPanel(
|
||||
BuildContext context, ConnectionsManager manager, Connection contact) {
|
||||
// Options are:
|
||||
// Accept and follow back
|
||||
// Accept and don't follow back
|
||||
// Reject
|
||||
// Back with no action
|
||||
// Calling method should check if completed (true) or not (false) to decide if updating their view of that item
|
||||
// TODO: implement build
|
||||
throw UnimplementedError();
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
CachedNetworkImage(imageUrl: contact.avatarUrl.toString()),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
contact.name,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const HorizontalPadding(),
|
||||
],
|
||||
),
|
||||
const VerticalPadding(),
|
||||
ElevatedButton(
|
||||
onPressed: processing
|
||||
? null
|
||||
: () async => await accept(manager, contact, true),
|
||||
child: const Text('Accept and follow back'),
|
||||
),
|
||||
const VerticalPadding(),
|
||||
ElevatedButton(
|
||||
onPressed:
|
||||
processing ? null : () async => accept(manager, contact, false),
|
||||
child: const Text("Accept but don't follow back"),
|
||||
),
|
||||
const VerticalPadding(),
|
||||
ElevatedButton(
|
||||
onPressed: processing ? null : () async => reject(manager, contact),
|
||||
child: const Text('Reject'),
|
||||
),
|
||||
const VerticalPadding(),
|
||||
ElevatedButton(
|
||||
onPressed: processing ? null : () async => ignore(manager, contact),
|
||||
child: const Text('Ignore (Rejects but user cannot ask again)'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> accept(
|
||||
ConnectionsManager manager,
|
||||
Connection contact,
|
||||
bool followBack,
|
||||
) async {
|
||||
setState(() {
|
||||
processing = true;
|
||||
});
|
||||
|
||||
await manager.acceptFollowRequest(contact);
|
||||
if (followBack) {
|
||||
await manager.follow(contact);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
processing = false;
|
||||
});
|
||||
|
||||
if (mounted && context.canPop()) {
|
||||
context.pop();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reject(ConnectionsManager manager, Connection contact) async {
|
||||
setState(() {
|
||||
processing = true;
|
||||
});
|
||||
|
||||
await manager.rejectFollowRequest(contact);
|
||||
|
||||
setState(() {
|
||||
processing = false;
|
||||
});
|
||||
|
||||
if (mounted && context.canPop()) {
|
||||
context.pop();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> ignore(ConnectionsManager manager, Connection contact) async {
|
||||
setState(() {
|
||||
processing = true;
|
||||
});
|
||||
|
||||
await manager.ignoreFollowRequest(contact);
|
||||
|
||||
setState(() {
|
||||
processing = false;
|
||||
});
|
||||
|
||||
if (mounted && context.canPop()) {
|
||||
context.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ class ConnectionsManager extends ChangeNotifier {
|
|||
_connectionsByName.clear();
|
||||
_connectionsByProfileUrl.clear();
|
||||
_groupsForConnection.clear();
|
||||
_myGroups.clear();
|
||||
_myContacts.clear();
|
||||
}
|
||||
|
||||
bool addConnection(Connection connection) {
|
||||
|
@ -71,6 +73,63 @@ class ConnectionsManager extends ChangeNotifier {
|
|||
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}');
|
||||
|
|
Ładowanie…
Reference in New Issue