Skip to content

Commit a30cf0a

Browse files
authored
Show progress in status center while preparing items for copy, cut and drag (#8047)
1 parent 720e3b2 commit a30cf0a

File tree

6 files changed

+156
-61
lines changed

6 files changed

+156
-61
lines changed

src/Files/BaseLayout.cs

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
using Microsoft.Toolkit.Uwp;
1616
using Microsoft.Toolkit.Uwp.UI;
1717
using System;
18+
using System.Collections.Concurrent;
1819
using System.Collections.Generic;
1920
using System.ComponentModel;
2021
using System.Diagnostics;
22+
using System.IO;
2123
using System.Linq;
2224
using System.Runtime.CompilerServices;
2325
using System.Threading;
@@ -838,62 +840,86 @@ protected virtual void Page_CharacterReceived(CoreWindow sender, CharacterReceiv
838840

839841
protected async void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
840842
{
841-
List<IStorageItem> selectedStorageItems = new List<IStorageItem>();
842-
var result = (FilesystemResult)false;
843+
ConcurrentBag<IStorageItem> selectedStorageItems = new ConcurrentBag<IStorageItem>();
843844

844845
e.Items.OfType<ListedItem>().ForEach(item => SelectedItems.Add(item));
845846

846-
foreach (var item in e.Items.OfType<ListedItem>())
847+
var itemsCount = e.Items.Count;
848+
PostedStatusBanner banner = itemsCount > 50 ? App.OngoingTasksViewModel.PostOperationBanner(
849+
string.Empty,
850+
string.Format("StatusPreparingItemsDetails_Plural".GetLocalized(), itemsCount),
851+
0,
852+
ReturnResult.InProgress,
853+
FileOperationType.Prepare, new CancellationTokenSource()) : null;
854+
855+
try
847856
{
848-
if (item is FtpItem ftpItem)
857+
await e.Items.OfType<ListedItem>().ParallelForEach(async item =>
849858
{
850-
if (item.PrimaryItemAttribute == StorageItemTypes.File)
859+
if (banner != null)
851860
{
852-
selectedStorageItems.Add(await new FtpStorageFile(ftpItem).ToStorageFileAsync());
861+
((IProgress<float>)banner.Progress).Report(selectedStorageItems.Count / (float)itemsCount * 100);
853862
}
854-
else if (item.PrimaryItemAttribute == StorageItemTypes.Folder)
863+
864+
if (item is FtpItem ftpItem)
855865
{
856-
selectedStorageItems.Add(new FtpStorageFolder(ftpItem));
866+
if (item.PrimaryItemAttribute == StorageItemTypes.File)
867+
{
868+
selectedStorageItems.Add(await new FtpStorageFile(ftpItem).ToStorageFileAsync());
869+
}
870+
else if (item.PrimaryItemAttribute == StorageItemTypes.Folder)
871+
{
872+
selectedStorageItems.Add(new FtpStorageFolder(ftpItem));
873+
}
857874
}
858-
}
859-
else if (item.PrimaryItemAttribute == StorageItemTypes.File || item is ZipItem)
860-
{
861-
result = await ParentShellPageInstance.FilesystemViewModel.GetFileFromPathAsync(item.ItemPath)
862-
.OnSuccess(t => selectedStorageItems.Add(t));
863-
if (!result)
875+
else if (item.PrimaryItemAttribute == StorageItemTypes.File || item is ZipItem)
864876
{
865-
break;
877+
var result = await ParentShellPageInstance.FilesystemViewModel.GetFileFromPathAsync(item.ItemPath)
878+
.OnSuccess(t => selectedStorageItems.Add(t));
879+
if (!result)
880+
{
881+
throw new IOException($"Failed to process {item.ItemPath}.", (int)result.ErrorCode);
882+
}
866883
}
867-
}
868-
else if (item.PrimaryItemAttribute == StorageItemTypes.Folder)
869-
{
870-
result = await ParentShellPageInstance.FilesystemViewModel.GetFolderFromPathAsync(item.ItemPath)
871-
.OnSuccess(t => selectedStorageItems.Add(t));
872-
if (!result)
884+
else if (item.PrimaryItemAttribute == StorageItemTypes.Folder)
873885
{
874-
break;
886+
var result = await ParentShellPageInstance.FilesystemViewModel.GetFolderFromPathAsync(item.ItemPath)
887+
.OnSuccess(t => selectedStorageItems.Add(t));
888+
if (!result)
889+
{
890+
throw new IOException($"Failed to process {item.ItemPath}.", (int)result.ErrorCode);
891+
}
875892
}
876-
}
893+
}, 10, banner?.CancellationToken ?? default);
877894
}
878-
879-
if (result.ErrorCode == FileSystemStatusCode.Unauthorized)
895+
catch (Exception ex)
880896
{
881-
var itemList = e.Items.OfType<ListedItem>().Select(x => StorageHelpers.FromPathAndType(
882-
x.ItemPath, x.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory));
883-
e.Data.Properties["FileDrop"] = itemList.ToList();
897+
if (ex.HResult == (int)FileSystemStatusCode.Unauthorized)
898+
{
899+
var itemList = e.Items.OfType<ListedItem>().Select(x => StorageHelpers.FromPathAndType(
900+
x.ItemPath, x.PrimaryItemAttribute == StorageItemTypes.File ? FilesystemItemType.File : FilesystemItemType.Directory));
901+
e.Data.Properties["FileDrop"] = itemList.ToList();
902+
}
903+
else
904+
{
905+
e.Cancel = true;
906+
}
907+
banner?.Remove();
884908
return;
885909
}
886910

911+
banner?.Remove();
912+
887913
var onlyStandard = selectedStorageItems.All(x => x is StorageFile || x is StorageFolder || x is SystemStorageFile || x is SystemStorageFolder);
888914
if (onlyStandard)
889915
{
890-
selectedStorageItems = await selectedStorageItems.ToStandardStorageItemsAsync();
916+
selectedStorageItems = new ConcurrentBag<IStorageItem>(await selectedStorageItems.ToStandardStorageItemsAsync());
891917
}
892918
if (selectedStorageItems.Count == 1)
893919
{
894-
if (selectedStorageItems[0] is IStorageFile file)
920+
if (selectedStorageItems.Single() is IStorageFile file)
895921
{
896-
var itemExtension = System.IO.Path.GetExtension(file.Name);
922+
var itemExtension = Path.GetExtension(file.Name);
897923
if (ImagePreviewViewModel.Extensions.Any((ext) => ext.Equals(itemExtension, StringComparison.OrdinalIgnoreCase)))
898924
{
899925
var streamRef = Windows.Storage.Streams.RandomAccessStreamReference.CreateFromFile(file);

src/Files/Enums/FileOperationType.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,10 @@ public enum FileOperationType : byte
5252
/// A link to an item has been created
5353
/// </summary>
5454
CreateLink = 9,
55+
56+
/// <summary>
57+
/// An item is being preparend for copy/move/drag
58+
/// </summary>
59+
Prepare = 10,
5560
}
5661
}

src/Files/Helpers/UIFilesystemHelpers.cs

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
using Files.Common;
22
using Files.Dialogs;
33
using Files.Enums;
4+
using Files.Extensions;
45
using Files.Filesystem;
56
using Files.Filesystem.StorageItems;
67
using Files.Interacts;
8+
using Files.ViewModels;
79
using Microsoft.Toolkit.Uwp;
810
using System;
911
using System.Collections.Concurrent;
1012
using System.IO;
1113
using System.Linq;
14+
using System.Threading;
1215
using System.Threading.Tasks;
1316
using Windows.ApplicationModel.AppService;
17+
using Windows.ApplicationModel.Core;
1418
using Windows.ApplicationModel.DataTransfer;
1519
using Windows.Foundation.Collections;
1620
using Windows.Storage;
@@ -32,17 +36,32 @@ public static async void CutItem(IShellPage associatedInstance)
3236
// First, reset DataGrid Rows that may be in "cut" command mode
3337
associatedInstance.SlimContentPage.ItemManipulationModel.RefreshItemsOpacity();
3438

39+
var itemsCount = associatedInstance.SlimContentPage.SelectedItems.Count;
40+
PostedStatusBanner banner = itemsCount > 50 ? App.OngoingTasksViewModel.PostOperationBanner(
41+
string.Empty,
42+
string.Format("StatusPreparingItemsDetails_Plural".GetLocalized(), itemsCount),
43+
0,
44+
ReturnResult.InProgress,
45+
FileOperationType.Prepare, new CancellationTokenSource()) : null;
46+
3547
try
3648
{
37-
await Task.WhenAll(associatedInstance.SlimContentPage.SelectedItems.ToList().Select(async listedItem =>
49+
await associatedInstance.SlimContentPage.SelectedItems.ToList().ParallelForEach(async listedItem =>
3850
{
51+
if (banner != null)
52+
{
53+
((IProgress<float>)banner.Progress).Report(items.Count / (float)itemsCount * 100);
54+
}
55+
3956
// FTP don't support cut, fallback to copy
4057
if (listedItem is not FtpItem)
4158
{
42-
// Dim opacities accordingly
43-
listedItem.Opacity = Constants.UI.DimItemOpacity;
59+
_ = CoreApplication.MainView.DispatcherQueue.TryEnqueue(Windows.System.DispatcherQueuePriority.Low, () =>
60+
{
61+
// Dim opacities accordingly
62+
listedItem.Opacity = Constants.UI.DimItemOpacity;
63+
});
4464
}
45-
4665
if (listedItem is FtpItem ftpItem)
4766
{
4867
if (listedItem.PrimaryItemAttribute == StorageItemTypes.File)
@@ -72,7 +91,7 @@ await Task.WhenAll(associatedInstance.SlimContentPage.SelectedItems.ToList().Sel
7291
throw new IOException($"Failed to process {listedItem.ItemPath}.", (int)result.ErrorCode);
7392
}
7493
}
75-
}));
94+
}, 10, banner?.CancellationToken ?? default);
7695
}
7796
catch (Exception ex)
7897
{
@@ -92,13 +111,17 @@ await Task.WhenAll(associatedInstance.SlimContentPage.SelectedItems.ToList().Sel
92111
});
93112
if (status == AppServiceResponseStatus.Success)
94113
{
114+
banner?.Remove();
95115
return;
96116
}
97117
}
98118
}
99119
associatedInstance.SlimContentPage.ItemManipulationModel.RefreshItemsOpacity();
120+
banner?.Remove();
100121
return;
101122
}
123+
124+
banner?.Remove();
102125
}
103126

