feat(social): local blocklist — hide blocked authors' offers, chats and incoming messages, with unblock management

This commit is contained in:
vjrj 2026-07-13 01:03:10 +02:00
parent cd9f09048d
commit b41dfd4248
13 changed files with 399 additions and 4 deletions

View file

@ -13,6 +13,7 @@ import '../services/social_service.dart';
import '../services/social_settings.dart';
import '../services/onboarding_store.dart';
import '../state/offers_cubit.dart';
import 'blocked_people.dart';
import 'edge_fade.dart';
import 'filter_chips.dart';
import 'market_gate.dart';
@ -92,6 +93,7 @@ class _MarketScreenState extends State<MarketScreen> {
setState(() => _loading = true);
final cubit =
await createOffersCubit(widget.connection, repository: repo);
cubit.setBlockedAuthors(await widget.settings.blockedPubkeys());
final area = await widget.settings.areaGeohash();
if (!mounted) {
await cubit.close();
@ -123,6 +125,12 @@ class _MarketScreenState extends State<MarketScreen> {
}
}
/// Re-reads the blocklist (the offer detail can block an author) so their
/// offers drop out of the visible list at once.
Future<void> _reloadBlocked() async {
_cubit?.setBlockedAuthors(await widget.settings.blockedPubkeys());
}
Future<void> _openConfig() async {
final changed = await showModalBottomSheet<bool>(
context: context,
@ -227,6 +235,7 @@ class _MarketScreenState extends State<MarketScreen> {
onRetry: _init,
hasArea: _hasArea,
selfPubkey: widget.social.publicKeyHex,
onOfferClosed: _reloadBlocked,
),
),
);
@ -239,6 +248,7 @@ class MarketBody extends StatelessWidget {
this.onRetry,
this.hasArea = true,
this.selfPubkey,
this.onOfferClosed,
super.key,
});
@ -253,6 +263,10 @@ class MarketBody extends StatelessWidget {
/// This user's own pubkey, so we don't offer to message our own listings.
final String? selfPubkey;
/// Called after coming back from an offer's detail (which can block its
/// author), so the visible list refreshes against the blocklist.
final Future<void> Function()? onOfferClosed;
@override
Widget build(BuildContext context) {
final t = context.t;
@ -371,8 +385,10 @@ class MarketBody extends StatelessWidget {
return _OfferCard(
offer: o,
mine: mine,
onTap: () =>
context.push('/market/offer', extra: o),
onTap: () async {
await context.push('/market/offer', extra: o);
await onOfferClosed?.call();
},
);
},
),
@ -826,6 +842,19 @@ class _ConfigSheetState extends State<_ConfigSheet> {
helperMaxLines: 2,
),
),
ListTile(
key: const Key('market.blockedPeople'),
contentPadding: EdgeInsets.zero,
leading: const Icon(Icons.block, color: seedMuted),
title: Text(t.block.manageTitle),
trailing: const Icon(Icons.chevron_right),
onTap: () => showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
builder: (_) =>
BlockedPeopleSheet(settings: widget.settings),
),
),
],
),
),