356 lines
No EOL
11 KiB
Text
356 lines
No EOL
11 KiB
Text
@using LANCommander.SDK.Enums
|
|
@using LANCommander.Server.ImportExport
|
|
@using LANCommander.Server.ImportExport.Factories
|
|
@using LANCommander.Server.ImportExport.Models
|
|
@inherits FeedbackComponent<ImportDialogOptions>
|
|
@inject ArchiveService ArchiveService
|
|
@inject StorageLocationService StorageLocationService
|
|
@inject ImportContextFactory ImportContextFactory
|
|
@inject IMessageService MessageService
|
|
@inject ILogger<ImportUploadDialog> Logger
|
|
|
|
@if (_stage == ImportStage.Upload)
|
|
{
|
|
<Flex Vertical Gap="FlexGap.Small">
|
|
<ChunkUploader
|
|
@ref="ChunkUploader"
|
|
Accept=".lcx"
|
|
@bind-File="File"
|
|
@bind-Status="Status"
|
|
StorageLocationId="StorageLocation.Id"
|
|
OnUploadCompleted="OnUploadCompleted"
|
|
OnUploadError="OnUploadError">
|
|
<Text>
|
|
<p>Drag and Drop</p>
|
|
<p>or</p>
|
|
<p>
|
|
<Button Type="@ButtonType.Primary" Style="margin-top: 8px;">Browse</Button>
|
|
</p>
|
|
</Text>
|
|
<Hint>@Options.Hint</Hint>
|
|
</ChunkUploader>
|
|
|
|
<StorageLocationSelector @bind-Value="StorageLocation" Type="StorageLocationType.Archive"/>
|
|
</Flex>
|
|
|
|
<Flex Justify="FlexJustify.End" Gap="FlexGap.Small" Style="margin-top: 16px;">
|
|
<FilePickerButton
|
|
EntrySelectable="@(entry => !String.IsNullOrWhiteSpace(entry.Name) && entry.Name.ToLower().EndsWith(".lcx"))"
|
|
OnSelected="OnLocalFileSelected"
|
|
Root="@RootPath"
|
|
Disabled="@(Status != "")">
|
|
Use Local File
|
|
</FilePickerButton>
|
|
<Button OnClick="Upload" Disabled="@(File == null || Status != "")" Type="@ButtonType.Primary">Upload</Button>
|
|
<Button OnClick="Cancel">Cancel</Button>
|
|
</Flex>
|
|
}
|
|
else if (_stage == ImportStage.RecordSelection)
|
|
{
|
|
<Tree TItem="string"
|
|
Checkable
|
|
Multiple
|
|
CheckOnClickNode="false"
|
|
@bind-CheckedKeys="_selectedKeys">
|
|
@foreach (var group in ImportItems.GroupBy(i => i.Type))
|
|
{
|
|
var size = group.Sum(i => i.Size);
|
|
|
|
<TreeNode Title="@($"{group.Key} ({group.Count()})")">
|
|
<TitleTemplate>
|
|
<GridRow>
|
|
<GridCol Flex=@("auto")>
|
|
@group.Key.GetDisplayName() (@group.Count())
|
|
</GridCol>
|
|
|
|
@if (size > 0)
|
|
{
|
|
<GridCol Flex=@("none")>
|
|
<ByteSize Value="@size" />
|
|
</GridCol>
|
|
}
|
|
</GridRow>
|
|
</TitleTemplate>
|
|
<ChildContent>
|
|
@foreach (var item in group)
|
|
{
|
|
<TreeNode Key="@item.Key">
|
|
<TitleTemplate>
|
|
<GridRow>
|
|
<GridCol Flex=@("auto")>
|
|
@item.Name
|
|
</GridCol>
|
|
|
|
@if (item.Size > 0)
|
|
{
|
|
<GridCol Flex=@("none")>
|
|
<ByteSize Value="@item.Size"/>
|
|
</GridCol>
|
|
}
|
|
</GridRow>
|
|
</TitleTemplate>
|
|
</TreeNode>
|
|
}
|
|
</ChildContent>
|
|
</TreeNode>
|
|
}
|
|
</Tree>
|
|
|
|
<Flex Justify="FlexJustify.End" Gap="FlexGap.Small" Style="margin-top: 16px;">
|
|
<Button OnClick="Import" Type="@ButtonType.Primary">Import</Button>
|
|
<Button OnClick="Cancel">Cancel</Button>
|
|
</Flex>
|
|
}
|
|
else if (_stage == ImportStage.Importing)
|
|
{
|
|
<Flex Align="FlexAlign.Center" Direction="FlexDirection.Vertical" Gap="FlexGap.Middle">
|
|
<Progress Type="ProgressType.Circle" Percent="_importProgress" />
|
|
|
|
@if (!String.IsNullOrWhiteSpace(_currentImportItemName))
|
|
{
|
|
<Text>Importing @_currentImportItemName</Text>
|
|
}
|
|
|
|
<Flex Gap="FlexGap.Small" Direction="FlexDirection.Vertical">
|
|
@foreach (var error in _errors)
|
|
{
|
|
<Alert Type="AlertType.Error" Message="@error" />
|
|
}
|
|
</Flex>
|
|
</Flex>
|
|
}
|
|
else if (_stage == ImportStage.Complete)
|
|
{
|
|
<Result Status="ResultStatus.Success" Title="Import Complete!">
|
|
<Extra>
|
|
<Button Type="ButtonType.Primary" OnClick="CloseFeedbackAsync">Close</Button>
|
|
</Extra>
|
|
</Result>
|
|
}
|
|
else if (_stage == ImportStage.Failed)
|
|
{
|
|
<Result Status="ResultStatus.Error" Title="Import Failed!">
|
|
<Extra>
|
|
<Flex Gap="FlexGap.Small" Direction="FlexDirection.Vertical">
|
|
@foreach (var error in _errors)
|
|
{
|
|
<Alert Type="AlertType.Error" Message="@error" />
|
|
}
|
|
|
|
<Button Type="ButtonType.Primary" OnClick="CloseFeedbackAsync">Close</Button>
|
|
</Flex>
|
|
</Extra>
|
|
</Result>
|
|
}
|
|
|
|
@code {
|
|
enum ImportStage
|
|
{
|
|
Upload,
|
|
RecordSelection,
|
|
Importing,
|
|
Complete,
|
|
Failed,
|
|
};
|
|
|
|
IBrowserFile File;
|
|
ChunkUploader ChunkUploader;
|
|
StorageLocation StorageLocation = new();
|
|
ImportContext ImportContext;
|
|
|
|
IEnumerable<IImportItemInfo> ImportItems = new List<IImportItemInfo>();
|
|
|
|
string RootPath = Path.GetPathRoot(Directory.GetCurrentDirectory());
|
|
|
|
bool IsValid = false;
|
|
string Filename;
|
|
string Status;
|
|
|
|
ImportStage _stage = ImportStage.Upload;
|
|
string[] _selectedKeys;
|
|
|
|
List<string> _errors = new();
|
|
|
|
int _importProgress = -1;
|
|
int _importTotal = 0;
|
|
string _currentImportItemName = String.Empty;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
ImportContext = ImportContextFactory.Create();
|
|
|
|
ImportContext.OnImportStarted.EventRaised += ImportStarted;
|
|
ImportContext.OnImportComplete.EventRaised += ImportComplete;
|
|
ImportContext.OnImportStatusUpdate.EventRaised += ImportStatusUpdate;
|
|
ImportContext.OnImportError.EventRaised += ImportError;
|
|
|
|
StorageLocation = await StorageLocationService.FirstAsync(l => l.Default && l.Type == StorageLocationType.Archive);
|
|
}
|
|
|
|
private async Task ImportStarted(ImportStatusUpdate status)
|
|
{
|
|
_stage = ImportStage.Importing;
|
|
_importProgress = -1;
|
|
|
|
await Task.Yield();
|
|
await InvokeStateHasChangedAsync();
|
|
}
|
|
|
|
private async Task ImportComplete(ImportStatusUpdate status)
|
|
{
|
|
_stage = ImportStage.Complete;
|
|
_importProgress = 100;
|
|
|
|
await Clear();
|
|
ImportContext.Dispose();
|
|
|
|
await Task.Yield();
|
|
await InvokeStateHasChangedAsync();
|
|
}
|
|
|
|
private async Task ImportStatusUpdate(ImportStatusUpdate status) => await UpdateProgress(status);
|
|
|
|
private async Task ImportError(ImportStatusUpdate status)
|
|
{
|
|
if (status.Error != null)
|
|
_errors.Add(status.Error);
|
|
|
|
await Task.Yield();
|
|
await InvokeStateHasChangedAsync();
|
|
}
|
|
|
|
private async Task UpdateProgress(ImportStatusUpdate? status)
|
|
{
|
|
if (status != null)
|
|
{
|
|
_importProgress = status.Total > 0 ? (int)Math.Floor(status.Index / (decimal)status.Total * 100) : 100;
|
|
_currentImportItemName = $"{status.CurrentItem?.Type.GetDisplayName()}: {status.CurrentItem?.Name}";
|
|
|
|
await Task.Yield();
|
|
await InvokeStateHasChangedAsync();
|
|
}
|
|
}
|
|
|
|
async Task Upload()
|
|
{
|
|
await ChunkUploader.Start();
|
|
}
|
|
|
|
async Task Import()
|
|
{
|
|
_stage = ImportStage.Importing;
|
|
|
|
var selectedGuids = _selectedKeys
|
|
.Select(k => Guid.TryParse(k, out var id) ? (Guid?)id : null)
|
|
.Where(id => id.HasValue)
|
|
.Select(g => g!.Value);
|
|
|
|
try
|
|
{
|
|
await ImportContext.PrepareImportQueueAsync(selectedGuids, StorageLocation.Id);
|
|
await ImportContext.ImportQueueAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_stage = ImportStage.Failed;
|
|
Logger?.LogError(ex, "An unknown error occurred while trying to import");
|
|
ImportContext.Dispose();
|
|
}
|
|
}
|
|
|
|
async Task Clear()
|
|
{
|
|
await ChunkUploader.Clear();
|
|
}
|
|
|
|
async Task Cancel()
|
|
{
|
|
await ChunkUploader.Clear();
|
|
await CloseFeedbackAsync();
|
|
}
|
|
|
|
public override async Task OnFeedbackCancelAsync(ModalClosingEventArgs args)
|
|
{
|
|
await Clear();
|
|
|
|
ImportContext.Dispose();
|
|
|
|
await base.OnFeedbackCancelAsync(args);
|
|
}
|
|
|
|
public override async Task OnFeedbackOkAsync(ModalClosingEventArgs args)
|
|
{
|
|
await ChunkUploader.Clear();
|
|
|
|
ImportContext.Dispose();
|
|
|
|
await base.OnFeedbackOkAsync(args);
|
|
}
|
|
|
|
public async Task OnUploadCompleted(string data)
|
|
{
|
|
if (Guid.TryParse(data, out var objectKey))
|
|
{
|
|
try
|
|
{
|
|
var archivePath = await ArchiveService.GetArchiveFileLocationAsync(objectKey.ToString());
|
|
|
|
ImportContext.TrackUploadedArchive(archivePath);
|
|
|
|
ImportItems = await ImportContext.InitializeImportAsync(archivePath);
|
|
|
|
_selectedKeys = ImportItems.Select(i => i.Key).ToArray();
|
|
|
|
_stage = ImportStage.RecordSelection;
|
|
|
|
await InvokeAsync(StateHasChanged);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger?.LogError(ex, "An unknown error occurred while trying to import");
|
|
MessageService.Error("An unknown error occurred while trying to import");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
await InvokeAsync(StateHasChanged);
|
|
|
|
MessageService.Error("Import file failed to upload!");
|
|
Logger.LogError("Import file failed to upload!");
|
|
|
|
await (base.OkCancelRef?.OnCancel?.Invoke() ?? Task.CompletedTask);
|
|
await CloseFeedbackAsync();
|
|
}
|
|
|
|
await Clear();
|
|
}
|
|
|
|
public async Task OnUploadError(string message)
|
|
{
|
|
await InvokeAsync(StateHasChanged);
|
|
|
|
MessageService.Error("An error occurred while trying to import");
|
|
Logger?.LogError($"An error occurred while trying to import: {message}");
|
|
|
|
await CloseFeedbackAsync();
|
|
}
|
|
|
|
public async Task OnLocalFileSelected(string path)
|
|
{
|
|
try
|
|
{
|
|
ImportItems = await ImportContext.InitializeImportAsync(path);
|
|
|
|
_selectedKeys = ImportItems.Select(i => i.Key).ToArray();
|
|
|
|
_stage = ImportStage.RecordSelection;
|
|
|
|
await InvokeAsync(StateHasChanged);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger?.LogError(ex, "An unknown error occurred while trying to import");
|
|
MessageService.Error("An unknown error occurred while trying to import");
|
|
}
|
|
}
|
|
} |