Added select folder dialog. Added item moving as a module

This commit is contained in:
Marcel Baumgartner
2024-03-30 23:47:59 +01:00
parent 5d9c32a196
commit 9abf32b288
5 changed files with 111 additions and 93 deletions

View File

@@ -53,9 +53,11 @@ public class FileManagerFeature : MoonlightFeature
var pluginService = context.Application.Services.GetRequiredService<PluginService>(); var pluginService = context.Application.Services.GetRequiredService<PluginService>();
await pluginService.RegisterImplementation<IFileManagerContextAction>(new RenameContextAction()); await pluginService.RegisterImplementation<IFileManagerContextAction>(new RenameContextAction());
await pluginService.RegisterImplementation<IFileManagerContextAction>(new MoveContextAction());
await pluginService.RegisterImplementation<IFileManagerContextAction>(new DownloadContextAction()); await pluginService.RegisterImplementation<IFileManagerContextAction>(new DownloadContextAction());
await pluginService.RegisterImplementation<IFileManagerContextAction>(new DeleteContextAction()); await pluginService.RegisterImplementation<IFileManagerContextAction>(new DeleteContextAction());
await pluginService.RegisterImplementation<IFileManagerSelectionAction>(new MoveSelectionAction());
await pluginService.RegisterImplementation<IFileManagerSelectionAction>(new DeleteSelectionAction()); await pluginService.RegisterImplementation<IFileManagerSelectionAction>(new DeleteSelectionAction());
await pluginService.RegisterImplementation<IFileManagerCreateAction>(new CreateFileAction()); await pluginService.RegisterImplementation<IFileManagerCreateAction>(new CreateFileAction());

View File

@@ -29,6 +29,7 @@ public class DeleteSelectionAction : IFileManagerSelectionAction
await toastService.RemoveProgress("fileManagerSelectionDelete"); await toastService.RemoveProgress("fileManagerSelectionDelete");
await toastService.Success($"Successfully deleted selection"); await toastService.Success("Successfully deleted selection");
await fileManager.View.Refresh();
} }
} }

View File

@@ -0,0 +1,26 @@
using MoonCoreUI.Services;
using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
namespace Moonlight.Features.FileManager.Implementations;
public class MoveContextAction : IFileManagerContextAction
{
public string Name => "Move";
public string Icon => "bx-move";
public string Color => "info";
public Func<FileEntry, bool> Filter => _ => true;
public async Task Execute(BaseFileAccess access, UI.NewFileManager.FileManager fileManager, FileEntry entry, IServiceProvider provider)
{
await fileManager.OpenFolderSelect("Select the location to move the item to", async path =>
{
var toastService = provider.GetRequiredService<ToastService>();
await access.Move(entry, path + entry.Name);
await toastService.Success("Successfully moved item");
await fileManager.View.Refresh();
});
}
}

View File

@@ -0,0 +1,33 @@
using MoonCoreUI.Services;
using Moonlight.Features.FileManager.Interfaces;
using Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
namespace Moonlight.Features.FileManager.Implementations;
public class MoveSelectionAction : IFileManagerSelectionAction
{
public string Name => "Move";
public string Color => "primary";
public async Task Execute(BaseFileAccess access, UI.NewFileManager.FileManager fileManager, FileEntry[] entries, IServiceProvider provider)
{
await fileManager.OpenFolderSelect("Select the location to move the items to", async path =>
{
var toastService = provider.GetRequiredService<ToastService>();
await toastService.CreateProgress("fileManagerSelectionMove", "Moving items");
foreach (var entry in entries)
{
await toastService.ModifyProgress("fileManagerSelectionMove", $"Moving '{entry.Name}'");
await access.Move(entry, path + entry.Name);
}
await toastService.RemoveProgress("fileManagerSelectionMove");
await toastService.Success("Successfully moved selection");
await fileManager.View.Refresh();
});
}
}

View File

