Skip to content

Commit c0e8a7b

Browse files
Fix: Fixed issue where opening and closing modals quickly may lead to a crash (#11928)
1 parent ea6331e commit c0e8a7b

28 files changed

+200
-233
lines changed

src/Files.App/Actions/BaseUIAction.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using CommunityToolkit.Mvvm.ComponentModel;
2+
using Files.App.Helpers;
3+
using System.ComponentModel;
4+
using System.Threading.Tasks;
5+
6+
namespace Files.App.Actions
7+
{
8+
internal abstract class BaseUIAction : ObservableObject, IAction
9+
{
10+
public abstract string Label { get; }
11+
12+
public abstract string Description { get; }
13+
14+
public virtual bool IsExecutable => UIHelpers.CanShowDialog;
15+
16+
public BaseUIAction()
17+
{
18+
UIHelpers.PropertyChanged += UIHelpers_PropertyChanged;
19+
}
20+
21+
public abstract Task ExecuteAsync();
22+
23+
private void UIHelpers_PropertyChanged(object? sender, PropertyChangedEventArgs e)
24+
{
25+
if (e.PropertyName is nameof(UIHelpers.CanShowDialog))
26+
OnPropertyChanged(nameof(IsExecutable));
27+
}
28+
}
29+
}

src/Files.App/Actions/Content/Archives/CompressIntoArchiveAction.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,44 @@
1-
using CommunityToolkit.Mvvm.ComponentModel;
2-
using CommunityToolkit.Mvvm.DependencyInjection;
1+
using CommunityToolkit.Mvvm.DependencyInjection;
32
using Files.App.Contexts;
43
using Files.App.Dialogs;
54
using Files.App.Extensions;
65
using Files.App.Filesystem.Archive;
76
using Files.App.Helpers;
7+
using Microsoft.UI.Xaml.Controls;
88
using System.ComponentModel;
99
using System.Threading.Tasks;
1010

1111
namespace Files.App.Actions
1212
{
13-
internal class CompressIntoArchiveAction : ObservableObject, IAction
13+
internal class CompressIntoArchiveAction : BaseUIAction
1414
{
1515
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
1616

17-
public string Label => "CreateArchive".GetLocalizedResource();
17+
public override string Label => "CreateArchive".GetLocalizedResource();
1818

19-
public string Description => "TODO: Need to be described.";
19+
public override string Description => "TODO: Need to be described.";
2020

21-
public bool IsExecutable => IsContextPageTypeAdaptedToCommand()
22-
&& ArchiveHelpers.CanCompress(context.SelectedItems);
21+
public override bool IsExecutable =>
22+
IsContextPageTypeAdaptedToCommand() &&
23+
ArchiveHelpers.CanCompress(context.SelectedItems) &&
24+
UIHelpers.CanShowDialog;
2325

2426
public CompressIntoArchiveAction()
2527
{
2628
context.PropertyChanged += Context_PropertyChanged;
2729
}
2830

29-
public async Task ExecuteAsync()
31+
public override async Task ExecuteAsync()
3032
{
3133
var (sources, directory, fileName) = ArchiveHelpers.GetCompressDestination(context.ShellPage);
3234

3335
var dialog = new CreateArchiveDialog
3436
{
3537
FileName = fileName,
3638
};
37-
await dialog.ShowAsync();
39+
var result = await dialog.TryShowAsync();
3840

39-
if (!dialog.CanCreate)
41+
if (!dialog.CanCreate || result != ContentDialogResult.Primary)
4042
return;
4143

4244
IArchiveCreator creator = new ArchiveCreator

src/Files.App/Actions/Content/Archives/DecompressArchive.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using CommunityToolkit.Mvvm.ComponentModel;
2-
using CommunityToolkit.Mvvm.DependencyInjection;
1+
using CommunityToolkit.Mvvm.DependencyInjection;
32
using Files.App.Commands;
43
using Files.App.Contexts;
54
using Files.App.Extensions;
@@ -9,25 +8,27 @@
98

109
namespace Files.App.Actions
1110
{
12-
internal class DecompressArchive : ObservableObject, IAction
11+
internal class DecompressArchive : BaseUIAction
1312
{
1413
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
1514

16-
public string Label => "ExtractFiles".GetLocalizedResource();
15+
public override string Label => "ExtractFiles".GetLocalizedResource();
1716

18-
public string Description => "TODO: Need to be described.";
17+
public override string Description => "TODO: Need to be described.";
1918

2019
public HotKey HotKey { get; } = new(Keys.E, KeyModifiers.Ctrl);
2120

22-
public bool IsExecutable => IsContextPageTypeAdaptedToCommand()
23-
&& ArchiveHelpers.CanDecompress(context.SelectedItems);
21+
public override bool IsExecutable =>
22+
IsContextPageTypeAdaptedToCommand() &&
23+
ArchiveHelpers.CanDecompress(context.SelectedItems) &&
24+
UIHelpers.CanShowDialog;
2425

2526
public DecompressArchive()
2627
{
2728
context.PropertyChanged += Context_PropertyChanged;
2829
}
2930

30-
public async Task ExecuteAsync()
31+
public override async Task ExecuteAsync()
3132
{
3233
await ArchiveHelpers.DecompressArchive(context.ShellPage);
3334
}

src/Files.App/Actions/Content/Archives/DecompressArchiveHere.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using CommunityToolkit.Mvvm.ComponentModel;
2-
using CommunityToolkit.Mvvm.DependencyInjection;
1+
using CommunityToolkit.Mvvm.DependencyInjection;
32
using Files.App.Contexts;
43
using Files.App.Extensions;
54
using Files.App.Helpers;
@@ -8,23 +7,25 @@
87

98
namespace Files.App.Actions
109
{
11-
internal class DecompressArchiveHere : ObservableObject, IAction
10+
internal class DecompressArchiveHere : BaseUIAction
1211
{
1312
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
1413

15-
public string Label => "ExtractHere".GetLocalizedResource();
14+
public override string Label => "ExtractHere".GetLocalizedResource();
1615

17-
public string Description => "TODO: Need to be described.";
16+
public override string Description => "TODO: Need to be described.";
1817

19-
public bool IsExecutable => IsContextPageTypeAdaptedToCommand()
20-
&& ArchiveHelpers.CanDecompress(context.SelectedItems);
18+
public override bool IsExecutable =>
19+
IsContextPageTypeAdaptedToCommand() &&
20+
ArchiveHelpers.CanDecompress(context.SelectedItems) &&
21+
UIHelpers.CanShowDialog;
2122

2223
public DecompressArchiveHere()
2324
{
2425
context.PropertyChanged += Context_PropertyChanged;
2526
}
2627

27-
public async Task ExecuteAsync()
28+
public override async Task ExecuteAsync()
2829
{
2930
await ArchiveHelpers.DecompressArchiveHere(context.ShellPage);
3031
}

src/Files.App/Actions/Content/Archives/DecompressArchiveToChildFolderAction.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using CommunityToolkit.Mvvm.ComponentModel;
2-
using CommunityToolkit.Mvvm.DependencyInjection;
1+
using CommunityToolkit.Mvvm.DependencyInjection;
32
using Files.App.Contexts;
43
using Files.App.Extensions;
54
using Files.App.Helpers;
@@ -10,23 +9,25 @@
109

1110
namespace Files.App.Actions
1211
{
13-
internal class DecompressArchiveToChildFolderAction : ObservableObject, IAction
12+
internal class DecompressArchiveToChildFolderAction : BaseUIAction
1413
{
1514
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
1615

17-
public string Label => ComputeLabel();
16+
public override string Label => ComputeLabel();
1817

19-
public string Description => "TODO: Need to be described.";
18+
public override string Description => "TODO: Need to be described.";
2019

21-
public bool IsExecutable => IsContextPageTypeAdaptedToCommand()
22-
&& ArchiveHelpers.CanDecompress(context.SelectedItems);
20+
public override bool IsExecutable =>
21+
IsContextPageTypeAdaptedToCommand() &&
22+
ArchiveHelpers.CanDecompress(context.SelectedItems) &&
23+
UIHelpers.CanShowDialog;
2324

2425
public DecompressArchiveToChildFolderAction()
2526
{
2627
context.PropertyChanged += Context_PropertyChanged;
2728
}
2829

29-
public async Task ExecuteAsync()
30+
public override async Task ExecuteAsync()
3031
{
3132
await ArchiveHelpers.DecompressArchiveToChildFolder(context.ShellPage);
3233
}

src/Files.App/Actions/FileSystem/CreateFolderAction.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using CommunityToolkit.Mvvm.ComponentModel;
2-
using CommunityToolkit.Mvvm.DependencyInjection;
1+
using CommunityToolkit.Mvvm.DependencyInjection;
32
using Files.App.Commands;
43
using Files.App.Contexts;
54
using Files.App.Extensions;
@@ -10,24 +9,24 @@
109

1110
namespace Files.App.Actions
1211
{
13-
internal class CreateFolderAction : ObservableObject, IAction
12+
internal class CreateFolderAction : BaseUIAction
1413
{
1514
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
1615

17-
public string Label { get; } = "Folder".GetLocalizedResource();
16+
public override string Label { get; } = "Folder".GetLocalizedResource();
1817

19-
public string Description => "TODO: Need to be described.";
18+
public override string Description => "TODO: Need to be described.";
2019

2120
public RichGlyph Glyph { get; } = new RichGlyph(baseGlyph: "\uE8B7");
2221

23-
public bool IsExecutable => context.ShellPage is not null;
22+
public override bool IsExecutable => context.ShellPage is not null && UIHelpers.CanShowDialog;
2423

2524
public CreateFolderAction()
2625
{
2726
context.PropertyChanged += Context_PropertyChanged;
2827
}
2928

30-
public Task ExecuteAsync()
29+
public override Task ExecuteAsync()
3130
{
3231
if (context.ShellPage is not null)
3332
UIFilesystemHelpers.CreateFileFromDialogResultType(AddItemDialogItemType.Folder, null!, context.ShellPage);

src/Files.App/Actions/FileSystem/CreateShortcutAction.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using CommunityToolkit.Mvvm.ComponentModel;
2-
using CommunityToolkit.Mvvm.DependencyInjection;
1+
using CommunityToolkit.Mvvm.DependencyInjection;
32
using Files.App.Commands;
43
using Files.App.Contexts;
54
using Files.App.Extensions;
@@ -11,24 +10,24 @@
1110

1211
namespace Files.App.Actions
1312
{
14-
internal class CreateShortcutAction : ObservableObject, IAction
13+
internal class CreateShortcutAction : BaseUIAction
1514
{
1615
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
1716

18-
public string Label { get; } = "CreateShortcut".GetLocalizedResource();
17+
public override string Label { get; } = "CreateShortcut".GetLocalizedResource();
1918

20-
public string Description => "TODO: Need to be described.";
19+
public override string Description => "TODO: Need to be described.";
2120

2221
public RichGlyph Glyph { get; } = new RichGlyph(opacityStyle: "ColorIconShortcut");
2322

24-
public bool IsExecutable => context.HasSelection;
23+
public override bool IsExecutable => context.HasSelection && UIHelpers.CanShowDialog;
2524

2625
public CreateShortcutAction()
2726
{
2827
context.PropertyChanged += Context_PropertyChanged;
2928
}
3029

31-
public async Task ExecuteAsync()
30+
public override async Task ExecuteAsync()
3231
{
3332
var currentPath = context.ShellPage?.FilesystemViewModel.WorkingDirectory;
3433

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,39 @@
1-
using CommunityToolkit.Mvvm.ComponentModel;
2-
using CommunityToolkit.Mvvm.DependencyInjection;
1+
using CommunityToolkit.Mvvm.DependencyInjection;
32
using Files.App.Commands;
43
using Files.App.Contexts;
54
using Files.App.Extensions;
65
using Files.App.Helpers;
6+
using System.ComponentModel;
77
using System.Threading.Tasks;
88

99
namespace Files.App.Actions
1010
{
11-
internal class CreateShortcutFromDialogAction : ObservableObject, IAction
11+
internal class CreateShortcutFromDialogAction : BaseUIAction
1212
{
1313
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
1414

15-
public string Label { get; } = "Shortcut".GetLocalizedResource();
15+
public override string Label { get; } = "Shortcut".GetLocalizedResource();
1616

17-
public string Description => "TODO: Need to be described.";
17+
public override string Description => "TODO: Need to be described.";
1818

1919
public RichGlyph Glyph { get; } = new RichGlyph(opacityStyle: "ColorIconShortcut");
2020

21-
public async Task ExecuteAsync()
21+
public override bool IsExecutable => context.ShellPage is not null && UIHelpers.CanShowDialog;
22+
23+
public CreateShortcutFromDialogAction()
24+
{
25+
context.PropertyChanged += Context_PropertyChanged;
26+
}
27+
28+
public override async Task ExecuteAsync()
29+
{
30+
await UIFilesystemHelpers.CreateShortcutFromDialogAsync(context.ShellPage);
31+
}
32+
33+
private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e)
2234
{
23-
if (context.ShellPage is not null)
24-
await UIFilesystemHelpers.CreateShortcutFromDialogAsync(context.ShellPage);
35+
if (e.PropertyName is nameof(IContentPageContext.ShellPage))
36+
OnPropertyChanged(nameof(IsExecutable));
2537
}
2638
}
2739
}

src/Files.App/Actions/FileSystem/DeleteItemAction.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using CommunityToolkit.Mvvm.ComponentModel;
2-
using CommunityToolkit.Mvvm.DependencyInjection;
1+
using CommunityToolkit.Mvvm.DependencyInjection;
32
using Files.App.Commands;
43
using Files.App.Contexts;
54
using Files.App.Extensions;
@@ -13,29 +12,30 @@
1312

1413
namespace Files.App.Actions
1514
{
16-
internal class DeleteItemAction : ObservableObject, IAction
15+
internal class DeleteItemAction : BaseUIAction
1716
{
1817
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
1918
private readonly IFoldersSettingsService settings = Ioc.Default.GetRequiredService<IFoldersSettingsService>();
2019

21-
public string Label { get; } = "Delete".GetLocalizedResource();
20+
public override string Label { get; } = "Delete".GetLocalizedResource();
2221

23-
public string Description => "TODO: Need to be described.";
22+
public override string Description => "TODO: Need to be described.";
2423

2524
public RichGlyph Glyph { get; } = new RichGlyph(opacityStyle: "ColorIconDelete");
2625

2726
public HotKey HotKey { get; } = new(Keys.Delete);
2827

29-
public bool IsExecutable =>
28+
public override bool IsExecutable =>
3029
context.HasSelection &&
31-
(!context.ShellPage?.SlimContentPage?.IsRenamingItem ?? false);
30+
(!context.ShellPage?.SlimContentPage?.IsRenamingItem ?? false) &&
31+
UIHelpers.CanShowDialog;
3232

3333
public DeleteItemAction()
3434
{
3535
context.PropertyChanged += Context_PropertyChanged;
3636
}
3737

38-
public async Task ExecuteAsync()
38+
public override async Task ExecuteAsync()
3939
{
4040
if (context.ShellPage is null || !IsExecutable)
4141
return;

0 commit comments

Comments
 (0)