kopia lustrzana https://gitlab.com/mysocialportal/relatica
Merge branch 'blocking' into 'main'
Blocking See merge request mysocialportal/relatica!39codemagic-setup
commit
6074ca3ac2
|
@ -74,6 +74,11 @@ class StandardAppDrawer extends StatelessWidget {
|
||||||
() => context.pushNamed(ScreenPaths.messages),
|
() => context.pushNamed(ScreenPaths.messages),
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
|
buildMenuButton(
|
||||||
|
context,
|
||||||
|
'Blocks',
|
||||||
|
() => context.pushNamed(ScreenPaths.blocks),
|
||||||
|
),
|
||||||
buildMenuButton(
|
buildMenuButton(
|
||||||
context,
|
context,
|
||||||
'Groups Management',
|
'Groups Management',
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'globals.dart';
|
||||||
import 'models/auth/profile.dart';
|
import 'models/auth/profile.dart';
|
||||||
import 'models/instance_info.dart';
|
import 'models/instance_info.dart';
|
||||||
import 'services/auth_service.dart';
|
import 'services/auth_service.dart';
|
||||||
|
import 'services/blocks_manager.dart';
|
||||||
import 'services/connections_manager.dart';
|
import 'services/connections_manager.dart';
|
||||||
import 'services/direct_message_service.dart';
|
import 'services/direct_message_service.dart';
|
||||||
import 'services/entry_manager_service.dart';
|
import 'services/entry_manager_service.dart';
|
||||||
|
@ -120,7 +121,9 @@ Future<void> dependencyInjectionInitialization() async {
|
||||||
getIt.registerSingleton<ActiveProfileSelector<InteractionsManager>>(
|
getIt.registerSingleton<ActiveProfileSelector<InteractionsManager>>(
|
||||||
ActiveProfileSelector((p) => InteractionsManager(p))
|
ActiveProfileSelector((p) => InteractionsManager(p))
|
||||||
..subscribeToProfileSwaps());
|
..subscribeToProfileSwaps());
|
||||||
|
getIt.registerSingleton<ActiveProfileSelector<BlocksManager>>(
|
||||||
|
ActiveProfileSelector((p) => BlocksManager(p))
|
||||||
|
..subscribeToProfileSwaps());
|
||||||
setupUpdateTimers();
|
setupUpdateTimers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -471,6 +471,33 @@ class RelationshipsClient extends FriendicaClient {
|
||||||
|
|
||||||
RelationshipsClient(super.credentials) : super();
|
RelationshipsClient(super.credentials) : super();
|
||||||
|
|
||||||
|
FutureResult<PagedResponse<List<Connection>>, ExecError> getBlocks(
|
||||||
|
PagingData page) async {
|
||||||
|
_networkStatusService.startNotificationUpdate();
|
||||||
|
final url = 'https://$serverName/api/v1/blocks';
|
||||||
|
final request = Uri.parse('$url&${page.toQueryParameters()}');
|
||||||
|
_logger.finest(() => 'Getting blocks for $page');
|
||||||
|
final result =
|
||||||
|
await _getApiListRequest(request).transformAsync((response) async {
|
||||||
|
final blocks = <Connection>[];
|
||||||
|
|
||||||
|
final st = Stopwatch()..start();
|
||||||
|
for (final json in response.data) {
|
||||||
|
if (st.elapsedMilliseconds > _maxProcessingMillis) {
|
||||||
|
await Future.delayed(_processingSleep, () => st.reset());
|
||||||
|
}
|
||||||
|
blocks.add(
|
||||||
|
ConnectionMastodonExtensions.fromJson(json)
|
||||||
|
.copy(status: ConnectionStatus.blocked),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return PagedResponse(blocks,
|
||||||
|
id: response.id, previous: response.previous, next: response.next);
|
||||||
|
});
|
||||||
|
_networkStatusService.finishNotificationUpdate();
|
||||||
|
return result.execErrorCast();
|
||||||
|
}
|
||||||
|
|
||||||
FutureResult<PagedResponse<List<Connection>>, ExecError> getMyFollowing(
|
FutureResult<PagedResponse<List<Connection>>, ExecError> getMyFollowing(
|
||||||
PagingData page) async {
|
PagingData page) async {
|
||||||
_logger.fine(() => 'Getting following with paging data $page');
|
_logger.fine(() => 'Getting following with paging data $page');
|
||||||
|
@ -617,30 +644,47 @@ class RelationshipsClient extends FriendicaClient {
|
||||||
: ExecError(type: ErrorType.localError, message: error.toString()));
|
: ExecError(type: ErrorType.localError, message: error.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FutureResult<Connection, ExecError> blockConnection(
|
||||||
|
Connection connection) async {
|
||||||
|
final id = connection.id;
|
||||||
|
final url = Uri.parse('https://$serverName/api/v1/accounts/$id/block');
|
||||||
|
final result = await postUrl(url, {}, headers: _headers).transform(
|
||||||
|
(_) => connection.copy(status: ConnectionStatus.blocked),
|
||||||
|
);
|
||||||
|
return result.execErrorCast();
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureResult<Connection, ExecError> unblockConnection(
|
||||||
|
Connection connection) async {
|
||||||
|
final id = connection.id;
|
||||||
|
final url = Uri.parse('https://$serverName/api/v1/accounts/$id/unblock');
|
||||||
|
final result = await postUrl(url, {}, headers: _headers)
|
||||||
|
.transformAsync((jsonString) async {
|
||||||
|
return _updateConnectionFromFollowRequestResult(connection, jsonString);
|
||||||
|
});
|
||||||
|
return result.execErrorCast();
|
||||||
|
}
|
||||||
|
|
||||||
FutureResult<Connection, ExecError> followConnection(
|
FutureResult<Connection, ExecError> followConnection(
|
||||||
Connection connection) async {
|
Connection connection) async {
|
||||||
final id = connection.id;
|
final id = connection.id;
|
||||||
final url = Uri.parse('https://$serverName/api/v1/accounts/$id/follow');
|
final url = Uri.parse('https://$serverName/api/v1/accounts/$id/follow');
|
||||||
final result = await postUrl(url, {}, headers: _headers)
|
final result =
|
||||||
.andThenSuccessAsync((jsonString) async {
|
await postUrl(url, {}, headers: _headers).transform((jsonString) {
|
||||||
return _updateConnectionFromFollowRequestResult(connection, jsonString);
|
return _updateConnectionFromFollowRequestResult(connection, jsonString);
|
||||||
});
|
});
|
||||||
return result.mapError((error) => error is ExecError
|
return result.execErrorCast();
|
||||||
? error
|
|
||||||
: ExecError(type: ErrorType.localError, message: error.toString()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<Connection, ExecError> unFollowConnection(
|
FutureResult<Connection, ExecError> unFollowConnection(
|
||||||
Connection connection) async {
|
Connection connection) async {
|
||||||
final id = connection.id;
|
final id = connection.id;
|
||||||
final url = Uri.parse('https://$serverName/api/v1/accounts/$id/unfollow');
|
final url = Uri.parse('https://$serverName/api/v1/accounts/$id/unfollow');
|
||||||
final result = await postUrl(url, {}, headers: _headers)
|
final result =
|
||||||
.andThenSuccessAsync((jsonString) async {
|
await postUrl(url, {}, headers: _headers).transform((jsonString) {
|
||||||
return _updateConnectionFromFollowRequestResult(connection, jsonString);
|
return _updateConnectionFromFollowRequestResult(connection, jsonString);
|
||||||
});
|
});
|
||||||
return result.mapError((error) => error is ExecError
|
return result.execErrorCast();
|
||||||
? error
|
|
||||||
: ExecError(type: ErrorType.localError, message: error.toString()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection _updateConnectionFromFollowRequestResult(
|
Connection _updateConnectionFromFollowRequestResult(
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'di_initialization.dart';
|
||||||
import 'globals.dart';
|
import 'globals.dart';
|
||||||
import 'routes.dart';
|
import 'routes.dart';
|
||||||
import 'services/auth_service.dart';
|
import 'services/auth_service.dart';
|
||||||
|
import 'services/blocks_manager.dart';
|
||||||
import 'services/connections_manager.dart';
|
import 'services/connections_manager.dart';
|
||||||
import 'services/direct_message_service.dart';
|
import 'services/direct_message_service.dart';
|
||||||
import 'services/entry_manager_service.dart';
|
import 'services/entry_manager_service.dart';
|
||||||
|
@ -27,7 +28,9 @@ import 'utils/old_android_letsencrypte_cert.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
MediaKit.ensureInitialized();
|
if (kReleaseMode) {
|
||||||
|
MediaKit.ensureInitialized();
|
||||||
|
}
|
||||||
// await dotenv.load(fileName: '.env');
|
// await dotenv.load(fileName: '.env');
|
||||||
const enablePreview = false;
|
const enablePreview = false;
|
||||||
Logger.root.level = Level.FINER;
|
Logger.root.level = Level.FINER;
|
||||||
|
@ -108,7 +111,10 @@ class App extends StatelessWidget {
|
||||||
ActiveProfileSelector<InteractionsManager>>(
|
ActiveProfileSelector<InteractionsManager>>(
|
||||||
create: (_) =>
|
create: (_) =>
|
||||||
getIt<ActiveProfileSelector<InteractionsManager>>(),
|
getIt<ActiveProfileSelector<InteractionsManager>>(),
|
||||||
)
|
),
|
||||||
|
ChangeNotifierProvider<ActiveProfileSelector<BlocksManager>>(
|
||||||
|
create: (_) => getIt<ActiveProfileSelector<BlocksManager>>(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: MaterialApp.router(
|
child: MaterialApp.router(
|
||||||
useInheritedMediaQuery: true,
|
useInheritedMediaQuery: true,
|
||||||
|
|
|
@ -113,6 +113,7 @@ class Connection {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConnectionStatus {
|
enum ConnectionStatus {
|
||||||
|
blocked(0),
|
||||||
youFollowThem(1),
|
youFollowThem(1),
|
||||||
theyFollowYou(2),
|
theyFollowYou(2),
|
||||||
mutual(3),
|
mutual(3),
|
||||||
|
@ -145,6 +146,8 @@ extension FriendStatusWriter on ConnectionStatus {
|
||||||
return "You";
|
return "You";
|
||||||
case ConnectionStatus.unknown:
|
case ConnectionStatus.unknown:
|
||||||
return 'Unknown';
|
return 'Unknown';
|
||||||
|
case ConnectionStatus.blocked:
|
||||||
|
return 'Blocked';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import 'globals.dart';
|
import 'globals.dart';
|
||||||
import 'models/interaction_type_enum.dart';
|
import 'models/interaction_type_enum.dart';
|
||||||
|
import 'screens/blocks_screen.dart';
|
||||||
import 'screens/contacts_screen.dart';
|
import 'screens/contacts_screen.dart';
|
||||||
import 'screens/editor.dart';
|
import 'screens/editor.dart';
|
||||||
import 'screens/follow_request_adjudication_screen.dart';
|
import 'screens/follow_request_adjudication_screen.dart';
|
||||||
|
@ -28,6 +29,7 @@ import 'screens/user_profile_screen.dart';
|
||||||
import 'services/auth_service.dart';
|
import 'services/auth_service.dart';
|
||||||
|
|
||||||
class ScreenPaths {
|
class ScreenPaths {
|
||||||
|
static String blocks = '/blocks';
|
||||||
static String thread = '/thread';
|
static String thread = '/thread';
|
||||||
static String connectHandle = '/connect';
|
static String connectHandle = '/connect';
|
||||||
static String contacts = '/contacts';
|
static String contacts = '/contacts';
|
||||||
|
@ -73,6 +75,11 @@ final appRouter = GoRouter(
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
routes: [
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: ScreenPaths.blocks,
|
||||||
|
name: ScreenPaths.blocks,
|
||||||
|
builder: (context, state) => const BlocksScreen(),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: ScreenPaths.signin,
|
path: ScreenPaths.signin,
|
||||||
name: ScreenPaths.signin,
|
name: ScreenPaths.signin,
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import '../routes.dart';
|
||||||
|
import '../services/blocks_manager.dart';
|
||||||
|
import '../utils/active_profile_selector.dart';
|
||||||
|
|
||||||
|
class BlocksScreen extends StatelessWidget {
|
||||||
|
const BlocksScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final manager =
|
||||||
|
context.watch<ActiveProfileSelector<BlocksManager>>().activeEntry.value;
|
||||||
|
final blocks = manager.getBlocks();
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Blocks'),
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final contact = blocks[index];
|
||||||
|
return ListTile(
|
||||||
|
onTap: () async {
|
||||||
|
context.pushNamed(ScreenPaths.userProfile,
|
||||||
|
params: {'id': contact.id});
|
||||||
|
},
|
||||||
|
title: Text(
|
||||||
|
'${contact.name} (${contact.handle})',
|
||||||
|
softWrap: true,
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
'Last Status: ${contact.lastStatus?.toIso8601String() ?? "Unknown"}',
|
||||||
|
softWrap: true,
|
||||||
|
),
|
||||||
|
trailing: ElevatedButton(
|
||||||
|
onPressed: () async => await manager.unblockConnection(contact),
|
||||||
|
child: const Text('Unblock'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemCount: blocks.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import '../routes.dart';
|
||||||
import '../services/connections_manager.dart';
|
import '../services/connections_manager.dart';
|
||||||
import '../services/feature_version_checker.dart';
|
import '../services/feature_version_checker.dart';
|
||||||
import '../services/follow_requests_manager.dart';
|
import '../services/follow_requests_manager.dart';
|
||||||
|
import '../services/network_status_service.dart';
|
||||||
import '../services/notifications_manager.dart';
|
import '../services/notifications_manager.dart';
|
||||||
import '../utils/active_profile_selector.dart';
|
import '../utils/active_profile_selector.dart';
|
||||||
import '../utils/url_opening_utils.dart';
|
import '../utils/url_opening_utils.dart';
|
||||||
|
@ -33,6 +34,7 @@ class _FollowRequestAdjudicationScreenState
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final nss = getIt<NetworkStatusService>();
|
||||||
final fm =
|
final fm =
|
||||||
getIt<ActiveProfileSelector<FollowRequestsManager>>().activeEntry.value;
|
getIt<ActiveProfileSelector<FollowRequestsManager>>().activeEntry.value;
|
||||||
final cm = context
|
final cm = context
|
||||||
|
@ -71,7 +73,15 @@ class _FollowRequestAdjudicationScreenState
|
||||||
break;
|
break;
|
||||||
case ConnectionStatus.you:
|
case ConnectionStatus.you:
|
||||||
case ConnectionStatus.unknown:
|
case ConnectionStatus.unknown:
|
||||||
body = Text('Invalid state, nothing to do here: ${contact.status}');
|
body = Text(nss.connectionUpdateStatus.value
|
||||||
|
? 'Loading...'
|
||||||
|
: 'Invalid state, nothing to do here: ${contact.status}');
|
||||||
|
break;
|
||||||
|
case ConnectionStatus.blocked:
|
||||||
|
// we should never get here because a blocked user shouldn't be allowed to create a connection request.
|
||||||
|
body = const Text(
|
||||||
|
'Use is blocked. Unblock to accept connection request.',
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import '../models/connection.dart';
|
||||||
import '../models/group_data.dart';
|
import '../models/group_data.dart';
|
||||||
import '../routes.dart';
|
import '../routes.dart';
|
||||||
import '../services/auth_service.dart';
|
import '../services/auth_service.dart';
|
||||||
|
import '../services/blocks_manager.dart';
|
||||||
import '../services/connections_manager.dart';
|
import '../services/connections_manager.dart';
|
||||||
import '../utils/active_profile_selector.dart';
|
import '../utils/active_profile_selector.dart';
|
||||||
import '../utils/snackbar_builder.dart';
|
import '../utils/snackbar_builder.dart';
|
||||||
|
@ -37,17 +38,22 @@ class _UserProfileScreenState extends State<UserProfileScreen> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final manager = context
|
final connectionManager = context
|
||||||
.watch<ActiveProfileSelector<ConnectionsManager>>()
|
.watch<ActiveProfileSelector<ConnectionsManager>>()
|
||||||
.activeEntry
|
.activeEntry
|
||||||
.value;
|
.value;
|
||||||
final body = manager.getById(widget.userId).fold(onSuccess: (profile) {
|
final blocksManager =
|
||||||
|
context.watch<ActiveProfileSelector<BlocksManager>>().activeEntry.value;
|
||||||
|
final body =
|
||||||
|
connectionManager.getById(widget.userId).fold(onSuccess: (profile) {
|
||||||
final notMyProfile =
|
final notMyProfile =
|
||||||
getIt<AccountsService>().currentProfile.userId != profile.id;
|
getIt<AccountsService>().currentProfile.userId != profile.id;
|
||||||
|
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await manager.fullRefresh(profile);
|
await connectionManager.fullRefresh(profile,
|
||||||
|
withNotifications: false);
|
||||||
|
await blocksManager.updateBlock(profile);
|
||||||
},
|
},
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
@ -73,11 +79,15 @@ class _UserProfileScreenState extends State<UserProfileScreen> {
|
||||||
const VerticalPadding(),
|
const VerticalPadding(),
|
||||||
Text('( ${profile.status.label()} )'),
|
Text('( ${profile.status.label()} )'),
|
||||||
const VerticalPadding(),
|
const VerticalPadding(),
|
||||||
Row(
|
Wrap(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
spacing: 10.0,
|
||||||
|
runSpacing: 10.0,
|
||||||
children: [
|
children: [
|
||||||
if (notMyProfile)
|
if (notMyProfile) ...[
|
||||||
buildConnectionStatusToggle(context, profile, manager),
|
buildConnectionStatusToggle(
|
||||||
|
context, profile, connectionManager),
|
||||||
|
buildBlockToggle(context, profile, blocksManager),
|
||||||
|
],
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => context.pushNamed(
|
onPressed: () => context.pushNamed(
|
||||||
ScreenPaths.userPosts,
|
ScreenPaths.userPosts,
|
||||||
|
@ -107,7 +117,7 @@ class _UserProfileScreenState extends State<UserProfileScreen> {
|
||||||
const VerticalPadding(),
|
const VerticalPadding(),
|
||||||
if (profile.status == ConnectionStatus.mutual ||
|
if (profile.status == ConnectionStatus.mutual ||
|
||||||
profile.status == ConnectionStatus.youFollowThem)
|
profile.status == ConnectionStatus.youFollowThem)
|
||||||
buildGroups(context, profile, manager),
|
buildGroups(context, profile, connectionManager),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -183,6 +193,66 @@ class _UserProfileScreenState extends State<UserProfileScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildBlockToggle(
|
||||||
|
BuildContext context,
|
||||||
|
Connection profile,
|
||||||
|
BlocksManager manager,
|
||||||
|
) {
|
||||||
|
late Widget blockToggleButton;
|
||||||
|
switch (profile.status) {
|
||||||
|
case ConnectionStatus.blocked:
|
||||||
|
blockToggleButton = ElevatedButton(
|
||||||
|
onPressed: isUpdating
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
final confirm =
|
||||||
|
await showYesNoDialog(context, 'Unblock ${profile.name}');
|
||||||
|
if (confirm != true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
isUpdating = true;
|
||||||
|
});
|
||||||
|
await manager.unblockConnection(profile);
|
||||||
|
setState(() {
|
||||||
|
isUpdating = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Text('Unblock'),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case ConnectionStatus.mutual:
|
||||||
|
case ConnectionStatus.theyFollowYou:
|
||||||
|
case ConnectionStatus.youFollowThem:
|
||||||
|
case ConnectionStatus.none:
|
||||||
|
case ConnectionStatus.unknown:
|
||||||
|
blockToggleButton = ElevatedButton(
|
||||||
|
onPressed: isUpdating
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
final confirm =
|
||||||
|
await showYesNoDialog(context, 'Block ${profile.name}');
|
||||||
|
if (confirm != true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
isUpdating = true;
|
||||||
|
});
|
||||||
|
await manager.blockConnection(profile);
|
||||||
|
setState(() {
|
||||||
|
isUpdating = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Text('Block'),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case ConnectionStatus.you:
|
||||||
|
blockToggleButton = const SizedBox();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return blockToggleButton;
|
||||||
|
}
|
||||||
|
|
||||||
Widget buildConnectionStatusToggle(
|
Widget buildConnectionStatusToggle(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Connection profile,
|
Connection profile,
|
||||||
|
@ -234,6 +304,7 @@ class _UserProfileScreenState extends State<UserProfileScreen> {
|
||||||
child: const Text('Follow'),
|
child: const Text('Follow'),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case ConnectionStatus.blocked:
|
||||||
case ConnectionStatus.you:
|
case ConnectionStatus.you:
|
||||||
case ConnectionStatus.unknown:
|
case ConnectionStatus.unknown:
|
||||||
followToggleButton = const SizedBox();
|
followToggleButton = const SizedBox();
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:result_monad/result_monad.dart';
|
||||||
|
|
||||||
|
import '../friendica_client/friendica_client.dart';
|
||||||
|
import '../friendica_client/paged_response.dart';
|
||||||
|
import '../friendica_client/paging_data.dart';
|
||||||
|
import '../globals.dart';
|
||||||
|
import '../models/auth/profile.dart';
|
||||||
|
import '../models/connection.dart';
|
||||||
|
import '../utils/active_profile_selector.dart';
|
||||||
|
import 'connections_manager.dart';
|
||||||
|
|
||||||
|
class BlocksManager extends ChangeNotifier {
|
||||||
|
static final _logger = Logger('$BlocksManager');
|
||||||
|
final Profile profile;
|
||||||
|
final _blocks = <Connection>[];
|
||||||
|
final _pages = <PagedResponse>[];
|
||||||
|
|
||||||
|
var mayHaveMore = true;
|
||||||
|
var initialized = false;
|
||||||
|
|
||||||
|
BlocksManager(this.profile);
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
_blocks.clear();
|
||||||
|
_pages.clear();
|
||||||
|
mayHaveMore = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Connection> getBlocks() {
|
||||||
|
if (!initialized) {
|
||||||
|
updateBlocks(nextOnly: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnmodifiableListView(_blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> blockConnection(Connection connection) async {
|
||||||
|
_logger
|
||||||
|
.finest('Attempting to block ${connection.name}: ${connection.status}');
|
||||||
|
await RelationshipsClient(profile)
|
||||||
|
.blockConnection(connection)
|
||||||
|
.withResult((blockedUser) {
|
||||||
|
getIt<ActiveProfileSelector<ConnectionsManager>>()
|
||||||
|
.getForProfile(profile)
|
||||||
|
.withResult(
|
||||||
|
(cm) => cm.upsertConnection(blockedUser),
|
||||||
|
);
|
||||||
|
}).match(
|
||||||
|
onSuccess: (blockedUser) {
|
||||||
|
_logger.finest(
|
||||||
|
'Successfully blocked ${blockedUser.name}: ${blockedUser.status}');
|
||||||
|
final existingIndex = _blocks.indexOf(connection);
|
||||||
|
if (existingIndex < 0) {
|
||||||
|
_blocks.add(blockedUser);
|
||||||
|
_sortBlocks();
|
||||||
|
} else {
|
||||||
|
_blocks.removeAt(existingIndex);
|
||||||
|
_blocks.insert(existingIndex, blockedUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
},
|
||||||
|
onError: (error) {
|
||||||
|
_logger.severe('Error blocking ${connection.name}: $error');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> unblockConnection(Connection connection) async {
|
||||||
|
_logger.finest(
|
||||||
|
'Attempting to unblock ${connection.name}: ${connection.status}');
|
||||||
|
await RelationshipsClient(profile)
|
||||||
|
.unblockConnection(connection)
|
||||||
|
.withResult((blockedUser) {
|
||||||
|
getIt<ActiveProfileSelector<ConnectionsManager>>()
|
||||||
|
.getForProfile(profile)
|
||||||
|
.withResult(
|
||||||
|
(cm) => cm.upsertConnection(blockedUser),
|
||||||
|
);
|
||||||
|
}).match(
|
||||||
|
onSuccess: (unblockedUser) {
|
||||||
|
_logger.finest(
|
||||||
|
'Successfully unblocked ${unblockedUser.name}: ${unblockedUser.status}');
|
||||||
|
final existingIndex = _blocks.indexOf(connection);
|
||||||
|
if (existingIndex >= 0) {
|
||||||
|
_blocks.removeAt(existingIndex);
|
||||||
|
_sortBlocks();
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
},
|
||||||
|
onError: (error) {
|
||||||
|
_logger.severe('Error unblocking ${connection.name}: $error');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateBlock(Connection connection) async {
|
||||||
|
final id = int.parse(connection.id);
|
||||||
|
final page = PagingData(minId: id - 1, maxId: id + 1);
|
||||||
|
await RelationshipsClient(profile).getBlocks(page).withResult((blocks) {
|
||||||
|
final conBlock = blocks.data.where((b) => b.id == connection.id).toList();
|
||||||
|
if (conBlock.isEmpty) {
|
||||||
|
_blocks.remove(connection);
|
||||||
|
} else {
|
||||||
|
_blocks.add(conBlock.first);
|
||||||
|
getIt<ActiveProfileSelector<ConnectionsManager>>()
|
||||||
|
.getForProfile(profile)
|
||||||
|
.withResult((cm) => cm.upsertConnection(conBlock.first));
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateBlocks({required bool nextOnly}) async {
|
||||||
|
if (nextOnly) {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
final client = RelationshipsClient(profile);
|
||||||
|
final bootstrapping = _pages.isEmpty;
|
||||||
|
|
||||||
|
var page = bootstrapping ? PagingData() : _pages.last.next;
|
||||||
|
|
||||||
|
while (page != null) {
|
||||||
|
page = await client
|
||||||
|
.getBlocks(page)
|
||||||
|
.withResult((result) {
|
||||||
|
_blocks.addAll(result.data);
|
||||||
|
_pages.add(result);
|
||||||
|
})
|
||||||
|
.withError(
|
||||||
|
(error) => _logger.severe('Error getting blocks data: $error'),
|
||||||
|
)
|
||||||
|
.fold<PagingData?>(
|
||||||
|
onSuccess: (result) => result.next,
|
||||||
|
onError: (error) => null,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nextOnly) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getIt<ActiveProfileSelector<ConnectionsManager>>()
|
||||||
|
.getForProfile(profile)
|
||||||
|
.withResult((cm) => cm.upsertAllConnections(_blocks));
|
||||||
|
|
||||||
|
_sortBlocks();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sortBlocks() {
|
||||||
|
_blocks.sort(
|
||||||
|
(b1, b2) => b1.name.toLowerCase().compareTo(
|
||||||
|
b2.name.toLowerCase(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -357,8 +357,8 @@ class ConnectionsManager extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Connection, ExecError> getById(String id, {bool forceUpdate = false}) {
|
Result<Connection, ExecError> getById(String id, {bool forceUpdate = false}) {
|
||||||
return conRepo.getById(id).andThenSuccess((c) {
|
return conRepo.getById(id).transform((c) {
|
||||||
if (forceUpdate) {
|
if (c.status == ConnectionStatus.unknown && forceUpdate) {
|
||||||
_refreshConnection(c, true);
|
_refreshConnection(c, true);
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
|
@ -383,11 +383,16 @@ class ConnectionsManager extends ChangeNotifier {
|
||||||
}).execErrorCast();
|
}).execErrorCast();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fullRefresh(Connection connection) async {
|
Future<void> fullRefresh(
|
||||||
|
Connection connection, {
|
||||||
|
bool withNotifications = true,
|
||||||
|
}) async {
|
||||||
await _updateMyGroups(false);
|
await _updateMyGroups(false);
|
||||||
await _refreshGroupListData(connection.id, false);
|
await _refreshGroupListData(connection.id, false);
|
||||||
await _refreshConnection(connection, false);
|
await _refreshConnection(connection, false);
|
||||||
notifyListeners();
|
if (withNotifications) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refreshGroupListData(String id, bool withNotification) async {
|
Future<void> _refreshGroupListData(String id, bool withNotification) async {
|
||||||
|
|
|
@ -33,7 +33,7 @@ Future<void> executeUpdatesForProfile(Profile profile) async {
|
||||||
.getForProfile(profile)
|
.getForProfile(profile)
|
||||||
.withResultAsync((info) async {
|
.withResultAsync((info) async {
|
||||||
final dt = DateTime.now().difference(info.lastMyConnectionsUpdate);
|
final dt = DateTime.now().difference(info.lastMyConnectionsUpdate);
|
||||||
_logger.info('Time since last connections update: $dt');
|
_logger.finer('Time since last connections update: $dt');
|
||||||
if (dt >= _connectionsRefreshInterval) {
|
if (dt >= _connectionsRefreshInterval) {
|
||||||
await getIt<ActiveProfileSelector<ConnectionsManager>>()
|
await getIt<ActiveProfileSelector<ConnectionsManager>>()
|
||||||
.getForProfile(profile)
|
.getForProfile(profile)
|
||||||
|
|
Ładowanie…
Reference in New Issue