Added playlists view context menu for rename, lock/unlock, and delete.

This commit is contained in:
2026-08-09 17:30:38 -04:00
parent cf8a40ee52
commit 91e4d88643
6 changed files with 154 additions and 15 deletions

View File

@@ -82,6 +82,11 @@ public class PlaylistManager : IPlaylistManager
playlistRepository.Delete(playlist); playlistRepository.Delete(playlist);
if (CurrentPlaylist == playlist)
{
CurrentPlaylist = _playlists.Count > 0 ? _playlists[0] : null;
}
PlaylistRemoved?.Invoke(this, new(playlist)); PlaylistRemoved?.Invoke(this, new(playlist));
} }

View File

@@ -681,18 +681,6 @@ public partial class PlaylistDetailViewModel : ViewModelBase
return; return;
_playlistManager.RemovePlaylist(Playlist); _playlistManager.RemovePlaylist(Playlist);
if (_playlistManager.CurrentPlaylist == Playlist)
{
if (_playlistManager.Playlists.Count > 0)
{
_playlistManager.CurrentPlaylist = _playlistManager.Playlists.ElementAt(0);
}
else
{
_playlistManager.CurrentPlaylist = null;
}
}
} }
private void SortAllSongs(SortItem? sortItem) private void SortAllSongs(SortItem? sortItem)

View File

@@ -35,6 +35,19 @@ public partial class PlaylistItemViewModel : ObservableObject
} }
} }
private bool _isLocked;
public bool IsLocked
{
get
{
return _isLocked;
}
set
{
SetProperty(ref _isLocked, value);
}
}
public PlaylistItemViewModel(Playlist playlist) public PlaylistItemViewModel(Playlist playlist)
{ {
Playlist = playlist; Playlist = playlist;
@@ -55,6 +68,7 @@ public partial class PlaylistItemViewModel : ObservableObject
private void UpdateMetaData() private void UpdateMetaData()
{ {
Name = Playlist.Name ?? "Untitled Playlist"; Name = Playlist.Name ?? "Untitled Playlist";
IsLocked = Playlist.IsLocked;
TimeSpan total = TimeSpan.FromSeconds(Playlist.Songs.Sum(s => s.Song.Length.TotalSeconds)); TimeSpan total = TimeSpan.FromSeconds(Playlist.Songs.Sum(s => s.Song.Length.TotalSeconds));
Summary = $"{Playlist.Songs.Count} songs / {total:h\\:mm\\:ss}"; Summary = $"{Playlist.Songs.Count} songs / {total:h\\:mm\\:ss}";

View File

@@ -1,9 +1,12 @@
using Harmonia.Core.Player; using CommunityToolkit.Mvvm.Input;
using Harmonia.Core.Player;
using Harmonia.Core.Playlists; using Harmonia.Core.Playlists;
using Harmonia.WinUI.Messaging;
using Microsoft.UI.Dispatching; using Microsoft.UI.Dispatching;
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
namespace Harmonia.WinUI.ViewModels; namespace Harmonia.WinUI.ViewModels;
@@ -11,6 +14,7 @@ public partial class PlaylistsViewModel : ViewModelBase
{ {
private readonly IPlaylistManager _playlistManager; private readonly IPlaylistManager _playlistManager;
private readonly IAudioPlayer _audioPlayer; private readonly IAudioPlayer _audioPlayer;
private readonly IMessageService _messageService;
private readonly DispatcherQueue _dispatcherQueue; private readonly DispatcherQueue _dispatcherQueue;
private ObservableCollection<PlaylistItemViewModel> _playlists = []; private ObservableCollection<PlaylistItemViewModel> _playlists = [];
@@ -55,12 +59,31 @@ public partial class PlaylistsViewModel : ViewModelBase
} }
} }
public PlaylistsViewModel(IPlaylistManager playlistManager, IAudioPlayer audioPlayer) private PlaylistItemViewModel? _flyoutPlaylist = null;
public PlaylistItemViewModel? FlyoutPlaylist
{
get
{
return _flyoutPlaylist;
}
set
{
SetProperty(ref _flyoutPlaylist, value);
}
}
public IAsyncRelayCommand<PlaylistItemViewModel?> RenamePlaylistCommand { get; }
public IRelayCommand<PlaylistItemViewModel?> LockPlaylistCommand { get; }
public IRelayCommand<PlaylistItemViewModel?> UnlockPlaylistCommand { get; }
public IAsyncRelayCommand<PlaylistItemViewModel?> DeletePlaylistCommand { get; }
public PlaylistsViewModel(IPlaylistManager playlistManager, IAudioPlayer audioPlayer, IMessageService messageService)
{ {
_playlistManager = playlistManager; _playlistManager = playlistManager;
_playlistManager.PlaylistAdded += OnPlaylistAdded; _playlistManager.PlaylistAdded += OnPlaylistAdded;
_playlistManager.PlaylistRemoved += OnPlaylistRemoved; _playlistManager.PlaylistRemoved += OnPlaylistRemoved;
_playlistManager.CurrentPlaylistChanged += OnCurrentPlaylistChanged; _playlistManager.CurrentPlaylistChanged += OnCurrentPlaylistChanged;
_messageService = messageService;
_audioPlayer = audioPlayer; _audioPlayer = audioPlayer;
_audioPlayer.PlayingSongChanged += OnPlayingSongChanged; _audioPlayer.PlayingSongChanged += OnPlayingSongChanged;
@@ -70,6 +93,11 @@ public partial class PlaylistsViewModel : ViewModelBase
_playlists = new ObservableCollection<PlaylistItemViewModel>(_playlistManager.Playlists.Select(p => new PlaylistItemViewModel(p))); _playlists = new ObservableCollection<PlaylistItemViewModel>(_playlistManager.Playlists.Select(p => new PlaylistItemViewModel(p)));
_selectedPlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _playlistManager.CurrentPlaylist); _selectedPlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _playlistManager.CurrentPlaylist);
_activePlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _audioPlayer.Playlist); _activePlaylist = _playlists.FirstOrDefault(pv => pv.Playlist == _audioPlayer.Playlist);
RenamePlaylistCommand = new AsyncRelayCommand<PlaylistItemViewModel?>(RenamePlaylist);
LockPlaylistCommand = new RelayCommand<PlaylistItemViewModel?>(LockPlaylist);
UnlockPlaylistCommand = new RelayCommand<PlaylistItemViewModel?>(UnlockPlaylist);
DeletePlaylistCommand = new AsyncRelayCommand<PlaylistItemViewModel?>(DeletePlaylistAsync);
} }
private void OnCurrentPlaylistChanged(object? sender, EventArgs e) private void OnCurrentPlaylistChanged(object? sender, EventArgs e)
@@ -102,4 +130,57 @@ public partial class PlaylistsViewModel : ViewModelBase
{ {
ActivePlaylist = Playlists.FirstOrDefault(pv => pv.Playlist == _audioPlayer.Playlist); ActivePlaylist = Playlists.FirstOrDefault(pv => pv.Playlist == _audioPlayer.Playlist);
} }
private async Task RenamePlaylist(PlaylistItemViewModel? playlistItem)
{
if (playlistItem is null || playlistItem.Playlist is null)
return;
Playlist playlist = playlistItem.Playlist;
string newName = await _messageService.InputTextAsync(
$"Rename Playlist - {playlist.Name}",
"Enter a new name for the playlist:",
playlist.Name ?? string.Empty);
if (string.IsNullOrWhiteSpace(newName))
return;
playlist.SetName(newName);
}
private void LockPlaylist(PlaylistItemViewModel? playlistItem)
{
if (playlistItem is null || playlistItem.Playlist is null)
return;
Playlist playlist = playlistItem.Playlist;
playlist.Lock();
}
private void UnlockPlaylist(PlaylistItemViewModel? playlistItem)
{
if (playlistItem is null || playlistItem.Playlist is null)
return;
Playlist playlist = playlistItem.Playlist;
playlist.Unlock();
}
private async Task DeletePlaylistAsync(PlaylistItemViewModel? playlistItem)
{
if (playlistItem is null || playlistItem.Playlist is null)
return;
Playlist playlist = playlistItem.Playlist;
bool confirmed = await _messageService.ConfirmAsync($"Delete Playlist - {playlist.Name}", "Are you sure you want to delete this playlist?");
if (!confirmed)
return;
_playlistManager.RemovePlaylist(playlist);
}
} }

View File

@@ -7,11 +7,15 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:Harmonia.WinUI.ViewModels" xmlns:vm="using:Harmonia.WinUI.ViewModels"
xmlns:converter="using:Harmonia.WinUI.Converters"
xmlns:playlists="using:Harmonia.Core.Playlists" xmlns:playlists="using:Harmonia.Core.Playlists"
DataContext="{Binding Source={StaticResource Locator}, Path=PlaylistsViewModel}" DataContext="{Binding Source={StaticResource Locator}, Path=PlaylistsViewModel}"
d:DataContext="{d:DesignInstance Type=vm:PlaylistsViewModel, IsDesignTimeCreatable=True}" d:DataContext="{d:DesignInstance Type=vm:PlaylistsViewModel, IsDesignTimeCreatable=True}"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources> <UserControl.Resources>
<converter:BooleanToVisibilityConverter x:Key="BoolToVis" />
<converter:BooleanToVisibilityConverter x:Key="InverseBoolToVis" TrueValue="Collapsed" FalseValue="Visible"/>
<SolidColorBrush x:Key="PlaylistItemIconBrush" Color="#dddddd"/> <SolidColorBrush x:Key="PlaylistItemIconBrush" Color="#dddddd"/>
<SolidColorBrush x:Key="PlaylistItemTitleBrush" Color="#dddddd"/> <SolidColorBrush x:Key="PlaylistItemTitleBrush" Color="#dddddd"/>
<SolidColorBrush x:Key="PlaylistItemSubtitleBrush" Color="#aaaaaa"/> <SolidColorBrush x:Key="PlaylistItemSubtitleBrush" Color="#aaaaaa"/>
@@ -76,7 +80,44 @@
Name="PlaylistsListView" Name="PlaylistsListView"
ItemsSource="{Binding Playlists, Mode=OneWay}" ItemsSource="{Binding Playlists, Mode=OneWay}"
SelectedItem="{Binding SelectedPlaylist, Mode=TwoWay}" SelectedItem="{Binding SelectedPlaylist, Mode=TwoWay}"
ItemTemplate="{StaticResource PlaylistTemplate}"> ItemTemplate="{StaticResource PlaylistTemplate}"
RightTapped="PlaylistsListView_RightTapped">
<ListView.ContextFlyout>
<MenuFlyout
x:Name="PlaylistListViewMenuFlyout"
ShouldConstrainToRootBounds="False"
SystemBackdrop="{StaticResource AcrylicBackgroundFillColorDefaultBackdrop}">
<MenuFlyout.MenuFlyoutPresenterStyle>
<Style TargetType="MenuFlyoutPresenter">
<Setter Property="Padding" Value="10"></Setter>
<Setter Property="Background" Value="Transparent"></Setter>
</Style>
</MenuFlyout.MenuFlyoutPresenterStyle>
<MenuFlyoutItem Text="{Binding FlyoutPlaylist.Name}" IsEnabled="False" FontWeight="SemiLight">
<MenuFlyoutItem.Resources>
<SolidColorBrush x:Key="MenuFlyoutItemForegroundDisabled" Color="#aaaaaa"/>
</MenuFlyoutItem.Resources>
</MenuFlyoutItem>
<MenuFlyoutSeparator></MenuFlyoutSeparator>
<MenuFlyoutItem Text="Rename" Command="{Binding RenamePlaylistCommand}" CommandParameter="{Binding FlyoutPlaylist}">
</MenuFlyoutItem>
<MenuFlyoutItem Text="Lock" Command="{Binding LockPlaylistCommand}" CommandParameter="{Binding FlyoutPlaylist}" Visibility="{Binding FlyoutPlaylist.IsLocked, Converter={StaticResource InverseBoolToVis}}">
</MenuFlyoutItem>
<MenuFlyoutItem Text="Unlock" Command="{Binding UnlockPlaylistCommand}" CommandParameter="{Binding FlyoutPlaylist}" Visibility="{Binding FlyoutPlaylist.IsLocked, Converter={StaticResource BoolToVis}}">
</MenuFlyoutItem>
<MenuFlyoutSeparator></MenuFlyoutSeparator>
<MenuFlyoutItem Text="Remove" Foreground="#ff99a4" Command="{Binding DeletePlaylistCommand}" CommandParameter="{Binding FlyoutPlaylist}">
</MenuFlyoutItem>
</MenuFlyout>
</ListView.ContextFlyout>
</ListView> </ListView>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -19,6 +19,16 @@ public sealed partial class PlaylistsView : UserControl
_viewModel.PropertyChanged += OnViewModelPropertyChanged; _viewModel.PropertyChanged += OnViewModelPropertyChanged;
PlaylistsListView.ContainerContentChanging += OnContainerContentChanging; PlaylistsListView.ContainerContentChanging += OnContainerContentChanging;
foreach (MenuFlyoutItemBase item in PlaylistListViewMenuFlyout.Items)
{
item.DataContext = _viewModel;
}
}
private void PlaylistsListView_RightTapped(object sender, Microsoft.UI.Xaml.Input.RightTappedRoutedEventArgs e)
{
_viewModel.FlyoutPlaylist = (e.OriginalSource as FrameworkElement)?.DataContext as PlaylistItemViewModel;
} }
private void OnContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args) private void OnContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)