296 lines
9.9 KiB
Plaintext
296 lines
9.9 KiB
Plaintext
@using MoonCore.Helpers
|
|
@using MoonCore.Services
|
|
@using Moonlight.Core.Configuration
|
|
@using Moonlight.Core.Services
|
|
@using Moonlight.Features.FileManager.Interfaces
|
|
@using Moonlight.Features.FileManager.Models.Abstractions.FileAccess
|
|
@using Moonlight.Features.FileManager.Services
|
|
|
|
@inject AlertService AlertService
|
|
@inject ToastService ToastService
|
|
@inject ModalService ModalService
|
|
@inject FileManagerInteropService FileManagerInteropService
|
|
@inject SharedFileAccessService FileAccessService
|
|
@inject ConfigService<CoreConfiguration> ConfigService
|
|
@inject PluginService PluginService
|
|
@inject IServiceProvider ServiceProvider
|
|
|
|
@implements IDisposable
|
|
|
|
<div class="card card-body px-5">
|
|
<div class="d-flex justify-content-center justify-content-md-between">
|
|
<div class="d-none d-md-flex justify-content-start align-items-center">
|
|
<div class="badge badge-primary badge-lg fs-5 py-2 text-center">
|
|
@{
|
|
var parts = Path
|
|
.Split("/")
|
|
.Where(x => !string.IsNullOrEmpty(x))
|
|
.ToArray();
|
|
|
|
var i = 1;
|
|
}
|
|
|
|
<a href="#" class="text-white mx-1" @onclick:preventDefault @onclick="() => NavigateBackToLevel(0)">/</a>
|
|
|
|
@foreach (var part in parts)
|
|
{
|
|
var x = i + 0;
|
|
|
|
<a href="#" class="text-white mx-1" @onclick:preventDefault @onclick="() => NavigateBackToLevel(x)">@(part)</a>
|
|
<div class="mx-2 text-white">/</div>
|
|
|
|
i++;
|
|
}
|
|
</div>
|
|
</div>
|
|
<div class="d-flex justify-content-center justify-content-md-end align-items-center">
|
|
@if (View != null && View.Selection.Any())
|
|
{
|
|
foreach (var action in SelectionActions)
|
|
{
|
|
var cssClass = $"btn btn-{action.Color} mx-2";
|
|
|
|
<WButton Text="@action.Name" CssClasses="@cssClass" OnClick="() => InvokeSelectionAction(action)"/>
|
|
}
|
|
}
|
|
else
|
|
{
|
|
<WButton OnClick="ManualRefresh" CssClasses="btn btn-icon btn-light-info">
|
|
<i class="bx bx-sm bx-refresh"></i>
|
|
</WButton>
|
|
<label for="fileManagerSelect" class="btn btn-light-primary mx-2">Upload</label>
|
|
<input id="fileManagerSelect" type="file" hidden="hidden" multiple/>
|
|
<div class="dropdown">
|
|
<a class="btn btn-primary dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
New
|
|
</a>
|
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
|
@foreach (var action in CreateActions)
|
|
{
|
|
<li>
|
|
<a href="#" class="dropdown-item" @onclick:preventDefault @onclick="() => InvokeCreateAction(action)">
|
|
<i class="bx bx-sm @action.Icon text-@action.Color me-2 align-middle"></i>
|
|
<span class="align-middle fs-6">@action.Name</span>
|
|
</a>
|
|
</li>
|
|
}
|
|
</ul>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if (ShowEditor)
|
|
{
|
|
<div class="card card-body px-2 py-2 mt-5">
|
|
<FileEditor @ref="Editor" FileAccess="FileAccess" File="FileToEdit" OnClosed="CloseEditor"/>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div id="fileManagerUpload" class="card card-body px-5 py-3 mt-5">
|
|
<FileView @ref="View"
|
|
FileAccess="FileAccess"
|
|
OnEntryClicked="OnEntryClicked"
|
|
OnNavigateUpClicked="OnNavigateUpClicked"
|
|
OnSelectionChanged="OnSelectionChanged"
|
|
EnableContextMenu="true"
|
|
ShowUploadPrompt="true">
|
|
<ContextMenuTemplate>
|
|
@foreach (var action in ContextActions)
|
|
{
|
|
if (!action.Filter.Invoke(context))
|
|
continue;
|
|
|
|
<a class="dropdown-item" href="#" @onclick:preventDefault @onclick="() => InvokeContextAction(action, context)">
|
|
<i class="bx bx-sm @action.Icon text-@action.Color align-middle"></i>
|
|
<span class="align-middle ms-3">@action.Name</span>
|
|
</a>
|
|
}
|
|
</ContextMenuTemplate>
|
|
</FileView>
|
|
</div>
|
|
}
|
|
|
|
@code
|
|
{
|
|
[Parameter] public BaseFileAccess FileAccess { get; set; }
|
|
|
|
public FileView View { get; private set; }
|
|
private string Path = "/";
|
|
|
|
private IFileManagerContextAction[] ContextActions;
|
|
private IFileManagerSelectionAction[] SelectionActions;
|
|
private IFileManagerCreateAction[] CreateActions;
|
|
|
|
// Editor
|
|
private FileEditor Editor;
|
|
private FileEntry FileToEdit;
|
|
private bool ShowEditor = false;
|
|
|
|
private Timer? UploadTokenTimer;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
// Load plugin ui and options
|
|
ContextActions = await PluginService.GetImplementations<IFileManagerContextAction>();
|
|
SelectionActions = await PluginService.GetImplementations<IFileManagerSelectionAction>();
|
|
CreateActions = await PluginService.GetImplementations<IFileManagerCreateAction>();
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (!firstRender)
|
|
return;
|
|
|
|
// Setup upload url update timer
|
|
UploadTokenTimer = new(async _ =>
|
|
{
|
|
await FileAccessService.Register(FileAccess);
|
|
var token = await FileAccessService.GenerateToken(FileAccess);
|
|
var url = $"/api/upload?token={token}";
|
|
|
|
await FileManagerInteropService.UpdateUrl("fileManager", url);
|
|
}, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
|
|
|
|
// Create initial url
|
|
await FileAccessService.Register(FileAccess);
|
|
var token = await FileAccessService.GenerateToken(FileAccess);
|
|
var url = $"/api/upload?token={token}";
|
|
|
|
// Refresh the file view when a upload is completed
|
|
FileManagerInteropService.OnUploadStateChanged += async () => { await View.Refresh(); };
|
|
|
|
// Initialize drop area & file select
|
|
await FileManagerInteropService.UpdateUrl("fileManager", url);
|
|
await FileManagerInteropService.InitDropzone("fileManagerUpload", "fileManager");
|
|
await FileManagerInteropService.InitFileSelect("fileManagerSelect", "fileManager");
|
|
}
|
|
|
|
private async Task OnEntryClicked(FileEntry entry)
|
|
{
|
|
if (entry.IsFile)
|
|
{
|
|
var fileSizeInKilobytes = ByteSizeValue.FromBytes(entry.Size).KiloBytes;
|
|
|
|
if (fileSizeInKilobytes > ConfigService.Get().Customisation.FileManager.MaxFileOpenSize)
|
|
{
|
|
await ToastService.Danger("Unable to open file as it exceeds the max file size limit");
|
|
return;
|
|
}
|
|
|
|
await OpenEditor(entry);
|
|
}
|
|
else
|
|
{
|
|
await FileAccess.ChangeDirectory(entry.Name);
|
|
await View.Refresh();
|
|
|
|
await Refresh();
|
|
}
|
|
}
|
|
|
|
private async Task InvokeContextAction(IFileManagerContextAction contextAction, FileEntry entry)
|
|
{
|
|
await View.HideContextMenu();
|
|
|
|
await contextAction.Execute(FileAccess, this, entry, ServiceProvider);
|
|
}
|
|
|
|
private async Task InvokeSelectionAction(IFileManagerSelectionAction action)
|
|
{
|
|
await action.Execute(FileAccess, this, View.Selection, ServiceProvider);
|
|
|
|
// Refresh resets the selection
|
|
await View.Refresh();
|
|
}
|
|
|
|
private async Task InvokeCreateAction(IFileManagerCreateAction action)
|
|
{
|
|
await action.Execute(FileAccess, this, ServiceProvider);
|
|
}
|
|
|
|
private async Task OnSelectionChanged(FileEntry[] _) => await InvokeAsync(StateHasChanged);
|
|
|
|
#region Navigation & Refreshing
|
|
|
|
private async Task OnNavigateUpClicked()
|
|
{
|
|
await FileAccess.ChangeDirectory("..");
|
|
await View.Refresh();
|
|
|
|
await Refresh();
|
|
}
|
|
|
|
private async Task NavigateBackToLevel(int level)
|
|
{
|
|
if (ShowEditor) // Ignore navigation events while the editor is open
|
|
return;
|
|
|
|
var path = await FileAccess.GetCurrentDirectory();
|
|
|
|
var parts = path.Split("/");
|
|
var pathToNavigate = string.Join("/", parts.Take(level + 1)) + "/";
|
|
|
|
await FileAccess.SetDirectory(pathToNavigate);
|
|
await View.Refresh();
|
|
await Refresh();
|
|
}
|
|
|
|
private async Task ManualRefresh()
|
|
{
|
|
if (ShowEditor) // Ignore refresh while editor is open
|
|
return;
|
|
|
|
await View.Refresh();
|
|
await Refresh();
|
|
|
|
await ToastService.Info("Refreshed");
|
|
}
|
|
|
|
private async Task Refresh()
|
|
{
|
|
Path = await FileAccess.GetCurrentDirectory();
|
|
|
|
await InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region File Editor
|
|
|
|
public async Task OpenEditor(FileEntry entry)
|
|
{
|
|
FileToEdit = entry;
|
|
ShowEditor = true;
|
|
|
|
await InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
public async Task CloseEditor()
|
|
{
|
|
ShowEditor = false;
|
|
await InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
#endregion
|
|
|
|
public async Task OpenFolderSelect(string title, Func<string, Task> onResult)
|
|
{
|
|
await ModalService.Launch<FolderSelectModal>(cssClasses: "modal-lg modal-dialog-centered", buildAttributes: parameters =>
|
|
{
|
|
parameters.Add("Title", title);
|
|
parameters.Add("OnResult", onResult);
|
|
parameters.Add("FileAccess", FileAccess.Clone());
|
|
});
|
|
}
|
|
|
|
public async void Dispose()
|
|
{
|
|
if (UploadTokenTimer != null)
|
|
await UploadTokenTimer.DisposeAsync();
|
|
|
|
await FileAccessService.Unregister(FileAccess);
|
|
}
|
|
} |