Разбиение по компонентам

This commit is contained in:
FrigaT
2026-04-14 18:20:11 +03:00
parent 0369f0af07
commit 65efb9ff76
10 changed files with 367 additions and 379 deletions

View File

@@ -2,50 +2,43 @@
@inject ISnackbar Snackbar
@inject NavigationManager Navigation
<MudIconButton @ref="_buttonRef"
Icon="@Icons.Material.Filled.Share"
<MudIconButton Icon="@Icons.Material.Filled.Share"
Color="Color.Default"
OnClick="@OpenPopover"
OnClick="@TogglePopover"
Title="Поделиться"
Size="Size.Medium" />
<MudPopover Open="_popoverOpen"
<MudPopover Open="@_popoverOpen"
AnchorOrigin="Origin.BottomCenter"
TransformOrigin="Origin.TopCenter"
Paper="true"
CloseOnOutsideClick="true"
OnOutsideClick="@(() => _popoverOpen = false)"
Style="min-width: 280px; max-width: 350px;">
RelativeWidth="DropdownWidth.Adaptive"
Paper="true">
<MudPaper Class="pa-4">
<MudText Typo="Typo.body2" Class="mb-2">Ссылка для приглашения:</MudText>
<div style="display: flex; gap: 8px; align-items: center;">
<MudTextField @bind-Value="_shareUrl"
ReadOnly="true"
Variant="Variant.Outlined"
Size="Size.Small"
FullWidth="true"
Style="flex: 1;" />
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
Size="Size.Small"
OnClick="CopyLink"
StartIcon="@Icons.Material.Filled.ContentCopy">
Копировать
</MudButton>
FullWidth="true" />
<MudIconButton Variant="Variant.Filled"
Color="Color.Primary"
Size="Size.Medium"
OnClick="CopyLink"
Icon="@Icons.Material.Filled.ContentCopy">
</MudIconButton>
</div>
</MudPaper>
</MudPopover>
@code {
private MudIconButton? _buttonRef;
private bool _popoverOpen;
private string _shareUrl = "";
/// <summary>
/// Ссылка для копирования. Если не указана, используется текущий URL страницы.
/// </summary>
[Parameter] public string ShareUrl { get; set; } = string.Empty;
protected override void OnInitialized()
{
if (string.IsNullOrEmpty(ShareUrl))
@@ -58,15 +51,22 @@
_shareUrl = ShareUrl;
}
private async Task OpenPopover()
private async Task TogglePopover()
{
if (string.IsNullOrEmpty(ShareUrl))
if (_popoverOpen)
{
Snackbar.Add("Ссылка недоступна", Severity.Warning);
return;
_popoverOpen = false;
}
else
{
if (string.IsNullOrEmpty(ShareUrl))
{
Snackbar.Add("Ссылка недоступна", Severity.Warning);
return;
}
_shareUrl = ShareUrl;
_popoverOpen = true;
}
_shareUrl = ShareUrl;
_popoverOpen = true;
}
private async Task CopyLink()

View File

@@ -1,4 +1,5 @@
@using PlaylistShared.Shared.DTO
@using PlaylistShared.Pwa.Components.Common
@using PlaylistShared.Shared.DTO
@inject HttpClient Http
@inject ISnackbar Snackbar

View File

@@ -20,7 +20,7 @@
}
else
{
<span>Добавить</span>
<MudText>Добавить</MudText>
}
</MudButton>
</MudItem>

View File