104127
var onlyStandard = items.All(x => x is StorageFile || x is StorageFolder || x is SystemStorageFile || x is SystemStorageFolder);
@@ -130,14 +153,25 @@ public static async Task CopyItem(IShellPage associatedInstance)
130153
};
131154
ConcurrentBag<IStorageItem> items = new ConcurrentBag<IStorageItem>();
132155

133-
string copySourcePath = associatedInstance.FilesystemViewModel.WorkingDirectory;
134-
135156
if (associatedInstance.SlimContentPage.IsItemSelected)
136157
{
158+
var itemsCount = associatedInstance.SlimContentPage.SelectedItems.Count;
159+
PostedStatusBanner banner = itemsCount > 50 ? App.OngoingTasksViewModel.PostOperationBanner(
160+
string.Empty,
161+
string.Format("StatusPreparingItemsDetails_Plural".GetLocalized(), itemsCount),
162+
0,
163+
ReturnResult.InProgress,
164+
FileOperationType.Prepare, new CancellationTokenSource()) : null;
165+
137166
try
138167
{
139-
await Task.WhenAll(associatedInstance.SlimContentPage.SelectedItems.ToList().Select(async listedItem =>
168+
await associatedInstance.SlimContentPage.SelectedItems.ToList().ParallelForEach(async listedItem =>
140169
{
170+
if (banner != null)
171+
{
172+
((IProgress<float>)banner.Progress).Report(items.Count / (float)itemsCount * 100);
173+
}
174+
141175
if (listedItem is FtpItem ftpItem)
142176
{
143177
if (listedItem.PrimaryItemAttribute == StorageItemTypes.File)
@@ -167,7 +201,7 @@ await Task.WhenAll(associatedInstance.SlimContentPage.SelectedItems.ToList().Sel
167201
throw new IOException($"Failed to process {listedItem.ItemPath}.", (int)result.ErrorCode);
168202
}
169203
}
170-
}));
204+
}, 10, banner?.CancellationToken ?? default);
171205
}
172206
catch (Exception ex)
173207
{
@@ -178,17 +212,25 @@ await Task.WhenAll(associatedInstance.SlimContentPage.SelectedItems.ToList().Sel
178212
if (connection != null)
179213
{
180214
string filePaths = string.Join('|', associatedInstance.SlimContentPage.SelectedItems.Select(x => x.ItemPath));
181-
await connection.SendMessageAsync(new ValueSet()
215+
AppServiceResponseStatus status = await connection.SendMessageAsync(new ValueSet()
182216
{
183217
{ "Arguments", "FileOperation" },
184218
{ "fileop", "Clipboard" },
185219
{ "filepath", filePaths },
186220
{ "operation", (int)DataPackageOperation.Copy }
187221
});
222+
if (status == AppServiceResponseStatus.Success)
223+
{
224+
banner?.Remove();
225+
return;
226+
}
188227
}
189228
}
229+
banner?.Remove();
190230
return;
191231
}
232+
233+
banner?.Remove();
192234
}
193235

