feat(app): first-run data location wizard step

On first launch (no data yet, no portable dir, no pointer file),
the wizard now shows a new step-0 screen asking where to store
wallets, config and certificates.

AppPaths changes:
- DefaultDataRoot() → ~/.PalladiumWallet (home, always writable)
- IsDataLocationConfigured() → true when portable / override / pointer
  already written / legacy or default already has data
- ConfigureDataLocation(root) writes a bootstrap pointer file and
  creates the directory
- DataRoot() resolution order: override → portable → pointer → legacy
  (has data) → default

ViewModel: new StepDataLocation step, UseDefaultDataLocationCommand,
ApplyDataLocation(root) (also called from View's folder picker).

View: new wizard panel with description, default-path display, two
buttons (use default / choose folder); folder picker via
StorageProvider.OpenFolderPickerAsync.

Loc: add wiz.data.* keys (6 languages); fix fallback language "it"→"en";
update test assertion accordingly.
This commit is contained in:
2026-06-12 09:25:00 +02:00
parent 87e1c82610
commit f3bf4cf94a
6 changed files with 173 additions and 14 deletions
+40 -1
View File
@@ -165,6 +165,7 @@ public partial class MainWindowViewModel : ViewModelBase
// ---- wizard di setup (§15): un passo alla volta ----
public const string StepDataLocation = "data-location";
public const string StepStart = "start";
public const string StepOpen = "open";
public const string StepShowSeed = "show-seed";
@@ -174,6 +175,7 @@ public partial class MainWindowViewModel : ViewModelBase
public const string StepPassword = "password";
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsStepDataLocation))]
[NotifyPropertyChangedFor(nameof(IsStepStart))]
[NotifyPropertyChangedFor(nameof(IsStepOpen))]
[NotifyPropertyChangedFor(nameof(IsStepShowSeed))]
@@ -183,6 +185,7 @@ public partial class MainWindowViewModel : ViewModelBase
[NotifyPropertyChangedFor(nameof(IsStepPassword))]
private string setupStep = StepStart;
public bool IsStepDataLocation => SetupStep == StepDataLocation;
public bool IsStepStart => SetupStep == StepStart;
public bool IsStepOpen => SetupStep == StepOpen;
public bool IsStepShowSeed => SetupStep == StepShowSeed;
@@ -191,6 +194,9 @@ public partial class MainWindowViewModel : ViewModelBase
public bool IsStepPassphrase => SetupStep == StepPassphrase;
public bool IsStepPassword => SetupStep == StepPassword;
/// <summary>Percorso dati predefinito, mostrato al primo avvio.</summary>
public string DefaultDataPath => AppPaths.DefaultDataRoot();
/// <summary>True quando il flusso è "ripristina" (parole inserite dall'utente).</summary>
private bool _isRestoreFlow;
@@ -375,7 +381,12 @@ public partial class MainWindowViewModel : ViewModelBase
public MainWindowViewModel()
{
_loc = Loc.SwitchTo(_config.Language);
RefreshSetupState();
// Primo avvio: se la posizione dei dati non è ancora determinata,
// chiediamola prima di tutto il resto del wizard.
if (!AppPaths.IsDataLocationConfigured())
SetupStep = StepDataLocation;
else
RefreshSetupState();
// Aggiornamenti continui (§9): ping periodico per tenere viva la
// connessione e accorgersi subito della caduta; se cade, riconnette
// e risincronizza da solo. Le notifiche push restano la via principale.
@@ -413,6 +424,34 @@ public partial class MainWindowViewModel : ViewModelBase
private ServerRegistry Registry => new(Profile, AppPaths.ServersPath(Net));
/// <summary>Primo avvio: usa il percorso dati predefinito di piattaforma.</summary>
[RelayCommand]
private void UseDefaultDataLocation() => ApplyDataLocation(AppPaths.DefaultDataRoot());
/// <summary>
/// Memorizza la radice dati scelta (default o personalizzata), ricarica la
/// configurazione dalla nuova posizione e prosegue col wizard. Chiamata anche
/// dalla View dopo la scelta di una cartella.
/// </summary>
public void ApplyDataLocation(string root)
{
try
{
AppPaths.ConfigureDataLocation(root);
}
catch (Exception ex)
{
StatusMessage = $"{Loc.Tr("msg.error")}: {ex.Message}";
return;
}
// La config globale vive nella radice dati: ricaricala da lì.
_config = AppConfig.Load();
_loc = Loc.SwitchTo(_config.Language);
OnPropertyChanged(nameof(Loc));
OnPropertyChanged(nameof(UnitLabel));
RefreshSetupState();
}
private void RefreshSetupState()
{
SetupStep = StepStart;