@@ -0,0 +1,153 @@
@using PlaylistShared.Pwa.Components.Common
@using PlaylistShared.Shared.Shared
@using PlaylistShared.Shared.Enums
@using System.Security.Claims
@inject HttpClient Http
@inject NavigationManager Navigation
@inject AuthenticationStateProvider AuthProvider
@inject ISnackbar Snackbar
@inject IDialogService DialogService
<div style="display: flex; gap: 16px; align-items: center;">
@if (!string.IsNullOrEmpty(Playlist?.CoverUrl))
{
<MudImage Src="@FormatCoverUrl(Playlist.CoverUrl, "80x80")" Height="80" Width="80" Class="rounded" />
}
<div>
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
<MudLink Href="@($"https://music.yandex.ru/playlists/{Playlist?.YandexPlaylistUuid}")"
Typo="Typo.h5"
Target="_blank"
Underline="Underline.Hover">
@Playlist?.Title
<MudIcon Icon="@Icons.Material.Filled.OpenInNew" Size="Size.Small" Class="ml-1" />
</MudLink>
<ShareButton />
<MudIconButton Icon="@(_isFavorite? Icons.Material.Filled.Star : Icons.Material.Outlined.StarBorder)"
Color="Color.Warning"
OnClick="ToggleFavorite"
Disabled="_favoriteLoading"
Size="Size.Medium" />
@if (_isCreator && _isAuthenticated)
{
<MudIconButton Icon="@Icons.Material.Filled.Settings"
Color="Color.Default"
OnClick="OpenPermissionsDialog"
Title="Настройки доступа"
Size="Size.Medium" />
}
</div>
<MudText Typo="Typo.body2" Color="Color.Secondary">Владелец: @Playlist?.Creator?.UserName</MudText>
</div>
</div>
@code {
[Parameter] public SharedPlaylistDto? Playlist { get; set; }
[Parameter] public EventCallback OnPermissionsChanged { get; set; }
private bool _isAuthenticated;
private bool _isCreator;
private string? _currentUserId;
private bool _isFavorite;
private bool _favoriteLoading;
protected override async Task OnInitializedAsync()
{
var authState = await AuthProvider.GetAuthenticationStateAsync();
_isAuthenticated = authState.User.Identity?.IsAuthenticated == true;
_currentUserId = authState.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
_isCreator = Playlist?.CreatorUserId.ToString() == _currentUserId;
if (_isAuthenticated)
{
await CheckFavoriteStatus();
}
}
private async Task CheckFavoriteStatus()
{
if (Playlist == null) return;
try
{
var response = await Http.GetFromJsonAsync<ApiResponse<bool>>($"/api/favorites/{Playlist.ShareToken}/check");
if (response?.Success == true)
_isFavorite = response.Data;
}
catch { }
}
private async Task ToggleFavorite()
{
if (!_isAuthenticated || Playlist == null) return;
_favoriteLoading = true;
try
{
if (_isFavorite)
{
var response = await Http.DeleteAsync($"/api/favorites/{Playlist.ShareToken}");
if (response.IsSuccessStatusCode)
{
_isFavorite = false;
Snackbar.Add("Плейлист удалён из избранного", Severity.Success);
}
else
{
Snackbar.Add("Ошибка удаления из избранного", Severity.Error);
}
}
else
{
var response = await Http.PostAsync($"/api/favorites/{Playlist.ShareToken}", null);
if (response.IsSuccessStatusCode)
{
_isFavorite = true;
Snackbar.Add("Плейлист добавлен в избранное", Severity.Success);
}
else
{
Snackbar.Add("Ошибка добавления в избранное", Severity.Error);
}
}
}
catch (Exception ex)
{
Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error);
}
finally
{
_favoriteLoading = false;
StateHasChanged();
}
}
private async Task OpenPermissionsDialog()
{
if (Playlist == null) return;
var initialPermissions = new UpdatePermissionsDto
{
ViewPermission = Playlist.ViewPermission,
PlayPermission = Playlist.PlayPermission,
AddPermission = Playlist.AddPermission,
RemovePermission = Playlist.RemovePermission
};
var parameters = new DialogParameters
{
{ nameof(PermissionsDialog.ShareToken), Playlist.ShareToken },
{ nameof(PermissionsDialog.InitialPermissions), initialPermissions }
};
var dialog = await DialogService.ShowAsync<PermissionsDialog>("Настройки доступа", parameters);
var result = await dialog.Result;
if (!result.Canceled)
{
await OnPermissionsChanged.InvokeAsync();
}
}
private string FormatCoverUrl(string? url, string size)
{
if (string.IsNullOrEmpty(url)) return "";
return "https://" + url.Replace("%%", size);
}
}

View File

