- TrustCubit: reads how many vouch for the peer + whether YOU do; toggleVouch certifies/revokes over TrustTransport; never vouches for self; offline-safe. Kept simple — the full known-member rule (threshold+distance) and its bootstrap set are still-open policy (network-trust §2); a live count + your vouch is the honest first step. - Chat screen now opens ONE session carrying both messaging AND trust (the shared-connection shape), and shows a slim vouch banner: 'Vouched for by N' + an 'I know this person' / 'You vouch for them' toggle. - i18n en/es/pt (human words: 'I know this person', not 'certify'). Tests: 4 trust cubit + messaging + chat, 10 green; analyzer clean.
121 lines
3.4 KiB
Dart
121 lines
3.4 KiB
Dart
import 'package:commons_core/commons_core.dart';
|
|
import 'package:equatable/equatable.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
import '../services/social_service.dart';
|
|
import '../services/social_settings.dart';
|
|
|
|
/// Trust standing of one peer: how many people vouch for them, and whether YOU
|
|
/// do. Kept simple on purpose — the full "known member" rule (threshold +
|
|
/// distance) and its bootstrap set are a policy decision still open
|
|
/// (network-trust.md §2). Vouching + a live count is the useful, honest first
|
|
/// step.
|
|
class TrustState extends Equatable {
|
|
const TrustState({
|
|
this.certifierCount = 0,
|
|
this.iVouch = false,
|
|
this.loading = true,
|
|
this.busy = false,
|
|
});
|
|
|
|
final int certifierCount;
|
|
final bool iVouch;
|
|
final bool loading;
|
|
final bool busy;
|
|
|
|
TrustState copyWith({
|
|
int? certifierCount,
|
|
bool? iVouch,
|
|
bool? loading,
|
|
bool? busy,
|
|
}) =>
|
|
TrustState(
|
|
certifierCount: certifierCount ?? this.certifierCount,
|
|
iVouch: iVouch ?? this.iVouch,
|
|
loading: loading ?? this.loading,
|
|
busy: busy ?? this.busy,
|
|
);
|
|
|
|
@override
|
|
List<Object?> get props => [certifierCount, iVouch, loading, busy];
|
|
}
|
|
|
|
/// Reads and toggles this user's vouch for [peerPubkey] over a [TrustTransport].
|
|
class TrustCubit extends Cubit<TrustState> {
|
|
TrustCubit(
|
|
this._transport, {
|
|
required this.peerPubkey,
|
|
required this.selfPubkey,
|
|
Future<void> Function()? onDispose,
|
|
}) : _onDispose = onDispose,
|
|
super(const TrustState());
|
|
|
|
final TrustTransport? _transport;
|
|
final String peerPubkey;
|
|
final String selfPubkey;
|
|
final Future<void> Function()? _onDispose;
|
|
|
|
bool get isOnline => _transport != null;
|
|
|
|
/// Loads the current certifiers of the peer.
|
|
Future<void> load() async {
|
|
final transport = _transport;
|
|
if (transport == null) {
|
|
emit(state.copyWith(loading: false));
|
|
return;
|
|
}
|
|
emit(state.copyWith(loading: true));
|
|
final certifiers = await transport.certifiersOf(peerPubkey);
|
|
emit(state.copyWith(
|
|
certifierCount: certifiers.length,
|
|
iVouch: certifiers.contains(selfPubkey),
|
|
loading: false,
|
|
));
|
|
}
|
|
|
|
/// Adds or removes this user's vouch, then reloads. Never vouches for self.
|
|
Future<void> toggleVouch() async {
|
|
final transport = _transport;
|
|
if (transport == null || peerPubkey == selfPubkey) return;
|
|
emit(state.copyWith(busy: true));
|
|
if (state.iVouch) {
|
|
await transport.revoke(subjectPubkey: peerPubkey);
|
|
} else {
|
|
await transport.certify(subjectPubkey: peerPubkey);
|
|
}
|
|
await load();
|
|
emit(state.copyWith(busy: false));
|
|
}
|
|
|
|
@override
|
|
Future<void> close() async {
|
|
await _onDispose?.call();
|
|
return super.close();
|
|
}
|
|
}
|
|
|
|
/// Opens a [TrustCubit] wired to the social layer for [peerPubkey], or an
|
|
/// offline one.
|
|
Future<TrustCubit> createTrustCubit(
|
|
SocialService social,
|
|
SocialSettings settings, {
|
|
required String peerPubkey,
|
|
}) async {
|
|
final relays = await settings.relayUrls();
|
|
if (relays.isEmpty) {
|
|
return TrustCubit(null,
|
|
peerPubkey: peerPubkey, selfPubkey: social.publicKeyHex);
|
|
}
|
|
try {
|
|
final session = await social.openSession(relays);
|
|
return TrustCubit(
|
|
session.trust,
|
|
peerPubkey: peerPubkey,
|
|
selfPubkey: social.publicKeyHex,
|
|
onDispose: session.close,
|
|
);
|
|
} catch (_) {
|
|
return TrustCubit(null,
|
|
peerPubkey: peerPubkey, selfPubkey: social.publicKeyHex);
|
|
}
|
|
}
|