feat(block2): show contact names (resolve peers' published profiles)

Closes the profile loop — peers now appear by name, not a raw key.
- ProfileCache: keystore-backed cache of peers' published display names (works
  offline) + shortPubkey() fallback.
- Chat: the app bar shows the peer's name (cached first, then freshened from
  their kind:0 over the session); falls back to a short key.
- Inbox: each conversation shows the cached name, and missing names are resolved
  over one session and cached.
- Wired via DI + TaneApp(profileCache).

Analyzer clean; ProfileCache covered by a plain test.
This commit is contained in:
vjrj 2026-07-10 12:47:48 +02:00
parent b96b6e45cc
commit 9cc1a5e171
7 changed files with 149 additions and 13 deletions

View file

@ -12,6 +12,7 @@ import 'services/coarse_location.dart';
import 'services/message_store.dart';
import 'services/offer_outbox.dart';
import 'services/onboarding_store.dart';
import 'services/profile_cache.dart';
import 'services/profile_store.dart';
import 'services/social_service.dart';
import 'services/social_settings.dart';
@ -44,6 +45,7 @@ class TaneApp extends StatelessWidget {
this.outbox,
this.messageStore,
this.profileStore,
this.profileCache,
this.showIntro = false,
this.autoBackup,
super.key,
@ -57,6 +59,7 @@ class TaneApp extends StatelessWidget {
outbox,
messageStore,
profileStore,
profileCache,
);
final VarietyRepository repository;
@ -79,6 +82,9 @@ class TaneApp extends StatelessWidget {
/// Optional local store for your own profile (name/about).
final ProfileStore? profileStore;
/// Optional cache of peers' published display names.
final ProfileCache? profileCache;
final bool showIntro;
/// Drives silent periodic backups off the app lifecycle. Null in widget tests
@ -96,6 +102,7 @@ class TaneApp extends StatelessWidget {
OfferOutbox? outbox,
MessageStore? messageStore,
ProfileStore? profileStore,
ProfileCache? profileCache,
) {
return GoRouter(
initialLocation: showIntro ? '/intro' : '/',
@ -118,7 +125,12 @@ class TaneApp extends StatelessWidget {
if (social != null && socialSettings != null && messageStore != null)
GoRoute(
path: '/messages',
builder: (context, state) => ChatListScreen(store: messageStore),
builder: (context, state) => ChatListScreen(
store: messageStore,
social: social,
settings: socialSettings,
profileCache: profileCache,
),
),
if (social != null && socialSettings != null && profileStore != null)
GoRoute(
@ -137,6 +149,7 @@ class TaneApp extends StatelessWidget {
settings: socialSettings,
peerPubkey: state.pathParameters['pubkey']!,
messageStore: messageStore,
profileCache: profileCache,
),
),
GoRoute(