Добавлен стандартный "поделиться ПЛ"

This commit is contained in:
FrigaT
2026-04-23 10:14:59 +03:00
parent 7c05940dbf
commit 362762a813
4 changed files with 146 additions and 2 deletions

View File

@@ -0,0 +1,45 @@
@inject IJSRuntime JS
@inject ISnackbar Snackbar
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6">Поделиться плейлистом</MudText>
</TitleContent>
<DialogContent>
<MudStack Spacing="2">
<MudText>Скопируйте ссылку и отправьте её друзьям:</MudText>
<MudTextField @bind-Value="ShareUrl"
Variant="Variant.Outlined"
ReadOnly="true"
Margin="Margin.Dense"
Class="mt-2" />
</MudStack>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled" Color="Color.Primary"
OnClick="CopyToClipboard">
Скопировать ссылку
</MudButton>
<MudButton Variant="Variant.Text" Color="Color.Default"
OnClick="Close">
Закрыть
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = default!;
[Parameter]
public string ShareUrl { get; set; } = string.Empty;
private async Task CopyToClipboard()
{
await JS.InvokeVoidAsync("navigator.clipboard.writeText", ShareUrl);
Snackbar.Add("Ссылка скопирована в буфер обмена!", Severity.Success);
MudDialog.Close(DialogResult.Ok(true));
}
private void Close() => MudDialog.Cancel();
}

View File

@@ -16,6 +16,8 @@
@inject AuthenticationStateProvider AuthProvider
@inject IDialogService DialogService
@inject IAudioPlayerService AudioPlayerService
@inject IJSRuntime JS
@inject IDialogService DialogService
@implements IDisposable
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="py-1 px-1" Style="height: 100%;">
@@ -32,18 +34,23 @@
<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" />
<MudIconButton Icon="@Icons.Material.Filled.Share"
OnClick="SharePlaylist"
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" />
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="LoadTracks" Disabled="@_tracksLoading" Size="Size.Medium" />
</CardHeaderActions>
</MudCardHeader>
@@ -116,7 +123,10 @@
OnClick="ToggleFavorite"
Disabled="@_favoriteLoading"
/>
<MudMenuItem Icon="@Icons.Material.Filled.Share"
OnClick="SharePlaylist"
Label="Поделиться" />
@if (_isCreator && _isAuthenticated)
{
<MudMenuItem Icon="@Icons.Material.Filled.Settings"
@@ -405,6 +415,12 @@
private bool _isFavorite;
/// <summary>Загрузка признака "фаворит".</summary>
private bool _favoriteLoading;
/********************************
* Поделиться ссылкой
*********************************/
private IJSObjectReference? _shareModule;
private bool _isWebShareSupported = false;
protected override async Task OnInitializedAsync()
{
@@ -417,6 +433,18 @@
AudioPlayerService.OnEndedTrack += OnPlayerStateChanged;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Загружаем JS-модуль
_shareModule = await JS.InvokeAsync<IJSObjectReference>("import", "/js/shareUtils.js");
// Проверяем поддержку Web Share API
_isWebShareSupported = await _shareModule.InvokeAsync<bool>("isSupported");
StateHasChanged();
}
}
private void OnPlayerStateChanged()
{
InvokeAsync(StateHasChanged);
@@ -803,9 +831,58 @@
}
#endregion
#region Поделитьсы ссылкой
/// <summary> Поделиться ссылкой </summary>
private async Task SharePlaylist()
{
if (_shareModule == null) return;
var shareUrl = Navigation.Uri;
var shareTitle = "🎵 Поделиться плейлистом";
var shareText = _playlist?.Title != null
? $"Послушайте плейлист '{_playlist.Title}'!"
: "Послушайте этот плейлист!";
if (_isWebShareSupported)
{
var result = await _shareModule.InvokeAsync<ShareResult>("shareLink", shareTitle, shareText, shareUrl);
if (result?.Success == false && !string.IsNullOrEmpty(result.Error) && !result.Cancelled)
{
Snackbar.Add($"Не удалось поделиться: {result.Error}", Severity.Warning);
await ShowShareDialog(shareUrl);
}
}
else
{
await ShowShareDialog(shareUrl);
}
}
/// <summary> Модальное окно, чтобы поделиться ссылкой </summary>
private async Task ShowShareDialog(string url)
{
var parameters = new DialogParameters<ShareDialog>
{
{ x => x.ShareUrl, url }
};
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small, FullWidth = true };
await DialogService.ShowAsync<ShareDialog>("Поделиться", parameters, options);
}
// Вспомогательный класс для результата
private class ShareResult
{
public bool Success { get; set; }
public string? Error { get; set; }
public bool Cancelled { get; set; }
}
#endregion
public void Dispose()
{
AudioPlayerService.OnStartedTrack -= OnPlayerStateChanged;
AudioPlayerService.OnEndedTrack -= OnPlayerStateChanged;
_shareModule?.DisposeAsync();
}
}

View File

@@ -28,6 +28,9 @@
<Content Update="wwwroot\js\AudioPlayer.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\js\shareUtils.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,19 @@
export function isSupported() {
return !!navigator.share;
}
export async function shareLink(title, text, url) {
if (!navigator.share) {
return { success: false, error: 'Web Share API не поддерживается' };
}
try {
await navigator.share({ title, text, url });
return { success: true };
} catch (error) {
if (error.name === 'AbortError') {
return { success: false, cancelled: true };
}
console.error('Ошибка при шеринге:', error);
return { success: false, error: error.message };
}
}