import 'package:logging/logging.dart'; import 'package:result_monad/result_monad.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:stack_trace/stack_trace.dart'; import '../models/auth/profile.dart'; import '../models/connection.dart'; import '../models/exec_error.dart'; import '../models/networking/paged_response.dart'; import '../models/networking/paging_data.dart'; import 'connection_manager_services.dart'; import 'networking/friendica_relationship_client_services.dart'; part 'blocks_services.g.dart'; final _logger = Logger('BlocksManager'); @Riverpod(keepAlive: true) class BlocksManager extends _$BlocksManager { final _pages = []; @override Future> build(Profile profile) async { final result = await updateBlocks(withUpdate: false); return result; } FutureResult blockConnection( Connection connection) async { _logger .fine('Attempting to block ${connection.name}: ${connection.status}'); final blocks = List.from(state.value ?? []); final result = await ref .read(blockConnectionProvider(profile, connection).future) .withResultAsync((blockedUser) async { await ref .read(connectionModifierProvider(profile, blockedUser).notifier) .upsertConnection(blockedUser); }); result.match( onSuccess: (blockedUser) { _logger.fine( 'Successfully blocked ${blockedUser.name}: ${blockedUser.status}'); final existingIndex = blocks.indexOf(connection); if (existingIndex < 0) { blocks.add(blockedUser); _sortBlocks(blocks); } else { blocks.removeAt(existingIndex); blocks.insert(existingIndex, blockedUser); } }, onError: (error) { _logger.severe( 'Error blocking ${connection.name}: $error', Trace.current()); }, ); state = AsyncData(blocks); return result.execErrorCast(); } FutureResult unblockConnection( Connection connection) async { _logger .fine('Attempting to unblock ${connection.name}: ${connection.status}'); final blocks = List.from(state.value ?? []); final result = await ref .read(unblockConnectionProvider(profile, connection).future) .withResultAsync((unblocked) async { await ref .read(connectionModifierProvider(profile, unblocked).notifier) .upsertConnection(unblocked); }); result.match( onSuccess: (unblockedUser) { _logger.fine( 'Successfully unblocked ${unblockedUser.name}: ${unblockedUser.status}'); final existingIndex = blocks.indexOf(connection); if (existingIndex >= 0) { blocks.removeAt(existingIndex); _sortBlocks(blocks); } }, onError: (error) { _logger.severe('Error unblocking ${connection.name}: $error'); }, ); state = AsyncData(blocks); return result.execErrorCast(); } Future updateBlock(Connection connection) async { final blocks = List.from(state.value ?? []); final id = int.parse(connection.id); final page = PagingData(minId: id - 1, maxId: id + 1); await ref .read(blocksClientProvider(profile, page).future) .withResultAsync((returnedBlocks) async { final conBlock = returnedBlocks.data.where((b) => b.id == connection.id).toList(); if (conBlock.isEmpty) { blocks.remove(connection); await ref .read(connectionModifierProvider(profile, connection).notifier) .refreshConnection(true); } else { blocks.add(conBlock.first); await ref .read(connectionModifierProvider(profile, conBlock.first).notifier) .upsertConnection(conBlock.first); } }); state = AsyncData(blocks); } Future> updateBlocks({ bool nextOnly = false, bool withUpdate = true, }) async { final blocks = state.value ?? []; if (!nextOnly) { blocks.clear(); _pages.clear(); } final bootstrapping = _pages.isEmpty; var page = bootstrapping ? PagingData() : _pages.last.next; while (page != null) { page = await ref .read(blocksClientProvider(profile, page).future) .withResult((result) { blocks.addAll(result.data); _pages.add(result); }) .withError( (error) => _logger.severe('Error getting blocks data: $error'), ) .fold( onSuccess: (result) => result.next, onError: (error) => null, ); if (nextOnly) { break; } } for (final b in blocks) { await ref .read(connectionModifierProvider(profile, b).notifier) .upsertConnection(b); } _sortBlocks(blocks); if (withUpdate) { state = AsyncData(blocks); } return blocks; } } void _sortBlocks(List blocks) { blocks.sort( (b1, b2) => b1.name.toLowerCase().compareTo( b2.name.toLowerCase(), ), ); }