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 7891a973f4
commit 1ce32c2b5c
36 changed files with 410 additions and 179 deletions

View file

@ -36,6 +36,11 @@ class AutoBackupService {
/// so a corrupt copy can't overwrite the only backup.
static const _keep = 3;
/// How often an automatic copy is made. The single source of truth for both
/// the schedule ([runIfDue]'s default) and the reassurance copy in Settings,
/// so the two can never drift apart.
static const Duration backupInterval = Duration(days: 7);
/// When the last automatic copy was made, or null if none has run yet. For a
/// reassurance line in Settings.
Future<DateTime?> lastBackupAt() => _store.lastBackupAt();
@ -43,7 +48,7 @@ class AutoBackupService {
/// Makes a fresh copy when at least [interval] has passed since the last one.
/// Returns true when it wrote a copy, false when not due (or on any failure
/// a backup must never crash startup or the move to the background).
Future<bool> runIfDue({Duration interval = const Duration(days: 7)}) async {
Future<bool> runIfDue({Duration interval = backupInterval}) async {
try {
final last = await _store.lastBackupAt();
if (last != null && _now().difference(last) < interval) return false;