feat(block2): trust UI — vouch for a person in chat (web of trust)
- 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.
This commit is contained in:
parent
efd64c4da4
commit
a85e71dc8d
10 changed files with 367 additions and 17 deletions
82
apps/app_seeds/test/state/trust_cubit_test.dart
Normal file
82
apps/app_seeds/test/state/trust_cubit_test.dart
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import 'package:commons_core/commons_core.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:tane/state/trust_cubit.dart';
|
||||
|
||||
/// In-memory [TrustTransport]: certify/revoke act as this device's identity.
|
||||
class FakeTrustTransport implements TrustTransport {
|
||||
FakeTrustTransport(this.selfId);
|
||||
final String selfId;
|
||||
final Map<String, Set<String>> _certs = {};
|
||||
|
||||
@override
|
||||
Future<Set<String>> certifiersOf(String subjectPubkey) async =>
|
||||
_certs[subjectPubkey] ?? <String>{};
|
||||
|
||||
@override
|
||||
Future<void> certify({
|
||||
required String subjectPubkey,
|
||||
Duration validity = const Duration(days: 365),
|
||||
String note = '',
|
||||
}) async =>
|
||||
(_certs[subjectPubkey] ??= {}).add(selfId);
|
||||
|
||||
@override
|
||||
Future<void> revoke({required String subjectPubkey}) async =>
|
||||
_certs[subjectPubkey]?.remove(selfId);
|
||||
|
||||
@override
|
||||
Future<List<Certification>> allCertifications() async => const [];
|
||||
|
||||
@override
|
||||
Future<void> close() async {}
|
||||
}
|
||||
|
||||
void main() {
|
||||
const me = 'me';
|
||||
const peer = 'peer';
|
||||
|
||||
test('loads certifier count and whether I vouch', () async {
|
||||
final transport = FakeTrustTransport(me);
|
||||
await transport.certify(subjectPubkey: peer); // I already vouch
|
||||
final cubit = TrustCubit(transport, peerPubkey: peer, selfPubkey: me);
|
||||
await cubit.load();
|
||||
expect(cubit.state.certifierCount, 1);
|
||||
expect(cubit.state.iVouch, isTrue);
|
||||
expect(cubit.state.loading, isFalse);
|
||||
await cubit.close();
|
||||
});
|
||||
|
||||
test('toggleVouch certifies then revokes', () async {
|
||||
final cubit =
|
||||
TrustCubit(FakeTrustTransport(me), peerPubkey: peer, selfPubkey: me);
|
||||
await cubit.load();
|
||||
expect(cubit.state.iVouch, isFalse);
|
||||
|
||||
await cubit.toggleVouch();
|
||||
expect(cubit.state.iVouch, isTrue);
|
||||
expect(cubit.state.certifierCount, 1);
|
||||
|
||||
await cubit.toggleVouch();
|
||||
expect(cubit.state.iVouch, isFalse);
|
||||
expect(cubit.state.certifierCount, 0);
|
||||
await cubit.close();
|
||||
});
|
||||
|
||||
test('never vouches for self', () async {
|
||||
final cubit =
|
||||
TrustCubit(FakeTrustTransport(me), peerPubkey: me, selfPubkey: me);
|
||||
await cubit.load();
|
||||
await cubit.toggleVouch();
|
||||
expect(cubit.state.iVouch, isFalse);
|
||||
await cubit.close();
|
||||
});
|
||||
|
||||
test('offline (no transport) never throws', () async {
|
||||
final cubit = TrustCubit(null, peerPubkey: peer, selfPubkey: me);
|
||||
expect(cubit.isOnline, isFalse);
|
||||
await cubit.load();
|
||||
await cubit.toggleVouch();
|
||||
expect(cubit.state.loading, isFalse);
|
||||
await cubit.close();
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue