1010using System . Text ;
1111using System . Threading ;
1212using System . Threading . Tasks ;
13+ using System . Windows . Threading ;
1314using CommunityToolkit . Mvvm . ComponentModel ;
1415using CommunityToolkit . Mvvm . Input ;
1516using TidyWindow . App . Services ;
@@ -217,7 +218,7 @@ public sealed partial class CleanupViewModel : ViewModelBase
217218 private const int DefaultPreviewCount = 50 ;
218219 private const int MaxLockInspectionItemsPerCategory = 32 ;
219220 private const int MaxLockInspectionSampleTotal = 600 ;
220- private const int PreviewUiYieldInterval = 6 ;
221+ private const int PreviewUiYieldInterval = 3 ;
221222
222223 private readonly CleanupPreviewFilter _previewFilter ;
223224 private readonly PreviewPagingController _previewPagingController ;
@@ -349,6 +350,9 @@ public CleanupViewModel(
349350 [ ObservableProperty ]
350351 private bool _isBusy ;
351352
353+ [ ObservableProperty ]
354+ private bool _isDeletionPreparationInProgress ;
355+
352356 [ ObservableProperty ]
353357 private string _headline = "Preview and clean up system clutter" ;
354358
@@ -922,7 +926,9 @@ private async Task RunPreviewAsync()
922926 ClearTargets ( ) ;
923927 CurrentPage = 1 ;
924928
925- var report = await _cleanupService . PreviewAsync ( IncludeDownloads , IncludeBrowserHistory , PreviewCount , SelectedItemKind ) ;
929+ var report = await Task . Run (
930+ ( ) => _cleanupService . PreviewAsync ( IncludeDownloads , IncludeBrowserHistory , PreviewCount , SelectedItemKind ) ,
931+ CancellationToken . None ) . ConfigureAwait ( true ) ;
926932
927933 var previewPrep = await Task . Run ( ( ) =>
928934 {
@@ -990,11 +996,11 @@ await TransitionToPhaseAsync(
990996 }
991997
992998 [ RelayCommand ( CanExecute = nameof ( CanDeleteSelected ) ) ]
993- private Task DeleteSelectedAsync ( )
999+ private async Task DeleteSelectedAsync ( )
9941000 {
995- if ( IsBusy )
1001+ if ( IsBusy || IsDeletionPreparationInProgress )
9961002 {
997- return Task . CompletedTask ;
1003+ return ;
9981004 }
9991005
10001006 var itemsToDelete = Targets
@@ -1003,11 +1009,32 @@ private Task DeleteSelectedAsync()
10031009
10041010 if ( itemsToDelete . Count == 0 )
10051011 {
1006- return Task . CompletedTask ;
1012+ return ;
10071013 }
10081014
1009- PrepareDeletionConfirmation ( itemsToDelete ) ;
1010- return Task . CompletedTask ;
1015+ var previousBusyMessage = BusyStatusMessage ;
1016+ var previousBusyDetail = BusyStatusDetail ;
1017+
1018+ IsDeletionPreparationInProgress = true ;
1019+ BusyStatusMessage = "Preparing confirmation…" ;
1020+ BusyStatusDetail = "Summarizing your selection before showing the sheet." ;
1021+
1022+ try
1023+ {
1024+ // Let the dispatcher paint the overlay before any heavier UI-thread work runs.
1025+ await Dispatcher . Yield ( DispatcherPriority . Render ) ;
1026+
1027+ // Force a UI pass so the overlay renders before prep.
1028+ var dispatcher = Dispatcher . FromThread ( Thread . CurrentThread ) ?? Dispatcher . CurrentDispatcher ;
1029+ await dispatcher . InvokeAsync ( ( ) => { } , DispatcherPriority . Background ) ;
1030+ PrepareDeletionConfirmation ( itemsToDelete ) ;
1031+ }
1032+ finally
1033+ {
1034+ BusyStatusMessage = previousBusyMessage ;
1035+ BusyStatusDetail = previousBusyDetail ;
1036+ IsDeletionPreparationInProgress = false ;
1037+ }
10111038 }
10121039
10131040 [ RelayCommand ]
@@ -1088,7 +1115,7 @@ private void HideRunConfirmationPopup()
10881115 }
10891116 }
10901117
1091- private bool CanDeleteSelected ( ) => ! IsBusy && HasSelection && ! IsConfirmationSheetVisible ;
1118+ private bool CanDeleteSelected ( ) => ! IsBusy && ! IsDeletionPreparationInProgress && HasSelection && ! IsConfirmationSheetVisible ;
10921119
10931120 private void PrepareDeletionConfirmation ( List < ( CleanupTargetGroupViewModel group , CleanupPreviewItemViewModel item ) > itemsToDelete )
10941121 {
@@ -3097,6 +3124,11 @@ partial void OnIsBusyChanged(bool value)
30973124 ShowRunConfirmationPopupCommand . NotifyCanExecuteChanged ( ) ;
30983125 }
30993126
3127+ partial void OnIsDeletionPreparationInProgressChanged ( bool value )
3128+ {
3129+ DeleteSelectedCommand . NotifyCanExecuteChanged ( ) ;
3130+ }
3131+
31003132 partial void OnBusyStatusMessageChanged ( string value )
31013133 {
31023134 OnPropertyChanged ( nameof ( ActiveOperationStatus ) ) ;
@@ -3533,10 +3565,17 @@ private async Task AddTargetGroupsAsync(IReadOnlyList<CleanupTargetReport> targe
35333565 return ;
35343566 }
35353567
3568+ var materialized = await Task . Run ( ( ) =>
3569+ targets
3570+ . Where ( static target => target is not null )
3571+ . Select ( static target => new CleanupTargetGroupViewModel ( target ) )
3572+ . ToList ( ) ,
3573+ CancellationToken . None ) . ConfigureAwait ( true ) ;
3574+
35363575 var added = 0 ;
3537- foreach ( var target in targets )
3576+ foreach ( var group in materialized )
35383577 {
3539- AddTargetGroup ( new CleanupTargetGroupViewModel ( target ) ) ;
3578+ AddTargetGroup ( group ) ;
35403579 added ++ ;
35413580
35423581 if ( added % PreviewUiYieldInterval == 0 )
0 commit comments