refactor(trust): ego-centric trust replaces the global Duniter membership

The global membership rule (curated bootstrap referents + sigQty/stepMax
parameters) solved sybil-proof identity for a UBI — a problem this app
doesn't have — and its screen leaked graph jargon (npub roots, parameter
steppers). Trust is now computed from the user's own position only:
you vouch / vouched by people you know (distance <=2) / vouched by N.

- TrustCubit: drop networkMember tier, referents and params; keep the
  circle rule and the 365-day vouch expiry (renewable, self-pruning).
- Delete TrustReferents, WotSettings, TrustNetworkScreen and the bundled
  referents asset; unwire injector/bootstrap/app/chat.
- New 'Your people' screen (/your-people, from the profile): who you
  vouch for (revocable) and who vouches for you, names via ProfileCache.
- i18n: wot.* removed, yourPeople.* added (en/es/pt/ast); trust.member
  removed. Kind 30777 events on relays stay fully compatible — only the
  interpretation changes.
This commit is contained in:
vjrj 2026-07-11 13:03:20 +02:00
parent 4af90876f4
commit dc7b2eee27
25 changed files with 595 additions and 863 deletions

View file

@ -13,9 +13,7 @@ import '../services/message_store.dart';
import '../services/profile_cache.dart';
import '../services/social_connection.dart';
import '../services/social_service.dart';
import '../services/trust_referents.dart';
import '../services/unread_service.dart';
import '../services/wot_settings.dart';
import '../state/messages_cubit.dart';
import '../state/trust_cubit.dart';
import 'peer_avatar.dart';
@ -31,8 +29,6 @@ class ChatScreen extends StatefulWidget {
required this.peerPubkey,
this.messageStore,
this.profileCache,
this.trustReferents,
this.wotSettings,
super.key,
});
@ -49,10 +45,6 @@ class ChatScreen extends StatefulWidget {
/// Optional cache of peer display names; null in tests.
final ProfileCache? profileCache;
/// Web-of-trust bootstrap referents + parameters, for the membership verdict.
final TrustReferents? trustReferents;
final WotSettings? wotSettings;
@override
State<ChatScreen> createState() => _ChatScreenState();
}
@ -88,8 +80,6 @@ class _ChatScreenState extends State<ChatScreen> {
// connected / unreachable) null transports and the screen degrades.
final session = await widget.connection.session();
final cachedName = await widget.profileCache?.name(widget.peerPubkey);
final referents = await widget.trustReferents?.all() ?? const <String>{};
final wotParams = await widget.wotSettings?.params() ?? WotParams.duniter;
if (!mounted) return; // shared session is owned by the connection, not us
final self = widget.social.publicKeyHex;
final messages = MessagesCubit(
@ -102,8 +92,6 @@ class _ChatScreenState extends State<ChatScreen> {
session?.trust,
peerPubkey: widget.peerPubkey,
selfPubkey: self,
referents: referents,
params: wotParams,
);
unawaited(trust.load());
setState(() {
@ -323,10 +311,9 @@ class _TrustBanner extends StatelessWidget {
builder: (context, state) {
final cubit = context.read<TrustCubit>();
if (!cubit.isOnline || state.loading) return const SizedBox.shrink();
// Strongest applicable signal decides the badge (member > circle >
// vouched > unknown).
// Strongest applicable signal decides the badge (circle > vouched >
// unknown).
final (IconData icon, String label, bool strong) = switch (state.tier) {
TrustTier.networkMember => (Icons.verified, t.trust.member, true),
TrustTier.inYourCircle => (Icons.verified_user, t.trust.circle, true),
TrustTier.vouched => (
Icons.people_outline,