Files
PlaylistShared/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor
2026-04-21 17:44:09 +03:00

285 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@page "/shared/{token}"
<PageTitle>@_playlist?.Title - Playlist Share</PageTitle>
@using PlaylistShared.Pwa.Components.Common
@using PlaylistShared.Pwa.Components.SharedPlaylist
@using PlaylistShared.Shared.DTO
@using PlaylistShared.Shared.Enums
@using PlaylistShared.Pwa.Services
@using PlaylistShared.Shared.SharedPlaylist
@using PlaylistShared.Shared.Yandex
@inject HttpClient Http
@inject ISnackbar Snackbar
@inject NavigationManager Navigation
@inject AuthenticationStateProvider AuthProvider
@inject IDialogService DialogService
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pa-1" Style="height: 100%;">
@if (_loading)
{
<MudProgressCircular Indeterminate />
}
else if (_playlist == null)
{
<MudAlert Severity="Severity.Error">Плейлист не найден или у вас нет доступа</MudAlert>
}
else
{
@* --- ВЕРСИЯ ДЛЯ ПК (сетка) --- *@
<MudHidden Breakpoint="Breakpoint.MdAndUp" Invert>
<MudGrid Spacing="2" Class="flex-grow-1" Style="height: 100%;">
<MudItem xs="12" md="6" Style="height: 100%;">
@PlaylistCardContent
</MudItem>
@if (_canAdd)
{
<MudItem xs="12" md="6" Style="height: 100%;">
@AddTrackCardContent
</MudItem>
}
</MudGrid>
</MudHidden>
@* --- ВЕРСИЯ ДЛЯ МОБИЛОК (вкладки внизу) --- *@
<MudHidden Breakpoint="Breakpoint.SmAndDown" Invert>
<div class="d-flex flex-column pa-0 ma-0" style="height: 100%; min-height: 0;">
@* Область контента: оба компонента здесь всегда *@
<div class="flex-grow-1 relative pa-0" style="min-height: 0;">
<div class="@(_activeMobileTab == 0 ? "d-flex" : "d-none") flex-column" style="height: 100%;">
<div class="flex-grow-1 overflow-auto pb-1">
@PlaylistCardContent
</div>
</div>
<div class="@(_activeMobileTab == 1 ? "d-flex" : "d-none") flex-column" style="height: 100%;">
<div class="flex-grow-1 overflow-auto pb-1">
@AddTrackCardContent
</div>
</div>
</div>
@* Кастомная панель навигации внизу *@
@if (_canAdd)
{
<MudPaper Elevation="0" Class="py-1">
<MudNavMenu Margin="Margin.None" Class="d-flex flex-row justify-space-around">
<MudIconButton Icon="@Icons.Material.Filled.LibraryMusic"
Color="@(_activeMobileTab == 0 ? Color.Primary : Color.Default)"
OnClick="() => _activeMobileTab = 0" />
<MudIconButton Icon="@Icons.Material.Filled.AddCircle"
Color="@(_activeMobileTab == 1 ? Color.Primary : Color.Default)"
OnClick="() => _activeMobileTab = 1" />
</MudNavMenu>
</MudPaper>
}
</div>
</MudHidden>
}
</MudContainer>
@code {
[Parameter] public string Token { get; set; }
private RenderFragment PlaylistCardContent => __builder =>
{
<MudCard Elevation="0" Class="d-flex flex-column" Style="height: 100%;">
<MudCardHeader>
<CardHeaderContent>
<PlaylistHeader Playlist="@_playlist" />
</CardHeaderContent>
<CardHeaderActions>
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="LoadTracks" Disabled="@_tracksLoading" />
</CardHeaderActions>
</MudCardHeader>
<MudCardContent Class="flex-grow-1 overflow-auto flex-column">
<MudTable Items="@_tracks" Virtualize Hover Elevation="0" Breakpoint="Breakpoint.None" Class="d-flex flex-grow-1 flex-column" Style="min-height: 0;">
<RowTemplate>
<MudTd Class="pa-1" Style="width: 100%;">
<TrackItem Track="@context" PlaylistShareToken="@Token" CanPlay="@_canPlay" />
</MudTd>
@if (_canRemove)
{
<MudTd Class="pa-1">
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" Size="Size.Small" OnClick="() => RemoveTrack(context)" />
</MudTd>
}
</RowTemplate>
</MudTable>
</MudCardContent>
</MudCard>
};
private RenderFragment AddTrackCardContent => __builder =>
{
<MudPaper Class="pa-4 d-flex flex-column" Elevation="1" Style="height: 100%; overflow: hidden;">
<MudText Typo="Typo.h6" Color="Color.Primary" Class="mb-4">Добавление треков</MudText>
<MudItem class="flex-grow-1 overflow-auto">
<AddTrackSection ShareToken="@Token" OnTrackAdded="LoadTracks" OnTrackRemoved="LoadTracks" ExistingTrackIds="_existingTrackIds" />
</MudItem>
</MudPaper>
};
private int _activeMobileTab = 0;
private HashSet<string> _existingTrackIds = new();
private bool _firstLoadExistingTrackIds;
private List<YandexTrack> _tracks = new();
private SharedPlaylistDto? _playlist;
private bool _loading = true;
private bool _isAuthenticated;
private bool _isCreator;
private bool _canPlay;
private bool _canAdd;
private bool _canRemove;
private UpdatePermissionsDto _editPermissions = new();
private bool _savingPermissions;
private string? _currentUserId;
private bool _isFavorite = false;
private bool _favoriteLoading = false;
private bool _tracksLoading;
private string _trackLink = "";
private bool _addingTrack;
protected override async Task OnInitializedAsync()
{
var authState = await AuthProvider.GetAuthenticationStateAsync();
_isAuthenticated = authState.User.Identity?.IsAuthenticated == true;
_currentUserId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
await LoadPlaylist();
await LoadTracks();
}
private async Task ConfigurePermissions()
{
if (_playlist is null)
{
_isCreator = false;
_canAdd = false;
_canRemove = false;
_canPlay = false;
}
else
{
_isCreator = _playlist.CreatorUserId.ToString() == _currentUserId;
_canAdd = _isCreator
|| _playlist.AddPermission == EditPermission.Everyone
|| (_playlist.AddPermission == EditPermission.AuthorizedOnly && _isAuthenticated);
_canRemove = _isCreator
|| _playlist.RemovePermission == EditPermission.Everyone
|| (_playlist.RemovePermission == EditPermission.AuthorizedOnly && _isAuthenticated);
_canPlay = _isCreator
|| _playlist.PlayPermission == ViewPermission.Everyone
|| (_playlist.PlayPermission == ViewPermission.AuthorizedOnly && _isAuthenticated);
if (_isCreator && _isAuthenticated)
{
_editPermissions = new UpdatePermissionsDto
{
ViewPermission = _playlist.ViewPermission,
AddPermission = _playlist.AddPermission,
RemovePermission = _playlist.RemovePermission,
PlayPermission = _playlist.PlayPermission,
};
}
}
}
private async Task LoadPlaylist()
{
try
{
var response = await Http.GetFromJsonAsync<ApiResponse<SharedPlaylistDto>>($"/api/sharedplaylist/{Token}");
if (response?.Success == true)
{
_playlist = response.Data;
_activeMobileTab = 0;
await ConfigurePermissions();
}
else
{
Snackbar.Add(response?.Error?.Message ?? "Не удалось загрузить плейлист", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error);
}
finally
{
_loading = false;
StateHasChanged();
}
}
private async Task LoadTracks()
{
if (_playlist == null) return;
_tracksLoading = true;
try
{
var response = await Http.GetFromJsonAsync<ApiResponse<YandexPlaylistData>>($"/api/sharedplaylist/{Token}/tracks");
if (response?.Success == true && response.Data != null)
{
_tracks = response.Data.Tracks;
_existingTrackIds = _tracks.Select(t => t.TrackId).ToHashSet();
}
else
{
Snackbar.Add(response?.Error?.Message ?? "Не удалось загрузить треки", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Ошибка загрузки треков: {ex.Message}", Severity.Error);
}
finally
{
_tracksLoading = false;
StateHasChanged();
}
}
private async Task RemoveTrack(YandexTrack track)
{
var confirmed = await DialogService.ShowMessageBoxAsync(
"Подтверждение удаления",
$"Вы уверены, что хотите удалить трек \"{track.Title}\"?",
yesText: "Удалить", cancelText: "Отмена");
if (confirmed != true) return;
try
{
var request = new UpdateTrackListRequest { TrackIds = new List<string> { track.TrackId } };
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);
}
}
}