Skip to content

Code Quality: Simplify UI operation layer for Copy and Cut #15724

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 0 additions & 46 deletions src/Files.App/Actions/FileSystem/CopyItemAction.cs

This file was deleted.

45 changes: 0 additions & 45 deletions src/Files.App/Actions/FileSystem/CutItemAction.cs

This file was deleted.

135 changes: 135 additions & 0 deletions src/Files.App/Actions/FileSystem/Transfer/BaseTransferItemAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
using System.IO;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage;
using Windows.System;

namespace Files.App.Actions
{
internal abstract class BaseTransferItemAction : ObservableObject
{
protected readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService<IContentPageContext>();
protected readonly StatusCenterViewModel StatusCenterViewModel = Ioc.Default.GetRequiredService<StatusCenterViewModel>();

public bool IsExecutable
=> ContentPageContext.HasSelection;

public BaseTransferItemAction()
{
ContentPageContext.PropertyChanged += ContentPageContext_PropertyChanged;
}

public async Task ExecuteTransferAsync(DataPackageOperation type = DataPackageOperation.Copy)
{
if (ContentPageContext.ShellPage is null ||
ContentPageContext.ShellPage.SlimContentPage.IsItemSelected is false)
return;

// Reset cut mode
ContentPageContext.ShellPage.SlimContentPage.ItemManipulationModel.RefreshItemsOpacity();

ConcurrentBag<IStorageItem> items = [];
var itemsCount = ContentPageContext.SelectedItems.Count;
var statusCenterItem = itemsCount > 50 ? StatusCenterHelper.AddCard_Prepare() : null;
var dataPackage = new DataPackage() { RequestedOperation = type };

try
{
// Update the status to in-progress
if (statusCenterItem is not null)
{
statusCenterItem.Progress.EnumerationCompleted = true;
statusCenterItem.Progress.ItemsCount = items.Count;
statusCenterItem.Progress.ReportStatus(FileSystemStatusCode.InProgress);
}

await ContentPageContext.SelectedItems.ToList().ParallelForEachAsync(async listedItem =>
{
// Update the status to increase processed count by one
if (statusCenterItem is not null)
{
statusCenterItem.Progress.AddProcessedItemsCount(1);
statusCenterItem.Progress.Report();
}

if (listedItem is FtpItem ftpItem)
{
// Don't dim selected items here since FTP doesn't support cut
if (ftpItem.PrimaryItemAttribute is StorageItemTypes.File or StorageItemTypes.Folder)
items.Add(await ftpItem.ToStorageItem());
}
else
{
if (type is DataPackageOperation.Move)
{
// Dim opacities accordingly
await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() =>
{
listedItem.Opacity = Constants.UI.DimItemOpacity;
});
}

FilesystemResult? result =
listedItem.PrimaryItemAttribute == StorageItemTypes.File || listedItem is ZipItem
? await ContentPageContext.ShellPage.ShellViewModel.GetFileFromPathAsync(listedItem.ItemPath).OnSuccess(t => items.Add(t))
: await ContentPageContext.ShellPage.ShellViewModel.GetFolderFromPathAsync(listedItem.ItemPath).OnSuccess(t => items.Add(t));

if (!result)
throw new IOException($"Failed to process {listedItem.ItemPath} in cutting/copying to the clipboard.", (int)result.ErrorCode);
}
},
10,
statusCenterItem?.CancellationToken ?? default);

var standardObjectsOnly = items.All(x => x is StorageFile or StorageFolder or SystemStorageFile or SystemStorageFolder);
if (standardObjectsOnly)
items = new ConcurrentBag<IStorageItem>(await items.ToStandardStorageItemsAsync());

if (items.IsEmpty)
return;

dataPackage.Properties.PackageFamilyName = Windows.ApplicationModel.Package.Current.Id.FamilyName;
dataPackage.SetStorageItems(items, false);

Clipboard.SetContent(dataPackage);
}
catch (Exception ex)
{
dataPackage = default;

if (ex is not IOException)
{
App.Logger.LogWarning(ex, "Failed to process cutting/copying due to an unknown error.");
}

if ((FileSystemStatusCode)ex.HResult is FileSystemStatusCode.Unauthorized)
{
string[] filePaths = ContentPageContext.SelectedItems.Select(x => x.ItemPath).ToArray();
await FileOperationsHelpers.SetClipboard(filePaths, type);

return;
}

// Reset cut mode
ContentPageContext.ShellPage.SlimContentPage.ItemManipulationModel.RefreshItemsOpacity();

return;
}
finally
{
if (statusCenterItem is not null)
StatusCenterViewModel.RemoveItem(statusCenterItem);
}
}

private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName is nameof(IContentPageContext.HasSelection))
OnPropertyChanged(nameof(IsExecutable));
}
}
}
31 changes: 31 additions & 0 deletions src/Files.App/Actions/FileSystem/Transfer/CopyItemAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using Windows.ApplicationModel.DataTransfer;

namespace Files.App.Actions
{
internal sealed class CopyItemAction : BaseTransferItemAction, IAction
{
public string Label
=> "Copy".GetLocalizedResource();

public string Description
=> "CopyItemDescription".GetLocalizedResource();

public RichGlyph Glyph
=> new(opacityStyle: "ColorIconCopy");

public HotKey HotKey
=> new(Keys.C, KeyModifiers.Ctrl);

public CopyItemAction() : base()
{
}

public Task ExecuteAsync(object? parameter = null)
{
return ExecuteTransferAsync(DataPackageOperation.Copy);
}
}
}
31 changes: 31 additions & 0 deletions src/Files.App/Actions/FileSystem/Transfer/CutItemAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using Windows.ApplicationModel.DataTransfer;

namespace Files.App.Actions
{
internal sealed class CutItemAction : BaseTransferItemAction, IAction
{
public string Label
=> "Cut".GetLocalizedResource();

public string Description
=> "CutItemDescription".GetLocalizedResource();

public RichGlyph Glyph
=> new(opacityStyle: "ColorIconCut");

public HotKey HotKey
=> new(Keys.X, KeyModifiers.Ctrl);

public CutItemAction() : base()
{
}

public Task ExecuteAsync(object? parameter = null)
{
return ExecuteTransferAsync(DataPackageOperation.Move);
}
}
}
Loading
Loading