Compare commits
4 Commits
45b8a168a1
...
b1febfc9dc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1febfc9dc | ||
|
|
0f2755281e | ||
|
|
d17ed30175 | ||
|
|
0f9dd1a8d8 |
@@ -11,7 +11,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<p role="alert">You are not authorized to access this resource.</p>
|
||||
<p role="alert">У вас нет прав доступа к этому ресурсу.</p>
|
||||
}
|
||||
</NotAuthorized>
|
||||
</AuthorizeRouteView>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
@implements IDisposable
|
||||
@inject ContextualActionBarService ContextualActionBarService
|
||||
|
||||
@code {
|
||||
[Parameter] public ContextualActionBarPosition Position { get; set; } = ContextualActionBarPosition.Default;
|
||||
[Parameter] public RenderFragment? ChildContent { get; set; }
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
ContextualActionBarService.Content = ChildContent;
|
||||
ContextualActionBarService.Position = Position;
|
||||
ContextualActionBarService.ChangeParameters();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ContextualActionBarService.Content = null;
|
||||
ContextualActionBarService.Position = ContextualActionBarPosition.Default;
|
||||
ContextualActionBarService.ChangeParameters();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
@using MudBlazor.Services
|
||||
@using PlaylistShared.Pwa.Components.Global
|
||||
@inherits LayoutComponentBase
|
||||
@inject PwaUpdateService PwaUpdateService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject ContextualActionBarService ContextualActionBarService
|
||||
@inject IBrowserViewportService BrowserViewportService
|
||||
@implements IBrowserViewportObserver
|
||||
|
||||
<MudThemeProvider Theme="@_theme" IsDarkMode="_isDarkMode" />
|
||||
<MudPopoverProvider />
|
||||
@@ -9,36 +13,47 @@
|
||||
<MudSnackbarProvider />
|
||||
|
||||
<MudLayout>
|
||||
<MudAppBar Elevation="1">
|
||||
<MudAppBar Elevation="1" Contextual Bottom = "@_actionBarBottom" Fixed>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@DrawerToggle" />
|
||||
<MudSpacer />
|
||||
<LoginDisplay />
|
||||
<MudIconButton Icon="@(DarkLightModeButtonIcon)" Color="Color.Inherit" OnClick="@DarkModeToggle" Class="ml-2" />
|
||||
<MudLink Href="https://git.frigat.duckdns.org/FrigaT/PlaylistShared" Target="_blank" Color="Color.Inherit" Underline="Underline.None" Class="ml-4">
|
||||
<MudIcon Icon="@Icons.Custom.Brands.GitHub" Size="Size.Small" Class="mr-1" /> Git
|
||||
</MudLink>
|
||||
@if (_actionBarContent != null)
|
||||
{
|
||||
@_actionBarContent
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudSpacer />
|
||||
<LoginDisplay />
|
||||
<MudIconButton Icon="@(DarkLightModeButtonIcon)" Color="Color.Inherit" OnClick="@DarkModeToggle" Class="ml-2" />
|
||||
<MudLink Href="https://git.frigat.duckdns.org/FrigaT/PlaylistShared" Target="_blank" Color="Color.Inherit" Underline="Underline.None" Class="ml-4">
|
||||
<MudIcon Icon="@Icons.Custom.Brands.GitHub" Size="Size.Small" Class="mr-1" /> Git
|
||||
</MudLink>
|
||||
}
|
||||
</MudAppBar>
|
||||
|
||||
<MudDrawer @bind-Open="_drawerOpen" ClipMode="DrawerClipMode.Always" Elevation="2">
|
||||
<NavMenu />
|
||||
</MudDrawer>
|
||||
|
||||
<MudMainContent Class="pt-16 d-flex flex-column" Style="height: 100vh;">
|
||||
<MudItem Class="flex-grow-1 overflow-y-auto">
|
||||
@Body
|
||||
</MudItem>
|
||||
|
||||
<MudItem>
|
||||
<AudioPlayer />
|
||||
</MudItem>
|
||||
<MudMainContent Class="@("d-flex flex-column" + (_actionBarBottom ? " pt-0 pb-16" : ""))" Style="height: 100dvh;">
|
||||
@Body
|
||||
</MudMainContent>
|
||||
</MudLayout>
|
||||
|
||||
@code {
|
||||
private RenderFragment? _actionBarContent;
|
||||
private bool _actionBarBottom => _contextualPosition switch
|
||||
{
|
||||
ContextualActionBarPosition.Bottom => true,
|
||||
ContextualActionBarPosition.Top => false,
|
||||
_ => _isMobile,
|
||||
};
|
||||
private bool _isMobile = false;
|
||||
private ContextualActionBarPosition _contextualPosition = ContextualActionBarPosition.Default;
|
||||
|
||||
private bool _drawerOpen = true;
|
||||
private bool _isDarkMode = true;
|
||||
private MudTheme? _theme;
|
||||
|
||||
|
||||
private DotNetObjectReference<PwaUpdateService>? _dotNetRef;
|
||||
|
||||
protected override void OnInitialized()
|
||||
@@ -51,6 +66,8 @@
|
||||
PaletteDark = _darkPalette,
|
||||
LayoutProperties = new LayoutProperties()
|
||||
};
|
||||
|
||||
ContextualActionBarService.OnChanged += OnContextualChangedHandler;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
@@ -59,9 +76,17 @@
|
||||
{
|
||||
_dotNetRef = DotNetObjectReference.Create(PwaUpdateService);
|
||||
await JSRuntime.InvokeVoidAsync("registerSWMessageHandler", _dotNetRef);
|
||||
await BrowserViewportService.SubscribeAsync(this, fireImmediately: true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnContextualChangedHandler()
|
||||
{
|
||||
_actionBarContent = ContextualActionBarService.Content;
|
||||
_contextualPosition = ContextualActionBarService.Position;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void DrawerToggle()
|
||||
{
|
||||
_drawerOpen = !_drawerOpen;
|
||||
@@ -116,4 +141,21 @@
|
||||
true => Icons.Material.Rounded.AutoMode,
|
||||
false => Icons.Material.Outlined.DarkMode,
|
||||
};
|
||||
|
||||
|
||||
|
||||
Guid IBrowserViewportObserver.Id { get; } = Guid.NewGuid();
|
||||
|
||||
ResizeOptions IBrowserViewportObserver.ResizeOptions { get; } = new()
|
||||
{
|
||||
ReportRate = 250,
|
||||
NotifyOnBreakpointOnly = true
|
||||
};
|
||||
|
||||
Task IBrowserViewportObserver.NotifyBrowserViewportChangeAsync(BrowserViewportEventArgs browserViewportEventArgs)
|
||||
{
|
||||
_isMobile = browserViewportEventArgs.Breakpoint <= Breakpoint.Sm;
|
||||
|
||||
return InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<PageTitle>@_playlist?.Title - Playlist Share</PageTitle>
|
||||
|
||||
@using PlaylistShared.Pwa.Components.Common
|
||||
@using PlaylistShared.Pwa.Components.Global
|
||||
@using PlaylistShared.Pwa.Components.SharedPlaylist
|
||||
@using PlaylistShared.Pwa.Components.SharedPlaylist.Cards
|
||||
@using PlaylistShared.Shared.DTO
|
||||
@@ -16,60 +17,121 @@
|
||||
@inject IDialogService DialogService
|
||||
|
||||
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pa-1" Style="height: 100%;">
|
||||
@* --- ВЕРСИЯ ДЛЯ ПК (сетка) --- *@
|
||||
<MudHidden Breakpoint="Breakpoint.MdAndUp" Invert>
|
||||
<MudGrid Spacing="2" Class="flex-grow-1 pt-2" Style="height: 100%;">
|
||||
<MudItem xs="12" md="6" Style="height: 100%;">
|
||||
@PlaylistCardContent
|
||||
</MudItem>
|
||||
<MudStack Style="height: 100%;" StretchItems="StretchItems.Start" Spacing="2">
|
||||
@*Первый элемент растянется на всю высоту*@
|
||||
<MudItem Style="min-height: 0; height: 100%;">
|
||||
|
||||
@if (_canAdd)
|
||||
{
|
||||
<MudItem xs="12" md="6" Style="height: 100%;">
|
||||
@AddTrackCardContent
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
</MudHidden>
|
||||
@* --- ВЕРСИЯ ДЛЯ ПК (сетка) --- *@
|
||||
<MudHidden Breakpoint="Breakpoint.MdAndUp" Invert>
|
||||
<MudGrid Spacing="2" Class="flex-grow-1 pt-2" Style="height: 100%;">
|
||||
<MudItem xs="12" md="6" Style="height: 100%; overflow-y: auto;">
|
||||
<MudCard Elevation="4" Class="d-flex flex-column" Style="height: 100%;">
|
||||
<MudCardHeader Class="pb-0">
|
||||
<CardHeaderContent>
|
||||
@PlaylistCardHeaderContent
|
||||
</CardHeaderContent>
|
||||
<CardHeaderActions>
|
||||
<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" OnClick="OpenPermissionsDialog" Size="Size.Medium" />
|
||||
}
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="LoadTracks" Disabled="@_tracksLoading" />
|
||||
</CardHeaderActions>
|
||||
</MudCardHeader>
|
||||
|
||||
<MudCardContent Class="flex-grow-1 overflow-auto flex-column py-0">
|
||||
@PlaylistCardBodyContent
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
|
||||
@if (_canAdd)
|
||||
{
|
||||
<MudItem xs="12" md="6" Style="height: 100%; overflow-y: auto;">
|
||||
@AddTrackCardContent
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
<ContextualBarContent />
|
||||
</MudHidden>
|
||||
|
||||
@* --- ВЕРСИЯ ДЛЯ МОБИЛОК (вкладки внизу) --- *@
|
||||
<MudHidden Breakpoint="Breakpoint.SmAndDown" Invert>
|
||||
<div class="d-flex flex-column pa-0 ma-0" style="height: 100%; min-height: 0;">
|
||||
@* --- ВЕРСИЯ ДЛЯ МОБИЛОК (вкладки внизу) --- *@
|
||||
<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="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">
|
||||
<MudCard Elevation="0" Class="d-flex flex-column" Style="height: 100%;">
|
||||
<MudCardHeader Class="pb-0">
|
||||
<CardHeaderContent>
|
||||
@PlaylistCardHeaderContent
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
|
||||
<MudCardContent Class="flex-grow-1 overflow-auto flex-column py-0">
|
||||
@PlaylistCardBodyContent
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (_canAdd)
|
||||
{
|
||||
<div class="@(_activeMobileTab == 1 ? "d-flex" : "d-none") flex-column" style="height: 100%;">
|
||||
<div class="flex-grow-1 overflow-auto pb-1">
|
||||
@AddTrackCardContent
|
||||
@if (_canAdd)
|
||||
{
|
||||
<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>
|
||||
</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" />
|
||||
@* Кастомная панель навигации внизу *@
|
||||
<ContextualBarContent>
|
||||
<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>
|
||||
|
||||
<MudSpacer />
|
||||
<MudMenu Icon="@Icons.Material.Filled.MoreVert"
|
||||
AnchorOrigin="Origin.TopRight"
|
||||
TransformOrigin="Origin.TopRight">
|
||||
|
||||
<MudMenuItem Icon="@(_isFavorite? Icons.Material.Filled.Star : Icons.Material.Outlined.StarBorder)"
|
||||
IconColor="Color.Warning"
|
||||
Label="Избранное"
|
||||
OnClick="ToggleFavorite"
|
||||
Disabled="@_favoriteLoading"
|
||||
/>
|
||||
|
||||
@if (_isCreator && _isAuthenticated)
|
||||
{
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Settings"
|
||||
OnClick="OpenPermissionsDialog"
|
||||
Label="Настройки" />
|
||||
}
|
||||
</MudMenu>
|
||||
</ContextualBarContent>
|
||||
</div>
|
||||
</MudHidden>
|
||||
</MudItem>
|
||||
|
||||
@*Второй элемент - плеер. Привязан к нижней части контейнера*@
|
||||
<MudItem>
|
||||
<AudioPlayer />
|
||||
</MudItem>
|
||||
</MudStack>
|
||||
</MudContainer>
|
||||
|
||||
<style>
|
||||
@@ -94,64 +156,70 @@
|
||||
/// <summary>Токен расшаренного плейлиста</summary>
|
||||
[Parameter] public required string Token { get; set; }
|
||||
|
||||
private RenderFragment PlaylistCardContent => __builder =>
|
||||
|
||||
/// <summary>Элемент: заголовок плейлиста</summary>
|
||||
private RenderFragment PlaylistCardHeaderContent => __builder =>
|
||||
{
|
||||
<MudCard Elevation="0" Class="d-flex flex-column" Style="height: 100%;">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
@if(_loading)
|
||||
{
|
||||
<MudSkeleton Width="200px" Height="32px" SkeletonType="SkeletonType.Text" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<PlaylistHeader Playlist="@_playlist" />
|
||||
}
|
||||
</CardHeaderContent>
|
||||
<CardHeaderActions>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="LoadTracks" Disabled="@_tracksLoading" />
|
||||
</CardHeaderActions>
|
||||
</MudCardHeader>
|
||||
@if (_loading)
|
||||
{
|
||||
<MudSkeleton Width="200px" Height="32px" SkeletonType="SkeletonType.Text" />
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
<MudCardContent Class="flex-grow-1 overflow-auto flex-column">
|
||||
@if (_loading || _tracksLoading)
|
||||
<MudStack Row AlignItems="AlignItems.Center">
|
||||
@if (!string.IsNullOrEmpty(_playlist?.CoverUrl))
|
||||
{
|
||||
<TrackItemSkeleton />
|
||||
<TrackItemSkeleton />
|
||||
<TrackItemSkeleton />
|
||||
<TrackItemSkeleton />
|
||||
<TrackItemSkeleton />
|
||||
<TrackItemSkeleton />
|
||||
<MudImage Src="@_playlist.CoverUrl.FormatCoverUrl(60, 60)" Height="60" Width="60" Class="rounded shadow-sm" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTable Items="@_tracks"
|
||||
Hover
|
||||
Elevation="0"
|
||||
Breakpoint="Breakpoint.None"
|
||||
Style="min-height: 0;">
|
||||
<RowTemplate>
|
||||
<MudTd Class="py-1 px-0" Style="width: 100%;">
|
||||
<TrackItem Track="@context" PlaylistShareToken="@Token" CanPlay="@_canPlay" />
|
||||
</MudTd>
|
||||
@if (_canRemove)
|
||||
{
|
||||
<MudTd Class="py-1 px-0">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" Size="Size.Small" OnClick="() => OnRemoveTrack(context)" />
|
||||
</MudTd>
|
||||
}
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
}
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary">
|
||||
@_playlist?.Title
|
||||
</MudText>
|
||||
</MudStack>
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// <summary>Элемент: треки плейлиста</summary>
|
||||
private RenderFragment PlaylistCardBodyContent => __builder =>
|
||||
{
|
||||
@if (_loading || _tracksLoading)
|
||||
{
|
||||
<TrackItemSkeleton />
|
||||
<TrackItemSkeleton />
|
||||
<TrackItemSkeleton />
|
||||
<TrackItemSkeleton />
|
||||
<TrackItemSkeleton />
|
||||
<TrackItemSkeleton />
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTable Items="@_tracks"
|
||||
Hover
|
||||
Elevation="0"
|
||||
Breakpoint="Breakpoint.None"
|
||||
Style="min-height: 0;">
|
||||
<RowTemplate>
|
||||
<MudTd Class="py-1 px-0" Style="width: 100%;">
|
||||
<TrackItem Track="@context" PlaylistShareToken="@Token" CanPlay="@_canPlay" />
|
||||
</MudTd>
|
||||
@if (_canRemove)
|
||||
{
|
||||
<MudTd Class="py-1 px-0">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" Size="Size.Small" OnClick="() => OnRemoveTrack(context)" />
|
||||
</MudTd>
|
||||
}
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>Элемент: блок добавления треков</summary>
|
||||
private RenderFragment AddTrackCardContent => __builder =>
|
||||
{
|
||||
<MudCard Class="d-flex flex-column" Elevation="0" Style="height: 100%;">
|
||||
<MudCardHeader>
|
||||
<MudStack Style="width: 100%;">
|
||||
<MudCardHeader Class="pb-0">
|
||||
<MudStack Style="width: 100%;" Spacing="0">
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="mb-4">Добавление треков</MudText>
|
||||
<MudTextField @bind-Value="_searchQuery"
|
||||
@bind-Value:after="OnSearchQueryChanged"
|
||||
@@ -177,7 +245,7 @@
|
||||
</MudStack>
|
||||
</MudCardHeader>
|
||||
|
||||
<MudCardContent Class="flex-grow-1 overflow-auto flex-column">
|
||||
<MudCardContent Class="flex-grow-1 overflow-auto flex-column py-0">
|
||||
@if (_isSearching)
|
||||
{
|
||||
|
||||
@@ -324,9 +392,16 @@
|
||||
private bool _isSearching = false;
|
||||
/// <summary>Ссылка на поле ввода</summary>
|
||||
private MudTextField<string> _searchField;
|
||||
|
||||
/// <summary>Результат поиска.</summary>
|
||||
private YandexSearchResult? _searchResult = null;
|
||||
|
||||
/********************************
|
||||
* Вкладка добавления треков
|
||||
*********************************/
|
||||
/// <summary>Признак, что альбом в фаворитах.</summary>
|
||||
private bool _isFavorite;
|
||||
/// <summary>Загрузка признака "фаворит".</summary>
|
||||
private bool _favoriteLoading;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@@ -378,6 +453,7 @@
|
||||
_activeMobileTab = 0;
|
||||
|
||||
await ConfigurePermissions();
|
||||
await CheckFavoriteStatus();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -629,4 +705,91 @@
|
||||
throw new ArgumentException("Unsupported URL pattern");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Избранное / Фаворит
|
||||
/// <summary>Установка галочки "избранное"</summary>
|
||||
private async Task CheckFavoriteStatus()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Token)) 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 (string.IsNullOrWhiteSpace(Token)) return;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Настройка доступов к плейлисту
|
||||
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), Token },
|
||||
{ nameof(PermissionsDialog.InitialPermissions), initialPermissions }
|
||||
};
|
||||
var dialog = await DialogService.ShowAsync<PermissionsDialog>("Настройки доступа", parameters);
|
||||
var result = await dialog.Result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -25,6 +25,7 @@ internal class Program
|
||||
builder.Services.AddScoped<PlayerStorage>();
|
||||
builder.Services.AddScoped<AuthStateProvider>();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<AuthStateProvider>());
|
||||
builder.Services.AddScoped<ContextualActionBarService>();
|
||||
builder.Services.AddScoped<ApiClient>();
|
||||
builder.Services.AddScoped<IAudioPlayerService, AudioPlayerService>();
|
||||
|
||||
|
||||
25
PlaylistShared.Pwa/Services/ContextualActionBarService.cs
Normal file
25
PlaylistShared.Pwa/Services/ContextualActionBarService.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace PlaylistShared.Pwa.Services;
|
||||
|
||||
public class ContextualActionBarService
|
||||
{
|
||||
// Событие, которое будет вызываться при изменении содержимого панели
|
||||
public event Action? OnChanged;
|
||||
|
||||
public RenderFragment? Content { get; set; } = null;
|
||||
|
||||
public ContextualActionBarPosition Position { get; set; } = ContextualActionBarPosition.Default;
|
||||
|
||||
public void ChangeParameters()
|
||||
{
|
||||
OnChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ContextualActionBarPosition
|
||||
{
|
||||
Default,
|
||||
Top,
|
||||
Bottom,
|
||||
}
|
||||
@@ -3,11 +3,15 @@
|
||||
"short_name": "PlaylistShare",
|
||||
"id": "./",
|
||||
"start_url": "./",
|
||||
"scope": "./",
|
||||
"scope": "/",
|
||||
"handle_links": "preferred",
|
||||
"display": "standalone",
|
||||
"background_color": "#1a1a27",
|
||||
"theme_color": "#7e6fff",
|
||||
"launch_handler": {
|
||||
"client_mode": "focus-existing"
|
||||
},
|
||||
|
||||
"prefer_related_applications": false,
|
||||
"icons": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user