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.
This commit is contained in:
parent
68b336ec93
commit
aa457ab91a
12 changed files with 92 additions and 108 deletions
|
|
@ -343,8 +343,9 @@
|
||||||
"empty": "No seeds shared near you yet",
|
"empty": "No seeds shared near you yet",
|
||||||
"near": "Near you",
|
"near": "Near you",
|
||||||
"contact": "Message",
|
"contact": "Message",
|
||||||
|
"mine": "You",
|
||||||
"configTitle": "Sharing setup",
|
"configTitle": "Sharing setup",
|
||||||
"setupIntro": "Sharing with people nearby is optional. Just set your rough area — you're already connected to shared community servers so people can find what you offer, with no company in the middle. You can change the servers below.",
|
"setupIntro": "Sharing with people nearby is optional. Just set your rough area — you're already connected to shared community servers so people can find what you offer, with no company in the middle.",
|
||||||
"areaLabel": "Your area",
|
"areaLabel": "Your area",
|
||||||
"areaHelp": "Kept rough on purpose — your zone, never an exact spot.",
|
"areaHelp": "Kept rough on purpose — your zone, never an exact spot.",
|
||||||
"serversLabel": "Community servers",
|
"serversLabel": "Community servers",
|
||||||
|
|
|
||||||
|
|
@ -343,8 +343,9 @@
|
||||||
"empty": "Aún no hay semillas compartidas cerca de ti",
|
"empty": "Aún no hay semillas compartidas cerca de ti",
|
||||||
"near": "Cerca de ti",
|
"near": "Cerca de ti",
|
||||||
"contact": "Mensaje",
|
"contact": "Mensaje",
|
||||||
|
"mine": "Tú",
|
||||||
"configTitle": "Configuración de compartir",
|
"configTitle": "Configuración de compartir",
|
||||||
"setupIntro": "Compartir con gente cercana es opcional. Solo indica tu zona aproximada — ya estás conectado a servidores comunitarios compartidos para que otras personas encuentren lo que ofreces, sin ninguna empresa en medio. Puedes cambiar los servidores abajo.",
|
"setupIntro": "Compartir con gente cercana es opcional. Solo indica tu zona aproximada — ya estás conectado a servidores comunitarios compartidos para que otras personas encuentren lo que ofreces, sin ninguna empresa en medio.",
|
||||||
"areaLabel": "Tu zona",
|
"areaLabel": "Tu zona",
|
||||||
"areaHelp": "Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.",
|
"areaHelp": "Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.",
|
||||||
"serversLabel": "Servidores de la comunidad",
|
"serversLabel": "Servidores de la comunidad",
|
||||||
|
|
|
||||||
|
|
@ -339,8 +339,9 @@
|
||||||
"empty": "Ainda não há sementes partilhadas perto de ti",
|
"empty": "Ainda não há sementes partilhadas perto de ti",
|
||||||
"near": "Perto de ti",
|
"near": "Perto de ti",
|
||||||
"contact": "Mensagem",
|
"contact": "Mensagem",
|
||||||
|
"mine": "Tu",
|
||||||
"configTitle": "Configuração de partilha",
|
"configTitle": "Configuração de partilha",
|
||||||
"setupIntro": "Partilhar com pessoas por perto é opcional. Basta indicar a tua zona aproximada — já estás ligado a servidores comunitários partilhados para que outras pessoas encontrem o que ofereces, sem nenhuma empresa no meio. Podes mudar os servidores abaixo.",
|
"setupIntro": "Partilhar com pessoas por perto é opcional. Basta indicar a tua zona aproximada — já estás ligado a servidores comunitários partilhados para que outras pessoas encontrem o que ofereces, sem nenhuma empresa no meio.",
|
||||||
"areaLabel": "A tua zona",
|
"areaLabel": "A tua zona",
|
||||||
"areaHelp": "Mantém-se aproximado de propósito — a tua zona, nunca um ponto exato.",
|
"areaHelp": "Mantém-se aproximado de propósito — a tua zona, nunca um ponto exato.",
|
||||||
"serversLabel": "Servidores da comunidade",
|
"serversLabel": "Servidores da comunidade",
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
/// To regenerate, run: `dart run slang`
|
/// To regenerate, run: `dart run slang`
|
||||||
///
|
///
|
||||||
/// Locales: 3
|
/// Locales: 3
|
||||||
/// Strings: 986 (328 per locale)
|
/// Strings: 989 (329 per locale)
|
||||||
///
|
///
|
||||||
/// Built on 2026-07-10 at 09:19 UTC
|
/// Built on 2026-07-10 at 09:34 UTC
|
||||||
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint, unused_import
|
// ignore_for_file: type=lint, unused_import
|
||||||
|
|
|
||||||
|
|
@ -1090,11 +1090,14 @@ class Translations$market$en {
|
||||||
/// en: 'Message'
|
/// en: 'Message'
|
||||||
String get contact => 'Message';
|
String get contact => 'Message';
|
||||||
|
|
||||||
|
/// en: 'You'
|
||||||
|
String get mine => 'You';
|
||||||
|
|
||||||
/// en: 'Sharing setup'
|
/// en: 'Sharing setup'
|
||||||
String get configTitle => 'Sharing setup';
|
String get configTitle => 'Sharing setup';
|
||||||
|
|
||||||
/// en: 'Sharing with people nearby is optional. Just set your rough area — you're already connected to shared community servers so people can find what you offer, with no company in the middle. You can change the servers below.'
|
/// en: 'Sharing with people nearby is optional. Just set your rough area — you're already connected to shared community servers so people can find what you offer, with no company in the middle.'
|
||||||
String get setupIntro => 'Sharing with people nearby is optional. Just set your rough area — you\'re already connected to shared community servers so people can find what you offer, with no company in the middle. You can change the servers below.';
|
String get setupIntro => 'Sharing with people nearby is optional. Just set your rough area — you\'re already connected to shared community servers so people can find what you offer, with no company in the middle.';
|
||||||
|
|
||||||
/// en: 'Your area'
|
/// en: 'Your area'
|
||||||
String get areaLabel => 'Your area';
|
String get areaLabel => 'Your area';
|
||||||
|
|
@ -1946,8 +1949,9 @@ extension on Translations {
|
||||||
'market.empty' => 'No seeds shared near you yet',
|
'market.empty' => 'No seeds shared near you yet',
|
||||||
'market.near' => 'Near you',
|
'market.near' => 'Near you',
|
||||||
'market.contact' => 'Message',
|
'market.contact' => 'Message',
|
||||||
|
'market.mine' => 'You',
|
||||||
'market.configTitle' => 'Sharing setup',
|
'market.configTitle' => 'Sharing setup',
|
||||||
'market.setupIntro' => 'Sharing with people nearby is optional. Just set your rough area — you\'re already connected to shared community servers so people can find what you offer, with no company in the middle. You can change the servers below.',
|
'market.setupIntro' => 'Sharing with people nearby is optional. Just set your rough area — you\'re already connected to shared community servers so people can find what you offer, with no company in the middle.',
|
||||||
'market.areaLabel' => 'Your area',
|
'market.areaLabel' => 'Your area',
|
||||||
'market.areaHelp' => 'Kept rough on purpose — your zone, never an exact spot.',
|
'market.areaHelp' => 'Kept rough on purpose — your zone, never an exact spot.',
|
||||||
'market.serversLabel' => 'Community servers',
|
'market.serversLabel' => 'Community servers',
|
||||||
|
|
|
||||||
|
|
@ -615,8 +615,9 @@ class _Translations$market$es extends Translations$market$en {
|
||||||
@override String get empty => 'Aún no hay semillas compartidas cerca de ti';
|
@override String get empty => 'Aún no hay semillas compartidas cerca de ti';
|
||||||
@override String get near => 'Cerca de ti';
|
@override String get near => 'Cerca de ti';
|
||||||
@override String get contact => 'Mensaje';
|
@override String get contact => 'Mensaje';
|
||||||
|
@override String get mine => 'Tú';
|
||||||
@override String get configTitle => 'Configuración de compartir';
|
@override String get configTitle => 'Configuración de compartir';
|
||||||
@override String get setupIntro => 'Compartir con gente cercana es opcional. Solo indica tu zona aproximada — ya estás conectado a servidores comunitarios compartidos para que otras personas encuentren lo que ofreces, sin ninguna empresa en medio. Puedes cambiar los servidores abajo.';
|
@override String get setupIntro => 'Compartir con gente cercana es opcional. Solo indica tu zona aproximada — ya estás conectado a servidores comunitarios compartidos para que otras personas encuentren lo que ofreces, sin ninguna empresa en medio.';
|
||||||
@override String get areaLabel => 'Tu zona';
|
@override String get areaLabel => 'Tu zona';
|
||||||
@override String get areaHelp => 'Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.';
|
@override String get areaHelp => 'Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.';
|
||||||
@override String get serversLabel => 'Servidores de la comunidad';
|
@override String get serversLabel => 'Servidores de la comunidad';
|
||||||
|
|
@ -1306,8 +1307,9 @@ extension on TranslationsEs {
|
||||||
'market.empty' => 'Aún no hay semillas compartidas cerca de ti',
|
'market.empty' => 'Aún no hay semillas compartidas cerca de ti',
|
||||||
'market.near' => 'Cerca de ti',
|
'market.near' => 'Cerca de ti',
|
||||||
'market.contact' => 'Mensaje',
|
'market.contact' => 'Mensaje',
|
||||||
|
'market.mine' => 'Tú',
|
||||||
'market.configTitle' => 'Configuración de compartir',
|
'market.configTitle' => 'Configuración de compartir',
|
||||||
'market.setupIntro' => 'Compartir con gente cercana es opcional. Solo indica tu zona aproximada — ya estás conectado a servidores comunitarios compartidos para que otras personas encuentren lo que ofreces, sin ninguna empresa en medio. Puedes cambiar los servidores abajo.',
|
'market.setupIntro' => 'Compartir con gente cercana es opcional. Solo indica tu zona aproximada — ya estás conectado a servidores comunitarios compartidos para que otras personas encuentren lo que ofreces, sin ninguna empresa en medio.',
|
||||||
'market.areaLabel' => 'Tu zona',
|
'market.areaLabel' => 'Tu zona',
|
||||||
'market.areaHelp' => 'Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.',
|
'market.areaHelp' => 'Se mantiene aproximado a propósito — tu zona, nunca un punto exacto.',
|
||||||
'market.serversLabel' => 'Servidores de la comunidad',
|
'market.serversLabel' => 'Servidores de la comunidad',
|
||||||
|
|
|
||||||
|
|
@ -611,8 +611,9 @@ class _Translations$market$pt extends Translations$market$en {
|
||||||
@override String get empty => 'Ainda não há sementes partilhadas perto de ti';
|
@override String get empty => 'Ainda não há sementes partilhadas perto de ti';
|
||||||
@override String get near => 'Perto de ti';
|
@override String get near => 'Perto de ti';
|
||||||
@override String get contact => 'Mensagem';
|
@override String get contact => 'Mensagem';
|
||||||
|
@override String get mine => 'Tu';
|
||||||
@override String get configTitle => 'Configuração de partilha';
|
@override String get configTitle => 'Configuração de partilha';
|
||||||
@override String get setupIntro => 'Partilhar com pessoas por perto é opcional. Basta indicar a tua zona aproximada — já estás ligado a servidores comunitários partilhados para que outras pessoas encontrem o que ofereces, sem nenhuma empresa no meio. Podes mudar os servidores abaixo.';
|
@override String get setupIntro => 'Partilhar com pessoas por perto é opcional. Basta indicar a tua zona aproximada — já estás ligado a servidores comunitários partilhados para que outras pessoas encontrem o que ofereces, sem nenhuma empresa no meio.';
|
||||||
@override String get areaLabel => 'A tua zona';
|
@override String get areaLabel => 'A tua zona';
|
||||||
@override String get areaHelp => 'Mantém-se aproximado de propósito — a tua zona, nunca um ponto exato.';
|
@override String get areaHelp => 'Mantém-se aproximado de propósito — a tua zona, nunca um ponto exato.';
|
||||||
@override String get serversLabel => 'Servidores da comunidade';
|
@override String get serversLabel => 'Servidores da comunidade';
|
||||||
|
|
@ -1298,8 +1299,9 @@ extension on TranslationsPt {
|
||||||
'market.empty' => 'Ainda não há sementes partilhadas perto de ti',
|
'market.empty' => 'Ainda não há sementes partilhadas perto de ti',
|
||||||
'market.near' => 'Perto de ti',
|
'market.near' => 'Perto de ti',
|
||||||
'market.contact' => 'Mensagem',
|
'market.contact' => 'Mensagem',
|
||||||
|
'market.mine' => 'Tu',
|
||||||
'market.configTitle' => 'Configuração de partilha',
|
'market.configTitle' => 'Configuração de partilha',
|
||||||
'market.setupIntro' => 'Partilhar com pessoas por perto é opcional. Basta indicar a tua zona aproximada — já estás ligado a servidores comunitários partilhados para que outras pessoas encontrem o que ofereces, sem nenhuma empresa no meio. Podes mudar os servidores abaixo.',
|
'market.setupIntro' => 'Partilhar com pessoas por perto é opcional. Basta indicar a tua zona aproximada — já estás ligado a servidores comunitários partilhados para que outras pessoas encontrem o que ofereces, sem nenhuma empresa no meio.',
|
||||||
'market.areaLabel' => 'A tua zona',
|
'market.areaLabel' => 'A tua zona',
|
||||||
'market.areaHelp' => 'Mantém-se aproximado de propósito — a tua zona, nunca um ponto exato.',
|
'market.areaHelp' => 'Mantém-se aproximado de propósito — a tua zona, nunca um ponto exato.',
|
||||||
'market.serversLabel' => 'Servidores da comunidade',
|
'market.serversLabel' => 'Servidores da comunidade',
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,5 @@
|
||||||
import '../security/secret_store.dart';
|
import '../security/secret_store.dart';
|
||||||
|
|
||||||
/// A suggested community server the user can join with one tap.
|
|
||||||
class RelayPreset {
|
|
||||||
const RelayPreset(this.name, this.url);
|
|
||||||
final String name;
|
|
||||||
final String url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// User choices for the (Block 2) social layer: the coarse area to publish/
|
/// 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
|
/// browse offers in, and which community relays to talk to. Backed by the OS
|
||||||
/// keystore (via [SecretStore]) to honour "no plaintext at rest" — no
|
/// keystore (via [SecretStore]) to honour "no plaintext at rest" — no
|
||||||
|
|
@ -24,17 +17,15 @@ class SocialSettings {
|
||||||
static const _areaKey = 'tane.social.area_geohash';
|
static const _areaKey = 'tane.social.area_geohash';
|
||||||
static const _relaysKey = 'tane.social.relays';
|
static const _relaysKey = 'tane.social.relays';
|
||||||
|
|
||||||
/// Suggested servers shown as one-tap options in the sharing setup.
|
/// Community servers used automatically so sharing works from the first
|
||||||
static const List<RelayPreset> presets = [
|
/// launch. The relay pool skips any that are unreachable, so a dead one never
|
||||||
RelayPreset('nos.lol', 'wss://nos.lol'),
|
/// breaks the market; the user never has to know these exist.
|
||||||
RelayPreset('Damus', 'wss://relay.damus.io'),
|
static const List<String> defaultRelays = [
|
||||||
RelayPreset('Primal', 'wss://relay.primal.net'),
|
'wss://nos.lol',
|
||||||
|
'wss://relay.damus.io',
|
||||||
|
'wss://relay.primal.net',
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Applied automatically for new users (a subset of [presets]) so sharing
|
|
||||||
/// works from the first launch, until the user changes it.
|
|
||||||
static const List<String> defaultRelays = ['wss://nos.lol', 'wss://relay.damus.io'];
|
|
||||||
|
|
||||||
/// The user's coarse area as a low-precision geohash (may be empty). Set
|
/// The user's coarse area as a low-precision geohash (may be empty). Set
|
||||||
/// manually or from device location; the transport coarsens it further.
|
/// manually or from device location; the transport coarsens it further.
|
||||||
Future<String> areaGeohash() async => (await _store.read(_areaKey)) ?? '';
|
Future<String> areaGeohash() async => (await _store.read(_areaKey)) ?? '';
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,11 @@ import 'theme.dart';
|
||||||
/// chat…) belong to Block 2 and are greyed with a "soon" tag, and Settings sits
|
/// chat…) belong to Block 2 and are greyed with a "soon" tag, and Settings sits
|
||||||
/// pinned at the bottom.
|
/// pinned at the bottom.
|
||||||
class AppDrawer extends StatelessWidget {
|
class AppDrawer extends StatelessWidget {
|
||||||
const AppDrawer({super.key});
|
const AppDrawer({this.marketEnabled = false, super.key});
|
||||||
|
|
||||||
|
/// When the Block 2 social layer is wired, the Market becomes a live drawer
|
||||||
|
/// destination; other social items stay "soon" until they're built.
|
||||||
|
final bool marketEnabled;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -34,6 +38,12 @@ class AppDrawer extends StatelessWidget {
|
||||||
icon: const Icon(Symbols.storefront),
|
icon: const Icon(Symbols.storefront),
|
||||||
label: t.menu.market,
|
label: t.menu.market,
|
||||||
divider: true,
|
divider: true,
|
||||||
|
onTap: marketEnabled
|
||||||
|
? () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
context.go('/market');
|
||||||
|
}
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
_DrawerItem(icon: const Icon(Icons.person), label: t.menu.profile),
|
_DrawerItem(icon: const Icon(Icons.person), label: t.menu.profile),
|
||||||
_DrawerItem(
|
_DrawerItem(
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class HomeScreen extends StatelessWidget {
|
||||||
final t = context.t;
|
final t = context.t;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(t.app.title)),
|
appBar: AppBar(title: Text(t.app.title)),
|
||||||
drawer: const AppDrawer(),
|
drawer: AppDrawer(marketEnabled: marketEnabled),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
const Positioned.fill(child: _SeedWatermark()),
|
const Positioned.fill(child: _SeedWatermark()),
|
||||||
|
|
|
||||||
|
|
@ -228,11 +228,11 @@ class MarketBody extends StatelessWidget {
|
||||||
separatorBuilder: (_, _) => const SizedBox(height: 12),
|
separatorBuilder: (_, _) => const SizedBox(height: 12),
|
||||||
itemBuilder: (context, i) {
|
itemBuilder: (context, i) {
|
||||||
final o = state.offers[i];
|
final o = state.offers[i];
|
||||||
|
final mine = o.authorPubkeyHex == selfPubkey;
|
||||||
return _OfferCard(
|
return _OfferCard(
|
||||||
offer: o,
|
offer: o,
|
||||||
onContact: o.authorPubkeyHex == selfPubkey
|
mine: mine,
|
||||||
? null
|
onContact: mine ? null : () => context.go('/chat/${o.authorPubkeyHex}'),
|
||||||
: () => context.go('/chat/${o.authorPubkeyHex}'),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -244,10 +244,13 @@ class MarketBody extends StatelessWidget {
|
||||||
/// One discovered offer. Human words for the reciprocity mode; coarse "near you"
|
/// One discovered offer. Human words for the reciprocity mode; coarse "near you"
|
||||||
/// instead of any precise location; price only when it's a sale.
|
/// instead of any precise location; price only when it's a sale.
|
||||||
class _OfferCard extends StatelessWidget {
|
class _OfferCard extends StatelessWidget {
|
||||||
const _OfferCard({required this.offer, this.onContact});
|
const _OfferCard({required this.offer, this.mine = false, this.onContact});
|
||||||
|
|
||||||
final Offer offer;
|
final Offer offer;
|
||||||
|
|
||||||
|
/// Whether this is the current user's own listing (shown with a "You" badge).
|
||||||
|
final bool mine;
|
||||||
|
|
||||||
/// Opens a private chat with the author; null for our own listings.
|
/// Opens a private chat with the author; null for our own listings.
|
||||||
final VoidCallback? onContact;
|
final VoidCallback? onContact;
|
||||||
|
|
||||||
|
|
@ -276,6 +279,24 @@ class _OfferCard extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (mine) ...[
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: seedGreen,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
t.market.mine,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
],
|
||||||
_TypeChip(label: _offerTypeLabel(t, offer.type)),
|
_TypeChip(label: _offerTypeLabel(t, offer.type)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -408,8 +429,6 @@ class _ConfigSheet extends StatefulWidget {
|
||||||
|
|
||||||
class _ConfigSheetState extends State<_ConfigSheet> {
|
class _ConfigSheetState extends State<_ConfigSheet> {
|
||||||
final _area = TextEditingController();
|
final _area = TextEditingController();
|
||||||
final _servers = TextEditingController();
|
|
||||||
final Set<String> _selectedRelays = {};
|
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
bool _locationBusy = false;
|
bool _locationBusy = false;
|
||||||
String? _locationError;
|
String? _locationError;
|
||||||
|
|
@ -422,22 +441,13 @@ class _ConfigSheetState extends State<_ConfigSheet> {
|
||||||
|
|
||||||
Future<void> _load() async {
|
Future<void> _load() async {
|
||||||
_area.text = await widget.settings.areaGeohash();
|
_area.text = await widget.settings.areaGeohash();
|
||||||
final current = await widget.settings.relayUrls();
|
|
||||||
final presetUrls = SocialSettings.presets.map((p) => p.url).toSet();
|
|
||||||
_selectedRelays
|
|
||||||
..clear()
|
|
||||||
..addAll(current.where(presetUrls.contains));
|
|
||||||
// Any relays that aren't presets go in the "add another" field.
|
|
||||||
_servers.text = current.where((u) => !presetUrls.contains(u)).join('\n');
|
|
||||||
if (mounted) setState(() => _loading = false);
|
if (mounted) setState(() => _loading = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _save() async {
|
Future<void> _save() async {
|
||||||
|
// Relays are managed automatically (default community servers, dead ones
|
||||||
|
// skipped by the pool) — the user only sets their area here.
|
||||||
await widget.settings.setAreaGeohash(_area.text);
|
await widget.settings.setAreaGeohash(_area.text);
|
||||||
await widget.settings.setRelayUrls([
|
|
||||||
..._selectedRelays,
|
|
||||||
..._servers.text.split('\n'),
|
|
||||||
]);
|
|
||||||
if (mounted) Navigator.of(context).pop(true);
|
if (mounted) Navigator.of(context).pop(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -465,7 +475,6 @@ class _ConfigSheetState extends State<_ConfigSheet> {
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_area.dispose();
|
_area.dispose();
|
||||||
_servers.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -533,64 +542,6 @@ class _ConfigSheetState extends State<_ConfigSheet> {
|
||||||
style: const TextStyle(color: Color(0xFFB3261E), fontSize: 12),
|
style: const TextStyle(color: Color(0xFFB3261E), fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
|
||||||
// Pick from a short list of shared servers — pre-selected so it
|
|
||||||
// works out of the box; no jargon-y blank field to puzzle over.
|
|
||||||
Text(
|
|
||||||
t.market.serversLabel,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: seedOnSurface,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
t.market.serversHelp,
|
|
||||||
style: const TextStyle(color: seedMuted, fontSize: 12, height: 1.3),
|
|
||||||
),
|
|
||||||
for (final preset in SocialSettings.presets)
|
|
||||||
CheckboxListTile(
|
|
||||||
key: Key('market.relay.${preset.name}'),
|
|
||||||
contentPadding: EdgeInsets.zero,
|
|
||||||
dense: true,
|
|
||||||
controlAffinity: ListTileControlAffinity.leading,
|
|
||||||
value: _selectedRelays.contains(preset.url),
|
|
||||||
title: Text(preset.name),
|
|
||||||
subtitle: Text(preset.url,
|
|
||||||
style: const TextStyle(fontSize: 11, color: seedMuted)),
|
|
||||||
onChanged: (checked) => setState(() {
|
|
||||||
if (checked ?? false) {
|
|
||||||
_selectedRelays.add(preset.url);
|
|
||||||
} else {
|
|
||||||
_selectedRelays.remove(preset.url);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
// A rarely-needed escape hatch for a community's own server.
|
|
||||||
Theme(
|
|
||||||
data: Theme.of(context)
|
|
||||||
.copyWith(dividerColor: Colors.transparent),
|
|
||||||
child: ExpansionTile(
|
|
||||||
key: const Key('market.serversAdvanced'),
|
|
||||||
tilePadding: EdgeInsets.zero,
|
|
||||||
childrenPadding: const EdgeInsets.only(bottom: 8),
|
|
||||||
title: Text(
|
|
||||||
t.market.serversAdvanced,
|
|
||||||
style: const TextStyle(fontSize: 13, color: seedMuted),
|
|
||||||
),
|
|
||||||
children: [
|
|
||||||
TextField(
|
|
||||||
key: const Key('market.servers'),
|
|
||||||
controller: _servers,
|
|
||||||
minLines: 1,
|
|
||||||
maxLines: 3,
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
hintText: 'wss://…',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
key: const Key('market.save'),
|
key: const Key('market.save'),
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class FakeOfferTransport implements OfferTransport {
|
||||||
Future<void> close() async {}
|
Future<void> close() async {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _wrapBody(OffersCubit cubit) {
|
Widget _wrapBody(OffersCubit cubit, {String? selfPubkey}) {
|
||||||
LocaleSettings.setLocaleSync(AppLocale.en);
|
LocaleSettings.setLocaleSync(AppLocale.en);
|
||||||
return TranslationProvider(
|
return TranslationProvider(
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
|
|
@ -62,7 +62,7 @@ Widget _wrapBody(OffersCubit cubit) {
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: BlocProvider.value(
|
body: BlocProvider.value(
|
||||||
value: cubit,
|
value: cubit,
|
||||||
child: MarketBody(onConfigure: () {}),
|
child: MarketBody(onConfigure: () {}, selfPubkey: selfPubkey),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -78,6 +78,27 @@ Offer _offer(String id, String summary, OfferType type) => Offer(
|
||||||
);
|
);
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
testWidgets('shows a "You" badge on my own listing', (tester) async {
|
||||||
|
const me = 'aa';
|
||||||
|
final cubit = OffersCubit(FakeOfferTransport([
|
||||||
|
Offer(
|
||||||
|
id: '1',
|
||||||
|
authorPubkeyHex: me,
|
||||||
|
summary: 'Mine',
|
||||||
|
type: OfferType.gift,
|
||||||
|
approxGeohash: 'sp3e9',
|
||||||
|
),
|
||||||
|
]));
|
||||||
|
await tester.pumpWidget(_wrapBody(cubit, selfPubkey: me));
|
||||||
|
await cubit.discover('sp3');
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('You'), findsOneWidget);
|
||||||
|
// My own listing offers no "Message yourself" button.
|
||||||
|
expect(find.text('Message'), findsNothing);
|
||||||
|
await cubit.close();
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('renders discovered offers with their type', (tester) async {
|
testWidgets('renders discovered offers with their type', (tester) async {
|
||||||
final cubit = OffersCubit(FakeOfferTransport([
|
final cubit = OffersCubit(FakeOfferTransport([
|
||||||
_offer('1', 'Tomate rosa de Barbastro', OfferType.gift),
|
_offer('1', 'Tomate rosa de Barbastro', OfferType.gift),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue