@page "/media/sources/local/{Id:int}/edit" @page "/media/sources/local/add" @using ErsatzTV.Application.Libraries @implements IDisposable @inject IDialogService _dialog @inject IEntityLocker _locker @inject IMediator _mediator @inject ILogger _logger @inject ISnackbar _snackbar @inject NavigationManager _navigationManager
@(IsEdit ? "Edit Local Library" : "Add Local Library") @foreach (LibraryMediaKind mediaKind in Enum.GetValues()) { @mediaKind } Add Path Save Changes
Library Paths Path @context.Path
@code { private readonly CancellationTokenSource _cts = new(); [Parameter] public int Id { get; set; } private readonly LocalLibraryEditViewModel _model = new(); private readonly LocalLibraryPathEditViewModel _newPath = new(); private EditContext _editContext; private ValidationMessageStore _messageStore; private bool IsEdit => Id != 0; protected override async Task OnParametersSetAsync() { if (IsEdit) { Option maybeLibrary = await _mediator.Send(new GetLocalLibraryById(Id), _cts.Token); await maybeLibrary.Match( async library => { _model.Id = library.Id; _model.Name = library.Name; _model.MediaKind = library.MediaKind; await LoadLibraryPaths(); }, () => { _navigationManager.NavigateTo("404"); return Task.CompletedTask; }); } else { _model.HasChanges = true; _model.Name = "New Local Library"; _model.MediaKind = LibraryMediaKind.Movies; _model.Paths = new List(); } } protected override void OnInitialized() { _locker.OnLibraryChanged += LockChanged; _editContext = new EditContext(_model); _messageStore = new ValidationMessageStore(_editContext); } private void LockChanged(object sender, EventArgs e) => InvokeAsync(StateHasChanged); private async Task LoadLibraryPaths() { _model.HasChanges = false; _model.Paths = await _mediator.Send(new GetLocalLibraryPaths(Id), _cts.Token) .Map(list => list.Map(vm => new LocalLibraryPathEditViewModel { Id = vm.Id, Path = vm.Path }).ToList()); } private async Task MoveLibraryPath(LocalLibraryPathEditViewModel libraryPath) { var parameters = new DialogParameters { { "MediaKind", _model.MediaKind }, { "SourceLibraryId", Id } }; var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; IDialogReference dialog = await _dialog.ShowAsync("Move Local Library Path", parameters, options); DialogResult result = await dialog.Result; if (!result.Canceled && result.Data is LocalLibraryViewModel library) { var request = new MoveLocalLibraryPath(libraryPath.Id, library.Id); Either moveResult = await _mediator.Send(request, _cts.Token); moveResult.Match( _ => _navigationManager.NavigateTo($"media/sources/local/{library.Id}/edit"), error => { _snackbar.Add(error.Value, Severity.Error); _logger.LogError("Unexpected error moving local library path: {Error}", error.Value); }); } } private async Task DeleteLibraryPath(LocalLibraryPathEditViewModel libraryPath) { if (libraryPath.Id == 0) { _model.HasChanges = true; _model.Paths.Remove(libraryPath); } int count = await _mediator.Send(new CountMediaItemsByLibraryPath(libraryPath.Id), _cts.Token); var parameters = new DialogParameters { { "EntityType", "library path" }, { "EntityName", libraryPath.Path }, { "DetailText", $"This library path contains {count} media items." }, { "DetailHighlight", count.ToString() } }; var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; IDialogReference dialog = await _dialog.ShowAsync("Delete Library Path", parameters, options); DialogResult result = await dialog.Result; if (!result.Canceled) { _model.HasChanges = true; _model.Paths.Remove(libraryPath); } } private void AddLibraryPath() { if (!string.IsNullOrWhiteSpace(_newPath.Path) && _model.Paths.All(p => NormalizePath(p.Path) != NormalizePath(_newPath.Path))) { _model.HasChanges = true; _model.Paths.Add(new LocalLibraryPathEditViewModel { Path = _newPath.Path }); } _newPath.Path = null; } private async Task SaveChangesAsync() { _messageStore.Clear(); if (_editContext.Validate()) { Either result = IsEdit ? await _mediator.Send(new UpdateLocalLibrary( _model.Id, _model.Name, _model.Paths.Map(p => new UpdateLocalLibraryPath(p.Id, p.Path)).ToList()), _cts.Token) : await _mediator.Send(new CreateLocalLibrary( _model.Name, _model.MediaKind, _model.Paths.Map(p => p.Path).ToList()), _cts.Token); result.Match( _ => _navigationManager.NavigateTo("media/sources/local"), error => { _snackbar.Add(error.Value, Severity.Error); _logger.LogError("Unexpected error saving local library: {Error}", error.Value); }); } } private string NormalizePath(string path) => Path.GetFullPath(new Uri(path).LocalPath) .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) .ToUpperInvariant(); void IDisposable.Dispose() { _locker.OnLibraryChanged -= LockChanged; _cts.Cancel(); _cts.Dispose(); } }