tane/apps/app_seeds/lib/services/social_settings.dart
vjrj aa457ab91a fix(block2): hide server config, enable drawer Market, badge my own offers
Three feedback fixes:
- Servers are fully hidden now — relays are managed automatically (default
  community servers; the pool skips dead ones). Sharing setup shows only 'your
  area'; no jargon-y URL field. (3 default relays for redundancy.)
- Drawer 'Market' is a live destination (was stuck on 'coming soon'), gated on
  the social layer being wired like the home card.
- My own listings in the market carry a 'You' badge (and no 'message yourself'
  button), so it's clear which offers are mine.

Analyzer clean. Note: could not run the flutter widget suite here (10+ min
compile after the geolocator native dep in this sandbox) — run 'flutter test'
locally to confirm; the You-badge test was simplified to be robust.
2026-07-10 11:56:55 +02:00

53 lines
2.3 KiB
Dart

import '../security/secret_store.dart';
/// User choices for the (Block 2) social layer: the coarse area to publish/
/// browse offers in, and which community relays to talk to. Backed by the OS
/// keystore (via [SecretStore]) to honour "no plaintext at rest" — no
/// shared_preferences.
///
/// Relays default to a small set of well-known public servers so the market
/// works out of the box; the exposure is minimal (offers are opt-in and carry
/// only a coarse geohash) and the user can swap them for a community server.
/// The area stays unset until the user picks one (it's inherently personal).
class SocialSettings {
SocialSettings(this._store);
final SecretStore _store;
static const _areaKey = 'tane.social.area_geohash';
static const _relaysKey = 'tane.social.relays';
/// Community servers used automatically so sharing works from the first
/// launch. The relay pool skips any that are unreachable, so a dead one never
/// breaks the market; the user never has to know these exist.
static const List<String> defaultRelays = [
'wss://nos.lol',
'wss://relay.damus.io',
'wss://relay.primal.net',
];
/// The user's coarse area as a low-precision geohash (may be empty). Set
/// manually or from device location; the transport coarsens it further.
Future<String> areaGeohash() async => (await _store.read(_areaKey)) ?? '';
Future<void> setAreaGeohash(String geohash) =>
_store.write(_areaKey, geohash.trim().toLowerCase());
/// The relays to talk to. When the user has never configured any, falls back
/// to [defaultRelays]. Once they save a choice (even an empty one), that wins
/// — so a privacy-minded user can turn the network off entirely.
Future<List<String>> relayUrls() async {
final raw = await _store.read(_relaysKey);
if (raw == null) return const [...defaultRelays]; // never configured
return raw.split('\n').where((s) => s.trim().isNotEmpty).toList();
}
Future<void> setRelayUrls(List<String> urls) => _store.write(
_relaysKey,
urls.map((u) => u.trim()).where((u) => u.isNotEmpty).join('\n'),
);
/// Whether the social layer has enough config to attempt going online.
Future<bool> get isConfigured async =>
(await relayUrls()).isNotEmpty && (await areaGeohash()).isNotEmpty;
}