@@ -50,8 +50,8 @@
foreach (var action in SelectionActions) foreach (var action in SelectionActions)
{ {
var cssClass = $"btn btn-{action.Color} mx-2"; var cssClass = $"btn btn-{action.Color} mx-2";
<WButton Text="@action.Name" CssClasses="@cssClass" OnClick="() => InvokeSelectionAction(action)" /> <WButton Text="@action.Name" CssClasses="@cssClass" OnClick="() => InvokeSelectionAction(action)"/>
} }
} }
else else
@@ -100,42 +100,37 @@ else
<ContextMenuTemplate> <ContextMenuTemplate>
@foreach (var action in ContextActions) @foreach (var action in ContextActions)
{ {
if(!action.Filter.Invoke(context)) if (!action.Filter.Invoke(context))
continue; continue;
<a class="dropdown-item" href="#" @onclick:preventDefault @onclick="() => InvokeContextAction(action, context)"> <a class="dropdown-item" href="#" @onclick:preventDefault @onclick="() => InvokeContextAction(action, context)">
<i class="bx bx-sm @action.Icon text-@action.Color align-middle"></i> <i class="bx bx-sm @action.Icon text-@action.Color align-middle"></i>
<span class="align-middle ms-3">@action.Name</span> <span class="align-middle ms-3">@action.Name</span>
</a> </a>
} }
<a class="dropdown-item" href="#" @onclick:preventDefault @onclick="() => Move(context)">
<i class="bx bx-sm bx-move text-info align-middle"></i>
<span class="align-middle ms-3">Move</span>
</a>
</ContextMenuTemplate> </ContextMenuTemplate>
</FileView> </FileView>
</div> </div>
<SmartModal @ref="MoveModal" CssClasses="modal-lg modal-dialog-centered"> <SmartModal @ref="FolderSelectModal" CssClasses="modal-lg modal-dialog-centered">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Select a new location</h5> <h5 class="modal-title">@FolderSelectTitle</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="HideMove"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="HideFolderSelect"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<FileView @ref="MoveView" <FileView @ref="FolderSelectView"
FileAccess="MoveAccess" FileAccess="FolderSelectFileAccess"
Filter="FolderOnlyFilter" Filter="FolderSelectFilter"
ShowDate="false" ShowDate="false"
ShowSelect="false" ShowSelect="false"
ShowSize="false" ShowSize="false"
OnEntryClicked="OnFolderClicked" OnEntryClicked="EntryClickFolderSelect"
OnNavigateUpClicked="OnMoveUpClicked" OnNavigateUpClicked="NavigateUpFolderSelect"
EnableContextMenu="false"/> EnableContextMenu="false"/>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="HideMove">Close</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="HideFolderSelect">Cancel</button>
<button type="button" class="btn btn-primary" @onclick="FinishMove">Save changes</button> <button type="button" class="btn btn-primary" @onclick="SubmitFolderSelect">Submit</button>
</div> </div>
</SmartModal> </SmartModal>
} }
@@ -156,15 +151,14 @@ else
private FileEntry FileToEdit; private FileEntry FileToEdit;
private bool ShowEditor = false; private bool ShowEditor = false;
// Move // Folder select dialog
private SmartModal MoveModal; private bool FolderSelectIsOpen = false;
private BaseFileAccess MoveAccess; private SmartModal FolderSelectModal;
private FileView MoveView; private BaseFileAccess FolderSelectFileAccess;
private bool InMoveState = false; private string FolderSelectTitle;
private Func<FileEntry, bool> FolderOnlyFilter = entry => entry.IsDirectory; private Func<string, Task> FolderSelectResult;
private Func<FileEntry, Task> OnFolderClicked; private FileView FolderSelectView;
private Func<Task> OnMoveUpClicked; private Func<FileEntry, bool> FolderSelectFilter => entry => entry.IsDirectory;
private List<FileEntry> FilesToMove = new();
private Timer? UploadTokenTimer; private Timer? UploadTokenTimer;
@@ -174,17 +168,6 @@ else
ContextActions = await PluginService.GetImplementations<IFileManagerContextAction>(); ContextActions = await PluginService.GetImplementations<IFileManagerContextAction>();
SelectionActions = await PluginService.GetImplementations<IFileManagerSelectionAction>(); SelectionActions = await PluginService.GetImplementations<IFileManagerSelectionAction>();
CreateActions = await PluginService.GetImplementations<IFileManagerCreateAction>(); CreateActions = await PluginService.GetImplementations<IFileManagerCreateAction>();
OnFolderClicked = async entry =>
{
await MoveAccess.ChangeDirectory(entry.Name);
await MoveView.Refresh();
};
OnMoveUpClicked = async () =>
{
await MoveAccess.ChangeDirectory("..");
await MoveView.Refresh();
};
} }
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
@@ -250,7 +233,7 @@ else
private async Task InvokeSelectionAction(IFileManagerSelectionAction action) private async Task InvokeSelectionAction(IFileManagerSelectionAction action)
{ {
await action.Execute(FileAccess, this, View.Selection, ServiceProvider); await action.Execute(FileAccess, this, View.Selection, ServiceProvider);
// Refresh resets the selection // Refresh resets the selection
await View.Refresh(); await View.Refresh();
} }
@@ -259,7 +242,7 @@ else
{ {
await action.Execute(FileAccess, this, ServiceProvider); await action.Execute(FileAccess, this, ServiceProvider);
} }
private async Task OnSelectionChanged(FileEntry[] _) => await InvokeAsync(StateHasChanged); private async Task OnSelectionChanged(FileEntry[] _) => await InvokeAsync(StateHasChanged);
#region Navigation & Refreshing #region Navigation & Refreshing
@@ -306,7 +289,7 @@ else
} }
#endregion #endregion
#region File Editor #region File Editor
public async Task OpenEditor(FileEntry entry) public async Task OpenEditor(FileEntry entry)
@@ -325,75 +308,48 @@ else
#endregion #endregion
#region Move #region Selects
private async Task Move(FileEntry entry) public async Task OpenFolderSelect(string title, Func<string, Task> onResult)
{ {
await View.HideContextMenu(); if (FolderSelectIsOpen)
await HideFolderSelect();
FilesToMove.Clear(); FolderSelectResult = onResult;
FolderSelectTitle = title;
FilesToMove.Add(entry); FolderSelectFileAccess = FileAccess.Clone();
await FolderSelectFileAccess.SetDirectory("/");
await StartMove(); await FolderSelectModal.Show();
} }
private async Task MoveSelection() public async Task HideFolderSelect()
{ {
FilesToMove.Clear(); await FolderSelectModal.Hide();
FolderSelectIsOpen = false;
FilesToMove.AddRange(View.Selection); FolderSelectFileAccess.Dispose();
await StartMove();
} }
private async Task StartMove() private async Task SubmitFolderSelect()
{ {
// Cleanup if modal was removed in any other way var path = await FolderSelectFileAccess.GetCurrentDirectory();
if (InMoveState)
await HideMove();
// Prepare file access and show modal await HideFolderSelect();
InMoveState = true;
await InvokeAsync(StateHasChanged);
MoveAccess = FileAccess.Clone(); await FolderSelectResult.Invoke(path);
await MoveAccess.SetDirectory("/");
await MoveModal.Show();
} }
private async Task HideMove() private async Task NavigateUpFolderSelect()
{ {
await MoveModal.Hide(); await FolderSelectFileAccess.ChangeDirectory("..");
MoveAccess.Dispose(); await FolderSelectView.Refresh();
InMoveState = false;
await InvokeAsync(StateHasChanged);
} }
private async Task FinishMove() private async Task EntryClickFolderSelect(FileEntry entry)
{ {
var target = await MoveAccess.GetCurrentDirectory(); await FolderSelectFileAccess.ChangeDirectory(entry.Name);
await FolderSelectView.Refresh();
await HideMove();
await ToastService.CreateProgress("fileManagerMoveFile", "Moving items");
var i = 1;
foreach (var entry in FilesToMove)
{
await ToastService.ModifyProgress("fileManagerMoveFile", $"[{i}/{FilesToMove.Count}] Moving items");
await FileAccess.Move(entry, target + entry.Name);
i++;
}
await ToastService.RemoveProgress("fileManagerMoveFile");
await ToastService.Success($"Successfully moved {FilesToMove.Count} items");
await View.Refresh();
} }
#endregion #endregion