mirror of https://github.com/ErsatzTV/ErsatzTV.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
9.7 KiB
256 lines
9.7 KiB
using System.Globalization; |
|
using ErsatzTV.Application.MediaCards; |
|
using ErsatzTV.Application.MediaCollections; |
|
using ErsatzTV.Core; |
|
using ErsatzTV.Shared; |
|
using LanguageExt.UnsafeValueAccess; |
|
using MediatR; |
|
using Microsoft.AspNetCore.Components; |
|
using Microsoft.AspNetCore.Components.Web; |
|
using MudBlazor; |
|
|
|
namespace ErsatzTV.Pages; |
|
|
|
public class MultiSelectBase<T> : FragmentNavigationBase |
|
{ |
|
private Option<MediaCardViewModel> _recentlySelected = None; |
|
|
|
[Inject] |
|
protected IDialogService Dialog { get; set; } |
|
|
|
[Inject] |
|
protected ISnackbar Snackbar { get; set; } |
|
|
|
[Inject] |
|
protected ILogger<T> Logger { get; set; } |
|
|
|
[Inject] |
|
protected IMediator Mediator { get; set; } |
|
|
|
protected System.Collections.Generic.HashSet<MediaCardViewModel> SelectedItems { get; } = []; |
|
|
|
protected bool IsSelected(MediaCardViewModel card) => |
|
SelectedItems.Contains(card); |
|
|
|
protected bool IsSelectMode() => |
|
SelectedItems.Count != 0; |
|
|
|
protected string SelectionLabel() => |
|
$"{SelectedItems.Count} {(SelectedItems.Count == 1 ? "Item" : "Items")} Selected"; |
|
|
|
protected void SelectAllPageItems(IEnumerable<MediaCardViewModel> cards) |
|
{ |
|
ResetSelectionWithCards(SelectedItems, cards); |
|
StateHasChanged(); |
|
} |
|
|
|
protected void ClearSelection() |
|
{ |
|
SelectedItems.Clear(); |
|
_recentlySelected = None; |
|
StateHasChanged(); |
|
} |
|
|
|
protected virtual Task RefreshData(CancellationToken cancellationToken) => Task.CompletedTask; |
|
|
|
protected void SelectClicked( |
|
Func<List<MediaCardViewModel>> getSortedItems, |
|
MediaCardViewModel card, |
|
MouseEventArgs e) |
|
{ |
|
if (!SelectedItems.Remove(card)) |
|
{ |
|
if (e.ShiftKey && _recentlySelected.IsSome) |
|
{ |
|
List<MediaCardViewModel> sorted = getSortedItems(); |
|
|
|
int start = sorted.IndexOf(_recentlySelected.ValueUnsafe()); |
|
int finish = sorted.IndexOf(card); |
|
if (start > finish) |
|
{ |
|
(start, finish) = (finish, start); |
|
} |
|
|
|
for (int i = start; i < finish; i++) |
|
{ |
|
SelectedItems.Add(sorted[i]); |
|
} |
|
} |
|
|
|
_recentlySelected = card; |
|
SelectedItems.Add(card); |
|
} |
|
} |
|
|
|
protected Task AddSelectionToCollection() => AddItemsToCollection( |
|
SelectedItems.OfType<MovieCardViewModel>().Map(m => m.MovieId).ToList(), |
|
SelectedItems.OfType<TelevisionShowCardViewModel>().Map(s => s.TelevisionShowId).ToList(), |
|
SelectedItems.OfType<TelevisionSeasonCardViewModel>().Map(s => s.TelevisionSeasonId).ToList(), |
|
SelectedItems.OfType<TelevisionEpisodeCardViewModel>().Map(e => e.EpisodeId).ToList(), |
|
SelectedItems.OfType<ArtistCardViewModel>().Map(a => a.ArtistId).ToList(), |
|
SelectedItems.OfType<MusicVideoCardViewModel>().Map(mv => mv.MusicVideoId).ToList(), |
|
SelectedItems.OfType<OtherVideoCardViewModel>().Map(ov => ov.OtherVideoId).ToList(), |
|
SelectedItems.OfType<SongCardViewModel>().Map(s => s.SongId).ToList(), |
|
SelectedItems.OfType<ImageCardViewModel>().Map(i => i.ImageId).ToList(), |
|
SelectedItems.OfType<RemoteStreamCardViewModel>().Map(i => i.RemoteStreamId).ToList()); |
|
|
|
protected Task AddSelectionToPlaylist() => AddItemsToPlaylist( |
|
SelectedItems.OfType<MovieCardViewModel>().Map(m => m.MovieId).ToList(), |
|
SelectedItems.OfType<TelevisionShowCardViewModel>().Map(s => s.TelevisionShowId).ToList(), |
|
SelectedItems.OfType<TelevisionSeasonCardViewModel>().Map(s => s.TelevisionSeasonId).ToList(), |
|
SelectedItems.OfType<TelevisionEpisodeCardViewModel>().Map(e => e.EpisodeId).ToList(), |
|
SelectedItems.OfType<ArtistCardViewModel>().Map(a => a.ArtistId).ToList(), |
|
SelectedItems.OfType<MusicVideoCardViewModel>().Map(mv => mv.MusicVideoId).ToList(), |
|
SelectedItems.OfType<OtherVideoCardViewModel>().Map(ov => ov.OtherVideoId).ToList(), |
|
SelectedItems.OfType<SongCardViewModel>().Map(s => s.SongId).ToList(), |
|
SelectedItems.OfType<ImageCardViewModel>().Map(i => i.ImageId).ToList(), |
|
SelectedItems.OfType<RemoteStreamCardViewModel>().Map(i => i.RemoteStreamId).ToList()); |
|
|
|
protected async Task AddItemsToCollection( |
|
List<int> movieIds, |
|
List<int> showIds, |
|
List<int> seasonIds, |
|
List<int> episodeIds, |
|
List<int> artistIds, |
|
List<int> musicVideoIds, |
|
List<int> otherVideoIds, |
|
List<int> songIds, |
|
List<int> imageIds, |
|
List<int> remoteStreamIds, |
|
string entityName = "selected items") |
|
{ |
|
int count = movieIds.Count + showIds.Count + seasonIds.Count + episodeIds.Count + artistIds.Count + |
|
musicVideoIds.Count + otherVideoIds.Count + songIds.Count + imageIds.Count; |
|
|
|
var parameters = new DialogParameters |
|
{ { "EntityType", count.ToString(CultureInfo.InvariantCulture) }, { "EntityName", entityName } }; |
|
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; |
|
|
|
IDialogReference dialog = |
|
await Dialog.ShowAsync<AddToCollectionDialog>("Add To Collection", parameters, options); |
|
DialogResult result = await dialog.Result; |
|
if (result is { Canceled: false, Data: MediaCollectionViewModel collection }) |
|
{ |
|
var request = new AddItemsToCollection( |
|
collection.Id, |
|
movieIds, |
|
showIds, |
|
seasonIds, |
|
episodeIds, |
|
artistIds, |
|
musicVideoIds, |
|
otherVideoIds, |
|
songIds, |
|
imageIds, |
|
remoteStreamIds); |
|
|
|
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken); |
|
addResult.Match( |
|
Left: error => |
|
{ |
|
Snackbar.Add($"Unexpected error adding items to collection: {error.Value}"); |
|
Logger.LogError("Unexpected error adding items to collection: {Error}", error.Value); |
|
}, |
|
Right: _ => |
|
{ |
|
Snackbar.Add( |
|
$"Added {count} items to collection {collection.Name}", |
|
Severity.Success); |
|
ClearSelection(); |
|
}); |
|
} |
|
} |
|
|
|
protected async Task RemoveSelectionFromCollection(int collectionId) |
|
{ |
|
var parameters = new DialogParameters |
|
{ |
|
{ "EntityType", SelectedItems.Count.ToString(CultureInfo.InvariantCulture) }, |
|
{ "EntityName", "selected items" } |
|
}; |
|
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; |
|
|
|
IDialogReference dialog = await Dialog.ShowAsync<RemoveFromCollectionDialog>( |
|
"Remove From Collection", |
|
parameters, |
|
options); |
|
DialogResult result = await dialog.Result; |
|
if (result is { Canceled: false }) |
|
{ |
|
var itemIds = SelectedItems.Map(vm => vm.MediaItemId).ToList(); |
|
|
|
await Mediator.Send( |
|
new RemoveItemsFromCollection(collectionId) |
|
{ |
|
MediaItemIds = itemIds |
|
}, |
|
CancellationToken); |
|
|
|
await RefreshData(CancellationToken); |
|
ClearSelection(); |
|
} |
|
} |
|
|
|
protected async Task AddItemsToPlaylist( |
|
List<int> movieIds, |
|
List<int> showIds, |
|
List<int> seasonIds, |
|
List<int> episodeIds, |
|
List<int> artistIds, |
|
List<int> musicVideoIds, |
|
List<int> otherVideoIds, |
|
List<int> songIds, |
|
List<int> imageIds, |
|
List<int> remoteStreamIds, |
|
string entityName = "selected items") |
|
{ |
|
int count = movieIds.Count + showIds.Count + seasonIds.Count + episodeIds.Count + artistIds.Count + |
|
musicVideoIds.Count + otherVideoIds.Count + songIds.Count + imageIds.Count; |
|
|
|
var parameters = new DialogParameters |
|
{ { "EntityType", count.ToString(CultureInfo.InvariantCulture) }, { "EntityName", entityName } }; |
|
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; |
|
|
|
IDialogReference dialog = |
|
await Dialog.ShowAsync<AddToPlaylistDialog>("Add To Playlist", parameters, options); |
|
DialogResult result = await dialog.Result; |
|
if (result is { Canceled: false, Data: PlaylistViewModel playlist }) |
|
{ |
|
var request = new AddItemsToPlaylist( |
|
playlist.Id, |
|
movieIds, |
|
showIds, |
|
seasonIds, |
|
episodeIds, |
|
artistIds, |
|
musicVideoIds, |
|
otherVideoIds, |
|
songIds, |
|
imageIds, |
|
remoteStreamIds); |
|
|
|
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken); |
|
addResult.Match( |
|
Left: error => |
|
{ |
|
Snackbar.Add($"Unexpected error adding items to playlist: {error.Value}"); |
|
Logger.LogError("Unexpected error adding items to playlist: {Error}", error.Value); |
|
}, |
|
Right: _ => |
|
{ |
|
Snackbar.Add( |
|
$"Added {count} items to playlist {playlist.Name}", |
|
Severity.Success); |
|
ClearSelection(); |
|
}); |
|
} |
|
} |
|
|
|
internal static void ResetSelectionWithCards( |
|
ISet<MediaCardViewModel> selectedItems, |
|
IEnumerable<MediaCardViewModel> cards) |
|
{ |
|
selectedItems.Clear(); |
|
selectedItems.UnionWith(cards); |
|
} |
|
}
|
|
|