feat: cover seedlings (plantón) in copy + legal, backup cadence, server picker

Beta-tester feedback triage. The data model already had LotType.seedling as
first class; the gaps were copy and UX.

- Seedlings/plantones: broaden landing hero (EN/ES), in-app legal notice, and
  all legal texts (site + docs masters) from seeds-only to "seeds and
  seedlings", with a live-plant phytosanitary/transport caveat. Seeds stay the
  hero — targeted, not a blanket rename.
- Backups: settings line now states the 7-day cadence explicitly; cadence lives
  in AutoBackupService.backupInterval as the single source for text and schedule.
- Community servers: replace the manual wss:// text box with a checklist of the
  known servers (defaults visible/toggleable) plus an "add server" affordance
  with basic validation. No jargon in the UI.

i18n across en/es/pt/fr/de/ast (ja falls back). Tests updated + a new
server-picker widget test.
This commit is contained in:
vjrj 2026-07-15 15:57:23 +02:00
parent ab027a8779
commit 15385a26cf
36 changed files with 410 additions and 179 deletions

View file

@ -25,9 +25,12 @@ void main() {
expect(find.text('Read the full documents online'), findsWidgets);
// The later sections start below the fold of the test viewport.
await tester.scrollUntilVisible(find.text('About sharing seeds'), 300);
await tester.scrollUntilVisible(
find.text('About sharing seeds and seedlings'),
300,
);
expect(find.text('Rules of the road'), findsOneWidget);
expect(find.text('About sharing seeds'), findsOneWidget);
expect(find.text('About sharing seeds and seedlings'), findsOneWidget);
});
testWidgets('renders under RTL without overflowing', (tester) async {

View file

@ -119,6 +119,35 @@ void main() {
expect(await settings.searchPrecision(), 3);
});
testWidgets('community servers are offered as toggles, not a URL box',
(tester) async {
final social = await SocialService.fromRootSeedHex('00' * 32);
final settings = SocialSettings(InMemorySecretStore());
await settings.setRelayUrls(const []); // offline + nothing selected
await tester.pumpWidget(_wrapMarket(social, settings));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('market.config')));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('market.advanced')));
await tester.pumpAndSettle();
// Every built-in server shows as a checkbox instead of a wss:// text field.
for (final url in SocialSettings.defaultRelays) {
expect(find.byKey(Key('market.server.$url')), findsOneWidget);
}
final firstKey = Key('market.server.${SocialSettings.defaultRelays.first}');
await tester.ensureVisible(find.byKey(firstKey));
await tester.pumpAndSettle();
expect(tester.widget<CheckboxListTile>(find.byKey(firstKey)).value, isFalse);
// Tapping the toggle selects it (no wss:// typing required).
await tester.tap(find.byKey(firstKey));
await tester.pumpAndSettle();
expect(tester.widget<CheckboxListTile>(find.byKey(firstKey)).value, isTrue);
});
testWidgets('"use my location" fills the area with a coarse geohash',
(tester) async {
final social = await SocialService.fromRootSeedHex('00' * 32);