- Chat messages are now loaded based on scroll position - Read status is now updated and set by last message read - Reworked script importing by using ScriptProvider
237 lines
7.4 KiB
Text
237 lines
7.4 KiB
Text
@using LANCommander.UI.Providers
|
|
@using Microsoft.AspNetCore.Components.Forms
|
|
@using Microsoft.JSInterop
|
|
@inject HttpClient HttpClient
|
|
@inject NavigationManager Navigator
|
|
@inject ScriptProvider ScriptProvider
|
|
@inject IMessageService MessageService
|
|
@implements IAsyncDisposable
|
|
@namespace LANCommander.UI.Components
|
|
|
|
<div class="ant-upload ant-upload-select-text ant-upload-drag ant-upload-select chunk-uploader" data-status="@CurrentProgressStatus">
|
|
<Progress Type="ProgressType.Circle" Percent="_progress" Status="@CurrentProgressStatus" Class="uploader-progress" />
|
|
|
|
<span class="uploader-progress-rate"></span>
|
|
|
|
<label tabindex="0" class="ant-upload" style="display: grid;" for="ChunkFileInput" role="button">
|
|
<InputFile @ref="_fileInput" @key="@_uploaderId" id="@($"ChunkFileInput-{_uploaderId}")" OnChange="OnFileSelected" accept="@Accept" style="position: absolute; width: 100%; height: 100%; opacity: 0; top: 0;left: 0; z-index:2;" />
|
|
|
|
<div class="ant-upload-drag-container">
|
|
<p class="ant-upload-drag-icon">
|
|
<Icon Type="@IconType.Outline.Upload" />
|
|
</p>
|
|
<p class="ant-upload-text">
|
|
@Text
|
|
</p>
|
|
<p class="ant-upload-hint">
|
|
@Hint
|
|
</p>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
|
|
|
|
@if (File != null)
|
|
{
|
|
<div class="ant-upload-list ant-upload-list-picture">
|
|
<div class="ant-upload-list-picture-container" style="display: list-item;">
|
|
<div class="ant-upload-list-item ant-upload-list-item-done ant-upload-list-item-list-type-picture">
|
|
<div class="ant-upload-list-item-info">
|
|
<span class="ant-upload-span">
|
|
<div class="ant-upload-list-item-thumbnail ant-upload-list-item-file">
|
|
<Icon Type="@IconType.Outline.FileZip" />
|
|
</div>
|
|
<span target="_blank" rel="noopener noreferrer" class="ant-upload-list-item-name" title="@File.Name (@ByteSizeLib.ByteSize.FromBytes(File.Size))">
|
|
@File.Name (<ByteSize Value="File.Size" />)
|
|
</span>
|
|
<span class="ant-upload-list-item-card-actions picture">
|
|
<Button Type="ButtonType.Text" Size="ButtonSize.Small" Icon="@IconType.Outline.Delete" OnClick="Clear" Disabled="@(CurrentProgressStatus != ProgressStatus.Normal)" />
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@code {
|
|
[Parameter] public RenderFragment? Text { get; set; }
|
|
[Parameter] public RenderFragment? Hint { get; set; }
|
|
[Parameter] public string? Accept { get; set; }
|
|
[Parameter] public string? ObjectKey { get; set; }
|
|
[Parameter] public EventCallback<string> OnUploadCompleted { get; set; }
|
|
[Parameter] public EventCallback<string> OnUploadError { get; set; }
|
|
[Parameter] public string? Status { get; set; }
|
|
[Parameter] public EventCallback<string> StatusChanged { get; set; }
|
|
[Parameter] public IBrowserFile? File { get; set; }
|
|
[Parameter] public EventCallback<IBrowserFile> FileChanged { get; set; }
|
|
[Parameter] public Guid StorageLocationId { get; set; }
|
|
[Parameter] public EventCallback<Guid> StorageLocationIdChanged { get; set; }
|
|
|
|
readonly Guid _uploaderId = Guid.NewGuid();
|
|
|
|
InputFile? _fileInput;
|
|
|
|
int _progress = 0;
|
|
bool _uploading = false;
|
|
bool _finished = false;
|
|
bool _clearInput = false;
|
|
double _speed = 0;
|
|
|
|
IJSObjectReference? _uploaderInterop;
|
|
|
|
ProgressStatus CurrentProgressStatus
|
|
{
|
|
get
|
|
{
|
|
switch (Status)
|
|
{
|
|
case "Complete":
|
|
return ProgressStatus.Success;
|
|
case "Uploading":
|
|
return ProgressStatus.Active;
|
|
default:
|
|
return ProgressStatus.Normal;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
HttpClient.BaseAddress = new Uri(Navigator.BaseUri);
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (firstRender)
|
|
_uploaderInterop ??= await ScriptProvider.ImportModuleAsync<ChunkUploader>();
|
|
}
|
|
|
|
public async Task Start()
|
|
{
|
|
if (_uploaderInterop is null)
|
|
return;
|
|
|
|
_uploading = true;
|
|
|
|
await ChangeStatus("Uploading");
|
|
|
|
ushort i = 0;
|
|
|
|
while (i < 20)
|
|
{
|
|
if (_fileInput != null)
|
|
{
|
|
if (!String.IsNullOrWhiteSpace(ObjectKey) && ObjectKey != Guid.Empty.ToString())
|
|
await _uploaderInterop.InvokeVoidAsync("Init", $"ChunkFileInput-{_uploaderId}", StorageLocationId, ObjectKey);
|
|
else
|
|
await _uploaderInterop.InvokeVoidAsync("Init", $"ChunkFileInput-{_uploaderId}", StorageLocationId, "");
|
|
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
|
|
await Task.Delay(500);
|
|
}
|
|
|
|
var dotNetReference = DotNetObjectReference.Create(this);
|
|
|
|
await _uploaderInterop.InvokeVoidAsync("Upload", dotNetReference);
|
|
|
|
await InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
public async Task Clear()
|
|
{
|
|
if (_uploaderInterop is null)
|
|
return;
|
|
|
|
await _uploaderInterop.InvokeVoidAsync("Clear");
|
|
|
|
File = null;
|
|
|
|
if (FileChanged.HasDelegate)
|
|
await FileChanged.InvokeAsync(null);
|
|
|
|
Status = "";
|
|
|
|
if (StatusChanged.HasDelegate)
|
|
await StatusChanged.InvokeAsync(Status);
|
|
|
|
StateHasChanged();
|
|
}
|
|
|
|
async void OnFileSelected(InputFileChangeEventArgs args)
|
|
{
|
|
var file = args.File;
|
|
|
|
if (!String.IsNullOrWhiteSpace(Accept))
|
|
{
|
|
var acceptedFileExtensions = Accept.Split(',').Select(x => x.Trim()).ToArray();
|
|
|
|
if (!acceptedFileExtensions.Any(x => file.Name.ToLower().EndsWith(x.ToLower())))
|
|
{
|
|
MessageService.Error($"Only {String.Join(", ", acceptedFileExtensions)} can be selected!");
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
File = file;
|
|
|
|
if (FileChanged.HasDelegate)
|
|
await FileChanged.InvokeAsync(File);
|
|
|
|
await ChangeStatus("");
|
|
}
|
|
|
|
async Task ChangeStatus(string status)
|
|
{
|
|
Status = status;
|
|
|
|
if (StatusChanged.HasDelegate)
|
|
await StatusChanged.InvokeAsync(Status);
|
|
}
|
|
|
|
[JSInvokable]
|
|
public async Task JSOnUploadComplete(string objectKey)
|
|
{
|
|
_uploading = false;
|
|
_finished = true;
|
|
await ChangeStatus("Complete");
|
|
|
|
if (OnUploadCompleted.HasDelegate)
|
|
await OnUploadCompleted.InvokeAsync(objectKey);
|
|
}
|
|
|
|
[JSInvokable]
|
|
public async Task JSOnUploadError(string message)
|
|
{
|
|
if (OnUploadError.HasDelegate)
|
|
await OnUploadError.InvokeAsync(message);
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (_fileInput is not null)
|
|
await CastAndDispose(_fileInput);
|
|
|
|
if (_uploaderInterop is not null)
|
|
await _uploaderInterop.DisposeAsync();
|
|
|
|
if (HttpClient is not null)
|
|
await CastAndDispose(HttpClient);
|
|
|
|
return;
|
|
|
|
static async ValueTask CastAndDispose(IDisposable resource)
|
|
{
|
|
if (resource is IAsyncDisposable resourceAsyncDisposable)
|
|
await resourceAsyncDisposable.DisposeAsync();
|
|
else
|
|
resource.Dispose();
|
|
}
|
|
}
|
|
|
|
}
|