Skip to content

Code Quality: Continued working on Shelf Pane #17308

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
101 changes: 1 addition & 100 deletions src/Files.App/Actions/FileSystem/Transfer/BaseTransferItemAction.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

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

Expand All @@ -24,103 +21,7 @@ public BaseTransferItemAction()

public async Task ExecuteTransferAsync(DataPackageOperation type = DataPackageOperation.Copy)
{
if (ContentPageContext.ShellPage?.SlimContentPage 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);
}
await TransferHelpers.ExecuteTransferAsync(ContentPageContext, StatusCenterViewModel, type);
}

private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e)
Expand Down
15 changes: 15 additions & 0 deletions src/Files.App/Data/Items/ShelfItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using Files.Shared.Utils;
using System.Windows.Documents;

namespace Files.App.Data.Items
{
Expand Down Expand Up @@ -34,6 +35,20 @@ public async Task InitAsync(CancellationToken cancellationToken = default)
Icon = await _imageService.GetIconAsync(Inner, cancellationToken);
}

[RelayCommand]
public async Task ViewInFolderAsync(CancellationToken cancellationToken)
{
var context = Ioc.Default.GetRequiredService<IContentPageContext>();
if (context.ShellPage is not { } shellPage)
return;

var parent = await Inner.GetParentAsync(cancellationToken);
if (parent is null)
return;

await NavigationHelpers.OpenPath(parent.Id, shellPage);
}

[RelayCommand]
public void Remove()
{
Expand Down
170 changes: 170 additions & 0 deletions src/Files.App/Helpers/TransferHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage;

namespace Files.App.Helpers
{
public static class TransferHelpers
{
public static async Task ExecuteTransferAsync(IReadOnlyList<IStorable> itemsToTransfer, ShellViewModel shellViewModel, StatusCenterViewModel statusViewModel, DataPackageOperation type = DataPackageOperation.Copy)
{
ConcurrentBag<IStorageItem> items = [];
var itemsCount = itemsToTransfer.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 itemsToTransfer.ParallelForEachAsync(async storable =>
{
// Update the status to increase processed count by one
if (statusCenterItem is not null)
{
statusCenterItem.Progress.AddProcessedItemsCount(1);
statusCenterItem.Progress.Report();
}

var result = storable switch
{
IFile => await shellViewModel.GetFileFromPathAsync(storable.Id).OnSuccess(x => items.Add(x)),
IFolder => await shellViewModel.GetFolderFromPathAsync(storable.Id).OnSuccess(x => items.Add(x)),
};

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

var standardObjectsOnly = items.All(x => x is StorageFile or StorageFolder or SystemStorageFile or SystemStorageFolder);
if (standardObjectsOnly)
items = new(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)
{
if (ex is not SystemIO.IOException)
App.Logger.LogWarning(ex, "Failed to process cutting/copying due to an unknown error.");

if ((FileSystemStatusCode)ex.HResult is FileSystemStatusCode.Unauthorized)
{
var filePaths = itemsToTransfer.Select(x => x.Id).ToArray();
await FileOperationsHelpers.SetClipboard(filePaths, type);
}
}
finally
{
if (statusCenterItem is not null)
statusViewModel.RemoveItem(statusCenterItem);
}
}

public static async Task ExecuteTransferAsync(IContentPageContext context, StatusCenterViewModel statusViewModel, DataPackageOperation type = DataPackageOperation.Copy)
{
if (context.ShellPage?.SlimContentPage is null ||
context.ShellPage.SlimContentPage.IsItemSelected is false)
return;

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

ConcurrentBag<IStorageItem> items = [];
var itemsCount = context.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 context.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;
});
}

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

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

var standardObjectsOnly = items.All(x => x is StorageFile or StorageFolder or SystemStorageFile or SystemStorageFolder);
if (standardObjectsOnly)
items = new(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)
{
if (ex is not SystemIO.IOException)
App.Logger.LogWarning(ex, "Failed to process cutting/copying due to an unknown error.");

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

return;
}

// Reset cut mode
context.ShellPage.SlimContentPage.ItemManipulationModel.RefreshItemsOpacity();
}
finally
{
if (statusCenterItem is not null)
statusViewModel.RemoveItem(statusCenterItem);
}
}
}
}
28 changes: 28 additions & 0 deletions src/Files.App/UserControls/Pane/ShelfPane.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
x:Class="Files.App.UserControls.ShelfPane"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Files.App.Controls"
xmlns:converters="using:Files.App.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:data="using:Files.App.Data.Items"
Expand Down Expand Up @@ -152,6 +153,33 @@
<Border Height="1" Background="{ThemeResource DividerStrokeColorDefaultBrush}" />

<!-- Bottom Actions -->
<StackPanel
HorizontalAlignment="Center"
Orientation="Horizontal"
Spacing="12">
<Button
Padding="8"
Background="Transparent"
BorderThickness="0"
Command="{x:Bind BulkDeleteCommand, Mode=OneWay}">
<controls:ThemedIcon Style="{StaticResource App.ThemedIcons.Delete}" />
</Button>
<Button
Padding="8"
Background="Transparent"
BorderThickness="0"
Command="{x:Bind BulkCopyCommand, Mode=OneWay}">
<controls:ThemedIcon Style="{StaticResource App.ThemedIcons.Copy}" />
</Button>
<Button
Padding="8"
Background="Transparent"
BorderThickness="0"
Command="{x:Bind BulkCutCommand, Mode=OneWay}">
<controls:ThemedIcon Style="{StaticResource App.ThemedIcons.Cut}" />
</Button>
</StackPanel>

<HyperlinkButton
HorizontalAlignment="Center"
VerticalAlignment="Center"
Expand Down
Loading
Loading