Update recursive folder delete logic (#1695)

This commit is contained in:
Leendert de Borst
2026-04-01 12:35:32 +02:00
parent 3f1c64ef81
commit b528678900
5 changed files with 55 additions and 28 deletions

View File

@@ -211,7 +211,7 @@
"allItemsInFolders": "All your items are organized in folders. Click a folder above to view your credentials, or use the search to find specific items.",
"deleteFolder": "Delete Folder",
"deleteFolderKeepItems": "Delete folder only",
"deleteFolderKeepItemsDescription": "Items will be moved back to the main list.",
"deleteFolderKeepItemsDescription": "Items will be moved to the parent folder.",
"deleteFolderAndItems": "Delete folder and all items",
"deleteFolderAndItemsDescription": "{{count}} item(s) will be moved to Recently Deleted.",
"filters": {

View File

@@ -109,9 +109,10 @@ export class FolderRepository extends BaseRepository {
/**
* Delete a folder (soft delete).
* Recursively handles child folders and items:
* - All items in this folder and child folders are moved to the parent folder (or root if no parent)
* - All child folders are moved to the parent of the deleted folder
* Handles child folders and items:
* - Items in this folder only are moved to the parent folder (or root if no parent)
* - Items in child folders stay in their respective folders (since child folders are moved to parent)
* - All direct child folders are moved to the parent of the deleted folder
* @param folderId - The ID of the folder to delete
* @returns The number of rows updated
*/
@@ -123,10 +124,7 @@ export class FolderRepository extends BaseRepository {
const folder = this.getById(folderId);
const targetParentId = folder?.ParentFolderId || null;
// Get all child folder IDs recursively
const allChildFolderIds = this.getAllChildFolderIds(folderId);
// Move all items in this folder and all child folders to the parent folder (or root if no parent)
// Move only items in this folder to the parent folder (or root if no parent)
if (targetParentId) {
// Has parent: move items to parent folder
this.client.executeUpdate(FolderQueries.MOVE_ITEMS_TO_FOLDER, [
@@ -134,27 +132,12 @@ export class FolderRepository extends BaseRepository {
currentDateTime,
folderId
]);
for (const childFolderId of allChildFolderIds) {
this.client.executeUpdate(FolderQueries.MOVE_ITEMS_TO_FOLDER, [
targetParentId,
currentDateTime,
childFolderId
]);
}
} else {
// No parent: move items to root (NULL)
this.client.executeUpdate(FolderQueries.CLEAR_ITEMS_FOLDER, [
currentDateTime,
folderId
]);
for (const childFolderId of allChildFolderIds) {
this.client.executeUpdate(FolderQueries.CLEAR_ITEMS_FOLDER, [
currentDateTime,
childFolderId
]);
}
}
// Move direct child folders to the parent of the deleted folder

View File

@@ -321,7 +321,7 @@ else
OnDeleteFolderOnly="DeleteFolderOnlyAsync"
OnDeleteFolderAndContents="DeleteFolderAndContentsAsync"
FolderName="@CurrentFolderName"
ItemCount="@FilteredAndSortedItems.Count()" />
ItemCount="@CurrentFolderItemCount" />
@code {
private IStringLocalizer Localizer => LocalizerFactory.Create("Pages.Main.Items.Home", "AliasVault.Client");
@@ -392,6 +392,25 @@ else
/// </summary>
private string CurrentFolderName { get; set; } = string.Empty;
/// <summary>
/// Gets the total item count in the current folder tree (includes items in all subfolders recursively).
/// Used for the delete folder modal to determine if "delete with contents" option should be shown.
/// </summary>
private int CurrentFolderItemCount
{
get
{
if (!FolderId.HasValue)
{
return FilteredAndSortedItems.Count();
}
// Find the current folder in the Folders list and return its recursive count
var currentFolder = Folders.FirstOrDefault(f => f.Id == FolderId.Value);
return currentFolder?.ItemCount ?? 0;
}
}
/// <summary>
/// Gets or sets whether the settings dropdown is shown.
/// </summary>
@@ -889,16 +908,28 @@ else
}
/// <summary>
/// Delete the current folder only (move items to root).
/// Delete the current folder only (move items to parent folder).
/// </summary>
private async Task DeleteFolderOnlyAsync()
{
if (FolderId.HasValue)
{
// Get the parent folder ID before deletion
var folder = await FolderService.GetByIdAsync(FolderId.Value);
var parentFolderId = folder?.ParentFolderId;
var success = await FolderService.DeleteAsync(FolderId.Value);
if (success)
{
NavigationManager.NavigateTo("/items");
// Navigate to parent folder if it exists, otherwise root
if (parentFolderId.HasValue)
{
NavigationManager.NavigateTo($"/items/folder/{parentFolderId.Value}");
}
else
{
NavigationManager.NavigateTo("/items");
}
}
else
{
@@ -914,10 +945,22 @@ else
{
if (FolderId.HasValue)
{
// Get the parent folder ID before deletion
var folder = await FolderService.GetByIdAsync(FolderId.Value);
var parentFolderId = folder?.ParentFolderId;
var success = await FolderService.DeleteWithContentsAsync(FolderId.Value);
if (success)
{
NavigationManager.NavigateTo("/items");
// Navigate to parent folder if it exists, otherwise root
if (parentFolderId.HasValue)
{
NavigationManager.NavigateTo($"/items/folder/{parentFolderId.Value}");
}
else
{
NavigationManager.NavigateTo("/items");
}
}
else
{

View File

@@ -72,7 +72,7 @@
<comment>Title for delete folder only option</comment>
</data>
<data name="DeleteFolderOnlyDescription" xml:space="preserve">
<value>Items in this folder will be moved to root</value>
<value>Items will be moved to the parent folder</value>
<comment>Description for delete folder only option</comment>
</data>
<data name="DeleteFolderAndContentsTitle" xml:space="preserve">

View File

@@ -233,6 +233,7 @@ public sealed class FolderService(DbService dbService)
foreach (var item in itemsInFolders)
{
item.DeletedAt = currentDateTime;
item.FolderId = null;
item.UpdatedAt = currentDateTime;
}