194236
var onlyStandard = items.All(x => x is StorageFile || x is StorageFolder || x is SystemStorageFile || x is SystemStorageFolder);

src/Files/MultilingualResources/Files.it-IT.xlf

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -138,22 +138,6 @@
138138
<source>Downloads</source>
139139
<target state="translated">Download</target>
140140
</trans-unit>
141-
<trans-unit id="SidebarHome" translate="yes" xml:space="preserve">
142-
<source>Home</source>
143-
<target state="translated">Home</target>
144-
</trans-unit>
145-
<trans-unit id="SidebarMusic" translate="yes" xml:space="preserve">
146-
<source>Music</source>
147-
<target state="translated">Musica</target>
148-
</trans-unit>
149-
<trans-unit id="SidebarPictures" translate="yes" xml:space="preserve">
150-
<source>Pictures</source>
151-
<target state="translated">Immagini</target>
152-
</trans-unit>
153-
<trans-unit id="SidebarVideos" translate="yes" xml:space="preserve">
154-
<source>Videos</source>
155-
<target state="translated">Video</target>
156-
</trans-unit>
157141
<trans-unit id="BaseLayoutContextFlyoutSortBy.Text" translate="yes" xml:space="preserve">
158142
<source>Sort by</source>
159143
<target state="translated">Ordina per</target>
@@ -3513,11 +3497,35 @@ Usiamo App Center per tenere traccia dell'utilizzo dell'app, trovare bug e risol
35133497
<source>Close tabs to the right</source>
35143498
<target state="translated">Chiudi schede a destra</target>
35153499
</trans-unit>
3516-
<trans-unit id="CloseOthers" translate="yes" xml:space="preserve">
3517-
<source>Close others</source>
3500+
<trans-unit id="CloseOtherTabs" translate="yes" xml:space="preserve">
3501+
<source>Close other tabs</source>
35183502
<target state="translated">Chiudi le altre schede</target>
35193503
</trans-unit>
3504+
<trans-unit id="Music" translate="yes" xml:space="preserve">
3505+
<source>Music</source>
3506+
<target state="translated">Musica</target>
3507+
</trans-unit>
3508+
<trans-unit id="Pictures" translate="yes" xml:space="preserve">
3509+
<source>Pictures</source>
3510+
<target state="translated">Immagini</target>
3511+
</trans-unit>
3512+
<trans-unit id="Videos" translate="yes" xml:space="preserve">
3513+
<source>Videos</source>
3514+
<target state="translated">Video</target>
3515+
</trans-unit>
3516+
<trans-unit id="UpdateFiles" translate="yes" xml:space="preserve">
3517+
<source>Update Files</source>
3518+
<target state="translated">Aggiorna Files</target>
3519+
</trans-unit>
3520+
<trans-unit id="StatusPreparingItemsDetails_Plural" translate="yes" xml:space="preserve">
3521+
<source>Preparing {0} items</source>
3522+
<target state="translated">Preparando {0} elementi</target>
3523+
</trans-unit>
3524+
<trans-unit id="PrepareInProgress" translate="yes" xml:space="preserve">
3525+
<source>Preparing items</source>
3526+
<target state="translated">Preparazione in corso</target>
3527+
</trans-unit>
35203528
</group>
35213529
</body>
35223530
</file>
3523-
</xliff>
3531+
</xliff>

src/Files/Strings/en-US/Resources.resw

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2747,4 +2747,10 @@ We use App Center to track which settings are being used, find bugs, and fix cra
27472747
<data name="UpdateFiles" xml:space="preserve">
27482748
<value>Update Files</value>
27492749
</data>
2750-
</root>
2750+
<data name="StatusPreparingItemsDetails_Plural" xml:space="preserve">
2751+
<value>Preparing {0} items</value>
2752+
</data>
2753+
<data name="PrepareInProgress" xml:space="preserve">
2754+
<value>Preparing items</value>
2755+
</data>
2756+
</root>

src/Files/ViewModels/StatusCenterViewModel.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,14 @@ public StatusBanner(string message, string title, float progress, ReturnResult s
395395
Glyph = "\xEF87" // RecycleBin Custom Glyph
396396
};
397397
break;
398+
399+
case FileOperationType.Prepare:
400+
Title = "PrepareInProgress".GetLocalized();
401+
GlyphSource = new FontIconSource()
402+
{
403+
Glyph = "\xE89A"
404+
};
405+
break;
398406
}
399407
}
400408
FullTitle = $"{Title} ({initialProgress}%)";

0 commit comments

Comments
 (0)