@@ -0,0 +1,164 @@
@using PlaylistShared.Pwa.Components.Common
@using PlaylistShared.Shared.DTO
@using PlaylistShared.Shared.Shared
@inject HttpClient Http
@inject ISnackbar Snackbar
@inject IDialogService DialogService
<MudTable Items="@_tracks" Hover="true" Breakpoint="Breakpoint.Sm" Loading="_loading">
<HeaderContent>
<MudTh>#</MudTh>
<MudTh>Обложка</MudTh>
<MudTh>Название</MudTh>
<MudTh>Исполнитель</MudTh>
<MudTh>Длительность</MudTh>
@if (CanRemove)
{
<MudTh></MudTh>
}
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="#" Style="font-weight: normal;">@context.Index</MudTd>
<MudTd DataLabel="Обложка">
@if (!string.IsNullOrEmpty(context.CoverUri))
{
@if (CanPlay)
{
<TrackCoverWithPlay CoverUrl="@context.CoverUri"
TrackId="@context.Id"
Width="50" Height="50"
IsPlaying="@(CurrentPlayingTrackId == context.Id && IsPlaying)"
OnPlay="PlayTrack" />
}
else
{
<MudImage Src="@FormatCoverUrl(context.CoverUri, "50x50")" Height="50" Width="50" Class="rounded" />
}
}
</MudTd>
<MudTd DataLabel="Название">
<MudLink Href="@($"https://music.yandex.ru/track/{context.Id}")" Target="_blank" Underline="Underline.Hover">
@context.Title
<MudIcon Icon="@Icons.Material.Filled.OpenInNew" Size="Size.Small" Class="ml-1" />
</MudLink>
</MudTd>
<MudTd DataLabel="Исполнитель">@string.Join(", ", context.Artists)</MudTd>
<MudTd DataLabel="Длительность">@FormatDuration(context.DurationMs)</MudTd>
@if (CanRemove)
{
<MudTd DataLabel="">
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="() => RemoveTrack(context)" />
</MudTd>
}
</RowTemplate>
</MudTable>
@code {
[Parameter] public string ShareToken { get; set; } = string.Empty;
[Parameter] public bool CanPlay { get; set; }
[Parameter] public bool CanRemove { get; set; }
[Parameter] public string? CurrentPlayingTrackId { get; set; }
[Parameter] public bool IsPlaying { get; set; }
[Parameter] public EventCallback<string> OnPlayTrack { get; set; }
public async Task Reload()
{
await LoadTracks();
}
private List<TrackDisplay> _tracks = new();
private bool _loading = true;
protected override async Task OnInitializedAsync()
{
await LoadTracks();
}
private async Task LoadTracks()
{
_loading = true;
try
{
var response = await Http.GetFromJsonAsync<ApiResponse<YandexPlaylistData>>($"/api/sharedplaylist/{ShareToken}/tracks");
if (response?.Success == true && response.Data != null)
{
_tracks = response.Data.Tracks.Select((t, idx) => new TrackDisplay
{
Id = t.Id,
Title = t.Title,
Artists = t.Artists,
DurationMs = t.DurationMs,
CoverUri = t.CoverUri,
Index = idx + 1
}).ToList();
}
else
{
Snackbar.Add(response?.Error?.Message ?? "Не удалось загрузить треки", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Ошибка загрузки треков: {ex.Message}", Severity.Error);
}
finally
{
_loading = false;
StateHasChanged();
}
}
private async Task RemoveTrack(TrackDisplay track)
{
var confirmed = await DialogService.ShowMessageBoxAsync(
"Подтверждение удаления",
$"Вы уверены, что хотите удалить трек \"{track.Title}\"?",
yesText: "Удалить", cancelText: "Отмена");
if (confirmed != true) return;
try
{
var request = new RemoveTracksRequest { TrackIds = new List<string> { track.Id } };
var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{ShareToken}/remove-tracks", request);
if (response.IsSuccessStatusCode)
{
Snackbar.Add("Трек удалён", Severity.Success);
await LoadTracks();
}
else
{
var error = await response.Content.ReadFromJsonAsync<ApiResponse<object>>();
Snackbar.Add(error?.Error?.Message ?? "Ошибка удаления", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error);
}
}
private async Task PlayTrack(string trackId)
{
await OnPlayTrack.InvokeAsync(trackId);
}
private string FormatCoverUrl(string? url, string size)
{
if (string.IsNullOrEmpty(url)) return "";
return "https://" + url.Replace("%%", size);
}
private string FormatDuration(long ms)
{
var seconds = ms / 1000;
var mins = seconds / 60;
var secs = seconds % 60;
return $"{mins}:{secs:D2}";
}
private class TrackDisplay : YandexTrack
{
public int Index { get; set; }
}
}

View File

@@ -25,38 +25,7 @@
<!-- Заголовок с обложкой -->
<MudCardHeader>
<CardHeaderContent>
<div style="display: flex; gap: 16px; align-items: center;">
@if (!string.IsNullOrEmpty(_playlist.CoverUrl))
{
<MudImage Src="@FormatCoverUrl(_playlist.CoverUrl, "80x80")" Height="80" Width="80" Class="rounded" />
}
<div>
<div style="display: flex; align-items: center; gap: 8px;">
<MudLink Href="@($"https://music.yandex.ru/playlists/{_playlist.YandexPlaylistUuid}")" Typo="Typo.h5" Target="_blank" Underline="Underline.Hover">
@_playlist.Title
<MudIcon Icon="@Icons.Material.Filled.OpenInNew" Size="Size.Small" Class="ml-1" />
</MudLink>
<ShareButton />
<MudIconButton Icon="@(_isFavorite? Icons.Material.Filled.Star : Icons.Material.Outlined.StarBorder)"
Color="Color.Warning"
OnClick="ToggleFavorite"
Disabled="_favoriteLoading"
Size="Size.Medium" />
@if (_isCreator && _isAuthenticated)
{
<MudIconButton Icon="@Icons.Material.Filled.Settings"
Color="Color.Default"
OnClick="OpenPermissionsDialog"
Title="Настройки доступа"
Size="Size.Medium" />
}
</div>
<MudText Typo="Typo.body2" Color="Color.Secondary">Владелец: @_playlist.Creator?.UserName</MudText>
</div>
</div>
<PlaylistHeader Playlist="@_playlist" />
</CardHeaderContent>
</MudCardHeader>
@@ -75,73 +44,21 @@
<MudText Typo="Typo.h6">Треки</MudText>
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="LoadTracks" Disabled="_tracksLoading" Size="Size.Medium" />
</div>
@if (_tracksLoading)
{
<MudProgressCircular Indeterminate />
}
else if (_tracks == null || !_tracks.Any())
{
<MudAlert Severity="Severity.Info">В плейлисте пока нет треков</MudAlert>
}
else
{
<MudTable Items="@_tracks">
<HeaderContent>
<MudTh>#</MudTh>
<MudTh>Обложка</MudTh>
<MudTh>Название</MudTh>
<MudTh>Исполнитель</MudTh>
<MudTh>Длительность</MudTh>
@if (_canRemove)
{
<MudTh></MudTh>
}
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="#" Style="font-weight: normal;">@context.Index</MudTd>
<MudTd DataLabel="Обложка">
@if (!string.IsNullOrEmpty(context.CoverUri))
{
@if (@_canPlay)
{
<TrackCoverWithPlay CoverUrl="@context.CoverUri"
TrackId="@context.Id"
Width="50" Height="50"
IsPlaying="@(_currentTrackId == context.Id && _isPlaying)"
OnPlay="PlayTrack" />
}
else
{
<MudImage Src="@FormatCoverUrl(context.CoverUri, "50x50")" Height="50" Width="50" Class="rounded" Style="display: block;" />
}
}
</MudTd>
<MudTd DataLabel="Название">
<MudLink Href="@($"https://music.yandex.ru/track/{context.Id}")" Target="_blank" Underline="Underline.Hover">
@context.Title
<MudIcon Icon="@Icons.Material.Filled.OpenInNew" Size="Size.Small" Class="ml-1" />
</MudLink>
</MudTd>
<MudTd DataLabel="Исполнитель">@string.Join(", ", context.Artists)</MudTd>
<MudTd DataLabel="Длительность">@FormatDuration(context.DurationMs)</MudTd>
@if (_canRemove)
{
<MudTd DataLabel="">
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="() => RemoveTrack(context)" />
</MudTd>
}
</RowTemplate>
</MudTable>
}
<TracksTable @ref="_tracksTableRef"
ShareToken="@Token"
CanPlay="@_canPlay"
CanRemove="@_canRemove"
CurrentPlayingTrackId="_currentTrackId"
IsPlaying="_isPlaying"
OnPlayTrack="PlayTrack" />
</MudCardContent>
</MudCard>
}
<!-- Фиксированный плеер внизу -->
<div class="fixed-player" style="display: @(_isPlayerVisible ? "block" : "none");">
<AudioPlayer @ref="_audioPlayer" OnTrackEnded="OnTrackEnded" RequireAuth="false" SharedPlaylistId="@Token"/>
<AudioPlayer @ref="_audioPlayer" OnTrackEnded="OnTrackEnded" RequireAuth="false" SharedPlaylistId="@Token" />
</div>
</MudContainer>
@@ -152,6 +69,7 @@
private int _addTrackTabIndex = 0; // 0 - ссылка, 1 - поиск
private AudioPlayer? _audioPlayer;
private TracksTable? _tracksTableRef;
private string? _currentTrackId { get; set; }
private bool _isPlaying = false;
private bool _isPlayerVisible = false;
@@ -169,8 +87,6 @@
private bool _isFavorite = false;
private bool _favoriteLoading = false;
private List<YandexTrackDisplay> _tracks = new();
private bool _tracksLoading;
private string _trackLink = "";
@@ -184,67 +100,6 @@
await LoadPlaylist();
}
private async Task CheckFavoriteStatus()
{
if (!_isAuthenticated || _playlist == null) return;
try
{
var response = await Http.GetFromJsonAsync<ApiResponse<bool>>($"/api/favorites/{Token}/check");
if (response?.Success == true)
_isFavorite = response.Data;
}
catch { }
}
private async Task ToggleFavorite()
{
if (!_isAuthenticated)
{
Snackbar.Add("Добавление в избранное доступно только авторизованным пользователям", Severity.Warning);
return;
}
_favoriteLoading = true;
try
{
if (_isFavorite)
{
var response = await Http.DeleteAsync($"/api/favorites/{Token}");
if (response.IsSuccessStatusCode)
{
_isFavorite = false;
Snackbar.Add("Плейлист удалён из избранного", Severity.Success);
}
else
{
Snackbar.Add("Ошибка удаления из избранного", Severity.Error);
}
}
else
{
var response = await Http.PostAsync($"/api/favorites/{Token}", null);
if (response.IsSuccessStatusCode)
{
_isFavorite = true;
Snackbar.Add("Плейлист добавлен в избранное", Severity.Success);
}
else
{
Snackbar.Add("Ошибка добавления в избранное", Severity.Error);
}
}
}
catch (Exception ex)
{
Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error);
}
finally
{
_favoriteLoading = false;
StateHasChanged();
}
}
private async Task ConfigurePermissions()
{
if (_playlist is null)
@@ -280,7 +135,7 @@
PlayPermission = _playlist.PlayPermission,
};
}
}
}
@@ -294,8 +149,7 @@
_playlist = response.Data;
await ConfigurePermissions();
await LoadTracks();
await CheckFavoriteStatus();
//await LoadTracks();
}
else
{
@@ -316,199 +170,15 @@
private async Task LoadTracks()
{
if (_playlist == null) return;
if (_tracksTableRef == null) return;
_tracksLoading = true;
try
{
var url = $"/api/sharedplaylist/{Token}/tracks";
var response = await Http.GetFromJsonAsync<ApiResponse<YandexPlaylistData>>(url);
if (response?.Success == true && response.Data != null)
{
_tracks = response.Data.Tracks.Select((t, idx) => new YandexTrackDisplay
{
Id = t.Id,
Title = t.Title,
Artists = t.Artists,
DurationMs = t.DurationMs,
CoverUri = t.CoverUri,
Index = idx + 1
}).ToList();
}
else
{
Snackbar.Add(response?.Error?.Message ?? "Не удалось загрузить треки", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Ошибка загрузки треков: {ex.Message}", Severity.Error);
}
finally
{
_tracksLoading = false;
StateHasChanged();
}
}
StateHasChanged();
private async Task AddTrackByLink()
{
if (string.IsNullOrWhiteSpace(_trackLink))
{
Snackbar.Add("Введите ссылку на трек", Severity.Warning);
return;
}
await _tracksTableRef.Reload();
_addingTrack = true;
try
{
var request = new AddTrackByLinkRequest { Link = _trackLink };
var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{Token}/add-track-by-link", request);
if (response.IsSuccessStatusCode)
{
Snackbar.Add("Трек успешно добавлен", Severity.Success);
_trackLink = "";
await LoadTracks();
}
else
{
var error = await response.Content.ReadFromJsonAsync<ApiResponse<object>>();
Snackbar.Add(error?.Error?.Message ?? "Ошибка добавления трека", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error);
}
finally
{
_addingTrack = false;
}
}
private async Task OnTrackAdded(string trackId)
{
if (!_canAdd) return;
try
{
var request = new AddTracksRequest { TrackIds = new List<string> { trackId } };
var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{Token}/add-tracks", request);
if (response.IsSuccessStatusCode)
{
Snackbar.Add("Трек успешно добавлен", Severity.Success);
await LoadTracks();
}
else
{
var error = await response.Content.ReadFromJsonAsync<ApiResponse<object>>();
Snackbar.Add(error?.Error?.Message ?? "Ошибка добавления трека", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error);
}
}
private async Task AddTrackById(string trackId)
{
if (!_canAdd)
{
Snackbar.Add("У вас нет прав на добавление треков", Severity.Warning);
return;
}
_addingTrack = true;
try
{
var request = new AddTracksRequest { TrackIds = new List<string> { trackId } };
var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{Token}/add-tracks", request);
if (response.IsSuccessStatusCode)
{
Snackbar.Add("Трек успешно добавлен", Severity.Success);
await LoadTracks();
}
else
{
var error = await response.Content.ReadFromJsonAsync<ApiResponse<object>>();
Snackbar.Add(error?.Error?.Message ?? "Ошибка добавления трека", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error);
}
finally
{
_addingTrack = false;
}
}
private async Task RemoveTrack(YandexTrackDisplay track)
{
var confirmed = await DialogService.ShowMessageBoxAsync(
"Подтверждение удаления",
$"Вы уверены, что хотите удалить трек \"{track.Title}\"?",
yesText: "Удалить", cancelText: "Отмена");
if (confirmed != true) return;
try
{
var request = new RemoveTracksRequest { TrackIds = new List<string> { track.Id } };
var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{Token}/remove-tracks", request);
if (response.IsSuccessStatusCode)
{
Snackbar.Add("Трек удалён", Severity.Success);
await LoadTracks();
}
else
{
var error = await response.Content.ReadFromJsonAsync<ApiResponse<object>>();
Snackbar.Add(error?.Error?.Message ?? "Ошибка удаления", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error);
}
}
private async Task OpenPermissionsDialog()
{
var parameters = new DialogParameters
{
{ nameof(PermissionsDialog.ShareToken), Token },
{ nameof(PermissionsDialog.InitialPermissions), _editPermissions }
};
var dialog = await DialogService.ShowAsync<PermissionsDialog>("Настройки доступа", parameters);
var result = await dialog.Result;
if (!result.Canceled && result.Data is UpdatePermissionsDto updatedPermissions)
{
// Обновляем локальные права и перезагружаем плейлист
_editPermissions = updatedPermissions;
await LoadPlaylist(); // перезагружаем, чтобы обновить _playlist и права доступа
await LoadTracks(); // возможно, треки тоже нужно перезагрузить
StateHasChanged();
}
}
private string FormatDuration(int ms)
{
var seconds = ms / 1000;
var mins = seconds / 60;
var secs = seconds % 60;
return $"{mins}:{secs:D2}";
}
private string FormatCoverUrl(string? url, string size = "200x200")
{
if (string.IsNullOrEmpty(url)) return "";
return "https://" + url.Replace("%%", size);
}
private class YandexTrackDisplay : YandexTrack
{
public int Index { get; set; }
_tracksLoading = false;
StateHasChanged();
}
private async Task PlayTrack(string trackId)

View File

@@ -5,6 +5,6 @@ public class YandexTrack
public string Id { get; set; } = "";
public string Title { get; set; } = "";
public List<string> Artists { get; set; } = new();
public int DurationMs { get; set; }
public long DurationMs { get; set; }
public string CoverUri { get; set; } = "";
}