fix(installer): make user-data deletion on uninstall opt-in
Add bool removeAppData parameter (default false) to UninstallRunner.RunAsync, gate ~/.todo-app deletion on it, surface a checkbox in SettingsWindow, and update the confirmation message to reflect whether data will be removed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ public sealed class UninstallRunner
|
|||||||
_stopService = stopService;
|
_stopService = stopService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<StepResult> RunAsync(IProgress<string> progress, CancellationToken ct)
|
public async Task<StepResult> RunAsync(bool removeAppData, IProgress<string> progress, CancellationToken ct)
|
||||||
{
|
{
|
||||||
// 1) Validate install dir up front — refuse obviously unsafe paths.
|
// 1) Validate install dir up front — refuse obviously unsafe paths.
|
||||||
// Prevents Directory.Delete(recursive:true) from wiping C:\ or C:\Program Files\.
|
// Prevents Directory.Delete(recursive:true) from wiping C:\ or C:\Program Files\.
|
||||||
@@ -67,7 +67,9 @@ public sealed class UninstallRunner
|
|||||||
failures.Add($"install dir ({_context.InstallDirectory}): {err}");
|
failures.Add($"install dir ({_context.InstallDirectory}): {err}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6) Delete ~/.todo-app (config + DB + logs) — user opted into full removal.
|
// 6) Delete ~/.todo-app (config + DB + logs) — only if user opted in.
|
||||||
|
if (removeAppData)
|
||||||
|
{
|
||||||
var appData = Paths.AppDataRoot();
|
var appData = Paths.AppDataRoot();
|
||||||
if (Directory.Exists(appData))
|
if (Directory.Exists(appData))
|
||||||
{
|
{
|
||||||
@@ -75,6 +77,7 @@ public sealed class UninstallRunner
|
|||||||
if (!TryDeleteDir(appData, out var err))
|
if (!TryDeleteDir(appData, out var err))
|
||||||
failures.Add($"app data ({appData}): {err}");
|
failures.Add($"app data ({appData}): {err}");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 7) If we were launched from inside the install dir (Apps & Features case),
|
// 7) If we were launched from inside the install dir (Apps & Features case),
|
||||||
// our own exe is still locked — schedule a cmd.exe trampoline to finish
|
// our own exe is still locked — schedule a cmd.exe trampoline to finish
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ public partial class SettingsViewModel : ObservableObject
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _versionLabel = "";
|
private string _versionLabel = "";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _removeAppData;
|
||||||
|
|
||||||
public SettingsViewModel(
|
public SettingsViewModel(
|
||||||
PageResolver resolver,
|
PageResolver resolver,
|
||||||
InstallContext context,
|
InstallContext context,
|
||||||
@@ -133,8 +136,12 @@ public partial class SettingsViewModel : ObservableObject
|
|||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task Uninstall()
|
private async Task Uninstall()
|
||||||
{
|
{
|
||||||
|
var dataNote = RemoveAppData
|
||||||
|
? "This will remove ClaudeDo AND delete all of your tasks, configuration, and database.\n\nContinue?"
|
||||||
|
: "This will remove ClaudeDo. Your tasks, configuration, and database in ~/.todo-app will be kept.\n\nContinue?";
|
||||||
|
|
||||||
var confirm = MessageBox.Show(
|
var confirm = MessageBox.Show(
|
||||||
"This will remove ClaudeDo AND delete all of your tasks, configuration, and database.\n\nContinue?",
|
dataNote,
|
||||||
"Uninstall ClaudeDo",
|
"Uninstall ClaudeDo",
|
||||||
MessageBoxButton.YesNo,
|
MessageBoxButton.YesNo,
|
||||||
MessageBoxImage.Warning);
|
MessageBoxImage.Warning);
|
||||||
@@ -142,7 +149,7 @@ public partial class SettingsViewModel : ObservableObject
|
|||||||
if (confirm != MessageBoxResult.Yes) return;
|
if (confirm != MessageBoxResult.Yes) return;
|
||||||
|
|
||||||
var progress = new Progress<string>(msg => StatusMessage = msg);
|
var progress = new Progress<string>(msg => StatusMessage = msg);
|
||||||
var r = await _uninstallRunner.RunAsync(progress, CancellationToken.None);
|
var r = await _uninstallRunner.RunAsync(RemoveAppData, progress, CancellationToken.None);
|
||||||
|
|
||||||
if (!r.Success)
|
if (!r.Success)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<!-- Status message / version label -->
|
<!-- Status message / version label -->
|
||||||
@@ -88,14 +89,17 @@
|
|||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Button Grid.Column="1" Content="Uninstall" Margin="0,0,8,0"
|
<CheckBox Grid.Column="1" IsChecked="{Binding RemoveAppData}"
|
||||||
|
Content="Remove user data (tasks, logs, configs in ~/.todo-app)"
|
||||||
|
Margin="0,0,12,0" VerticalAlignment="Center"/>
|
||||||
|
<Button Grid.Column="2" Content="Uninstall" Margin="0,0,8,0"
|
||||||
Command="{Binding UninstallCommand}"/>
|
Command="{Binding UninstallCommand}"/>
|
||||||
<Button Grid.Column="2" Content="Repair" Margin="0,0,8,0"
|
<Button Grid.Column="3" Content="Repair" Margin="0,0,8,0"
|
||||||
Command="{Binding RepairCommand}"/>
|
Command="{Binding RepairCommand}"/>
|
||||||
<Button Grid.Column="3" Content="Save" Margin="0,0,8,0"
|
<Button Grid.Column="4" Content="Save" Margin="0,0,8,0"
|
||||||
Command="{Binding SaveCommand}"
|
Command="{Binding SaveCommand}"
|
||||||
Style="{StaticResource AccentButton}"/>
|
Style="{StaticResource AccentButton}"/>
|
||||||
<Button Grid.Column="4" Content="Close"
|
<Button Grid.Column="5" Content="Close"
|
||||||
Command="{Binding CloseCommand}"/>
|
Command="{Binding CloseCommand}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
Reference in New Issue
Block a user