Compare commits
2 Commits
07a52b12d6
...
6b399f7fb7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b399f7fb7 | ||
|
|
ab56c34646 |
@@ -210,7 +210,6 @@
|
|||||||
private async Task OnServiceVolumeChange(double volume)
|
private async Task OnServiceVolumeChange(double volume)
|
||||||
{
|
{
|
||||||
if (_audioElement == null) return;
|
if (_audioElement == null) return;
|
||||||
if (volume == AudioPlayerService.CurrentVolume) return;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
@using System.Text.RegularExpressions
|
||||||
|
@using PlaylistShared.Shared.DTO
|
||||||
|
@inject HttpClient Http
|
||||||
|
@inject ISnackbar Snackbar
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<TitleContent>
|
||||||
|
<MudText Typo="Typo.h6">Подключение Яндекс.Музыки</MudText>
|
||||||
|
</TitleContent>
|
||||||
|
<DialogContent>
|
||||||
|
<MudStepper @bind-ActiveIndex="_index" Vertical CenterLabels CompletedStepColor="Color.Success" @onwheel="HandleWheel">
|
||||||
|
<ChildContent>
|
||||||
|
<MudStep Title="Вход" Skippable>
|
||||||
|
<MudText Typo="Typo.body2" Class="mb-4">Нажмите на кнопку и разрешите доступ приложению.</MudText>
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
StartIcon="@PlaylistShared.Pwa.CustomIcons.Yandex"
|
||||||
|
Href="https://oauth.yandex.ru/authorize?response_type=token&client_id=23cabbbdc6cd418abb4b39c32c41195d"
|
||||||
|
Target="_blank"
|
||||||
|
@onclick="() => {_index++;}"
|
||||||
|
FullWidth>
|
||||||
|
Войти в Яндекс
|
||||||
|
</MudButton>
|
||||||
|
</MudStep>
|
||||||
|
|
||||||
|
<MudStep Title="Добавление токена">
|
||||||
|
Скопируйте значение <code>access_token</code> или <code>весь URL</code> из адресной строки после перенаправления.
|
||||||
|
<MudAlert Severity="Severity.Info" Class="mt-4">
|
||||||
|
https://music.yandex.ru/#access_token=<code>ВАШ_ТОКЕН</code>&...
|
||||||
|
</MudAlert>
|
||||||
|
<MudTextField @bind-Value="_rawInput" @bind-Value:after="Submit" Label="Вставьте скопированную ссылку или токен" Variant="Variant.Outlined" Margin="Margin.Dense" Error="_tokenErr" />
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Success" OnClick="Submit" FullWidth Class="mt-4">
|
||||||
|
Сохранить
|
||||||
|
</MudButton>
|
||||||
|
</MudStep>
|
||||||
|
</ChildContent>
|
||||||
|
<ActionContent Context="stepper">
|
||||||
|
<MudIconButton OnClick="@(() => stepper.PreviousStepAsync())" Icon="@Icons.Material.Filled.ArrowBack" Color="Color.Primary" Disabled="@(_index <= 0)" />
|
||||||
|
<MudSpacer />
|
||||||
|
<MudIconButton OnClick="@(() => stepper.NextStepAsync())" Icon="@Icons.Material.Filled.ArrowForward" Color="Color.Primary" Disabled="@(_index >= 1)" />
|
||||||
|
</ActionContent>
|
||||||
|
</MudStepper>
|
||||||
|
</DialogContent>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter] IMudDialogInstance MudDialog { get; set; }
|
||||||
|
private string _rawInput = "";
|
||||||
|
private int _index;
|
||||||
|
private bool _tokenErr = false;
|
||||||
|
|
||||||
|
private async Task HandleWheel(WheelEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (e.DeltaY > 0 && _index < 1) // Прокрутка вниз -> Вперед
|
||||||
|
{
|
||||||
|
_index++;
|
||||||
|
}
|
||||||
|
else if (e.DeltaY < 0 && _index > 0) // Прокрутка вверх -> Назад
|
||||||
|
{
|
||||||
|
_index--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Submit()
|
||||||
|
{
|
||||||
|
var token = ExtractToken(_rawInput);
|
||||||
|
if (string.IsNullOrWhiteSpace(token))
|
||||||
|
{
|
||||||
|
_tokenErr = true;
|
||||||
|
Snackbar.Add("Токен не найден", Severity.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_tokenErr = false;
|
||||||
|
|
||||||
|
var response = await Http.PostAsJsonAsync("/api/yandextoken/set", new SetYandexTokenRequest { Token = token });
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
Snackbar.Add("Токен успешно обновлен", Severity.Success);
|
||||||
|
MudDialog.Close(DialogResult.Ok(true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Snackbar.Add("Ошибка обновления токена. Повторите позже.", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ExtractToken(string input) =>
|
||||||
|
input.Contains("access_token=") ? Regex.Match(input, @"access_token=([^&]+)").Groups[1].Value : input.Trim();
|
||||||
|
|
||||||
|
public class SetYandexTokenRequest { public string Token { get; set; } }
|
||||||
|
}
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
@* Компонент с инструкцией по получению токена Яндекс.Музыки *@
|
|
||||||
|
|
||||||
<MudContainer Class="pa-4">
|
|
||||||
<MudText Typo="Typo.body2" GutterBottom>
|
|
||||||
Токен нужен для доступа к вашим плейлистам. Получите его один раз:
|
|
||||||
</MudText>
|
|
||||||
|
|
||||||
<!-- Вертикальный список шагов -->
|
|
||||||
<MudStack Class="my-4">
|
|
||||||
<MudStack Row AlignItems="AlignItems.Center">
|
|
||||||
<MudPaper Elevation="0" Style="width: 28px; height: 28px; background-color: var(--mud-palette-primary); color: white; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: bold;">1</MudPaper>
|
|
||||||
<MudText>
|
|
||||||
Перейдите по <MudLink Href="https://oauth.yandex.ru/authorize?response_type=token&client_id=23cabbbdc6cd418abb4b39c32c41195d" Target="_blank">ссылке</MudLink>
|
|
||||||
</MudText>
|
|
||||||
</MudStack>
|
|
||||||
|
|
||||||
<MudStack Row AlignItems="AlignItems.Center">
|
|
||||||
<MudPaper Elevation="0" Style="width: 28px; height: 28px; background-color: var(--mud-palette-primary); color: white; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: bold;">1</MudPaper>
|
|
||||||
<MudText>
|
|
||||||
Авторизуйтесь в Яндексе (если ещё не вошли)
|
|
||||||
</MudText>
|
|
||||||
</MudStack>
|
|
||||||
|
|
||||||
<MudStack Row AlignItems="AlignItems.Center">
|
|
||||||
<MudPaper Elevation="0" Style="width: 28px; height: 28px; background-color: var(--mud-palette-primary); color: white; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: bold;">1</MudPaper>
|
|
||||||
<MudText>
|
|
||||||
Нажмите «Разрешить»
|
|
||||||
</MudText>
|
|
||||||
</MudStack>
|
|
||||||
|
|
||||||
<MudStack Row AlignItems="AlignItems.Center">
|
|
||||||
<MudPaper Elevation="0" Style="width: 28px; height: 28px; background-color: var(--mud-palette-primary); color: white; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: bold;">1</MudPaper>
|
|
||||||
<MudText>
|
|
||||||
Скопируйте <strong>access_token</strong> из адресной строки после перенаправления
|
|
||||||
</MudText>
|
|
||||||
</MudStack>
|
|
||||||
</MudStack>
|
|
||||||
|
|
||||||
<MudAlert Severity="Severity.Info" Class="mt-4">
|
|
||||||
Пример: <code>https://music.yandex.ru/#access_token=ВАШ_ТОКЕН&...</code>
|
|
||||||
</MudAlert>
|
|
||||||
|
|
||||||
<MudAlert Severity="Severity.Warning" Class="mt-2">
|
|
||||||
Токен даёт доступ к вашим плейлистам. Никому его не сообщайте.
|
|
||||||
</MudAlert>
|
|
||||||
|
|
||||||
<MudAlert Severity="Severity.Success" Class="mt-2" Icon="@Icons.Material.Filled.Security">
|
|
||||||
Ваш токен сохраняется в зашифрованном виде и никому не передаётся.
|
|
||||||
</MudAlert>
|
|
||||||
</MudContainer>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.instruction-steps {
|
|
||||||
margin: 16px 0;
|
|
||||||
}
|
|
||||||
.step-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
.step-number {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
background-color: var(--mud-palette-primary);
|
|
||||||
color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-weight: bold;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
.step-content {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
8
PlaylistShared.Pwa/Icons/CustomIcons.cs
Normal file
8
PlaylistShared.Pwa/Icons/CustomIcons.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace PlaylistShared.Pwa;
|
||||||
|
|
||||||
|
public static class CustomIcons
|
||||||
|
{
|
||||||
|
// SVG путь для логотипа Яндекса (буква Я в круге или просто Я)
|
||||||
|
public const string Yandex = "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm.72 15.79h-2.14v-1.58c-.37.49-.87.89-1.48 1.18-.61.29-1.29.44-2.03.44-1.2 0-2.13-.34-2.8-.1-1.02-.66-1.52-1.61-1.52-2.84 0-1.25.43-2.22 1.28-2.91.85-.69 2.05-1.04 3.59-1.04h1.1v-.84c0-.62-.15-1.07-.46-1.34-.31-.27-.79-.41-1.44-.41-.53 0-1.02.08-1.48.24-.46.16-.9.41-1.32.74v-1.8c.48-.25 1.01-.45 1.58-.59.57-.14 1.15-.21 1.74-.21 1.45 0 2.53.33 3.23 1 .7.67 1.05 1.66 1.05 2.97v6.29zm-2.14-5.18h-.9c-.8 0-1.4.15-1.8.44-.4.29-.6.74-.6 1.34 0 .55.16.96.48 1.23.32.27.76.41 1.32.41.51 0 .97-.13 1.37-.39.4-.26.6-.64.6-1.14v-1.89z";
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,65 +1,52 @@
|
|||||||
@page "/profile"
|
@page "/profile"
|
||||||
<PageTitle>Профиль - Playlist Share</PageTitle>
|
|
||||||
|
|
||||||
@using Microsoft.AspNetCore.Authorization
|
|
||||||
@using PlaylistShared.Pwa.Components.Profile
|
|
||||||
@using PlaylistShared.Shared.DTO
|
|
||||||
@attribute [Authorize]
|
@attribute [Authorize]
|
||||||
@inject HttpClient Http
|
@inject HttpClient Http
|
||||||
@inject ISnackbar Snackbar
|
@inject IDialogService DialogService
|
||||||
|
@using PlaylistShared.Pwa.Components.Profile
|
||||||
|
@using PlaylistShared.Shared.Profile
|
||||||
|
|
||||||
<MudContainer MaxWidth="MaxWidth.Small" Class="mt-8">
|
<MudContainer MaxWidth="MaxWidth.Small" Class="mt-8">
|
||||||
|
<MudText Typo="Typo.h4" Class="mb-6">Профиль</MudText>
|
||||||
|
|
||||||
|
<MudStack Spacing="4">
|
||||||
|
@*
|
||||||
|
<!-- Секция почты -->
|
||||||
<MudCard>
|
<MudCard>
|
||||||
<MudCardHeader>
|
|
||||||
<CardHeaderContent>
|
|
||||||
<MudText Typo="Typo.h5">Личный кабинет</MudText>
|
|
||||||
</CardHeaderContent>
|
|
||||||
</MudCardHeader>
|
|
||||||
<MudCardContent>
|
<MudCardContent>
|
||||||
<MudStack Row Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="mb-4">
|
<MudText Typo="Typo.h6" GutterBottom="true">Данные аккаунта</MudText>
|
||||||
<MudText Typo="Typo.body2">
|
<MudTextField @bind-Value="_email" Label="Электронная почта" ReadOnly="true" Variant="Variant.Outlined" Margin="Margin.Dense" />
|
||||||
Здесь вы можете указать токен доступа к Яндекс.Музыке.
|
<MudButton Variant="Variant.Text" Color="Color.Primary" Class="mt-2" Disabled="true">Сменить почту</MudButton>
|
||||||
</MudText>
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.HelpOutline"
|
|
||||||
Color="Color.Info"
|
|
||||||
OnClick="() => _instructionDrawerOpen = true"
|
|
||||||
Title="Как получить токен?" />
|
|
||||||
</MudStack>
|
|
||||||
|
|
||||||
<MudTextField @bind-Value="_token" Label="Токен Яндекс.Музыки" Variant="Variant.Outlined" FullWidth="true" />
|
|
||||||
|
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="SaveToken" Class="mt-4" FullWidth="true">
|
|
||||||
Сохранить токен
|
|
||||||
</MudButton>
|
|
||||||
|
|
||||||
<MudText Class="mt-4" Typo="Typo.body2">Статус: @_statusText</MudText>
|
|
||||||
</MudCardContent>
|
</MudCardContent>
|
||||||
</MudCard>
|
</MudCard>
|
||||||
|
*@
|
||||||
|
|
||||||
|
<!-- Секция Яндекс.Музыки -->
|
||||||
|
<MudCard>
|
||||||
|
<MudCardContent>
|
||||||
|
<MudStack Row Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||||
|
<MudStack Spacing="0">
|
||||||
|
<MudText Typo="Typo.h6">Яндекс.Музыка</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Color="@(_hasToken? Color.Success: Color.Error)">
|
||||||
|
@_statusText
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="OpenTokenDialog">
|
||||||
|
@(_hasToken ? "Переподключить" : "Установить")
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCard>
|
||||||
|
</MudStack>
|
||||||
</MudContainer>
|
</MudContainer>
|
||||||
|
|
||||||
<!-- Выдвижная панель с инструкцией -->
|
|
||||||
<MudDrawer @bind-Open="_instructionDrawerOpen"
|
|
||||||
Anchor="Anchor.Right"
|
|
||||||
Variant="DrawerVariant.Temporary"
|
|
||||||
Elevation="3"
|
|
||||||
Width="500px"
|
|
||||||
MiniWidth="0px">
|
|
||||||
<MudDrawerHeader>
|
|
||||||
<MudText Typo="Typo.h6">Как получить токен Яндекс.Музыки</MudText>
|
|
||||||
</MudDrawerHeader>
|
|
||||||
<MudDivider />
|
|
||||||
<YandexTokenInstructions />
|
|
||||||
</MudDrawer>
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _token = "";
|
private string _email = "user@example.com"; // Загрузите из стейта или API
|
||||||
private string _statusText = "Загрузка...";
|
private string _statusText = "Загрузка...";
|
||||||
private bool _instructionDrawerOpen = false;
|
private bool _hasToken;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync() => await LoadStatus();
|
||||||
{
|
|
||||||
await LoadStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LoadStatus()
|
private async Task LoadStatus()
|
||||||
{
|
{
|
||||||
@@ -68,36 +55,19 @@
|
|||||||
var response = await Http.GetFromJsonAsync<ApiResponse<YandexTokenStatus>>("/api/yandextoken/status");
|
var response = await Http.GetFromJsonAsync<ApiResponse<YandexTokenStatus>>("/api/yandextoken/status");
|
||||||
if (response?.Success == true)
|
if (response?.Success == true)
|
||||||
{
|
{
|
||||||
_statusText = response.Data.HasToken
|
_hasToken = response.Data.HasToken;
|
||||||
? $"Токен установлен{(response.Data.IsValid ? "" : " (просрочен)")}"
|
_statusText = _hasToken ? "Аккаунт подключен" : "Аккаунт не подключен";
|
||||||
: "Токен не установлен";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { _statusText = "Не удалось загрузить статус"; }
|
catch { _statusText = "Ошибка загрузки статуса"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveToken()
|
private async Task OpenTokenDialog()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(_token))
|
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Small, FullWidth = true };
|
||||||
{
|
var dialog = await DialogService.ShowAsync<YandexTokenDialog>("", options);
|
||||||
Snackbar.Add("Введите токен", Severity.Warning);
|
var result = await dialog.Result;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = new SetYandexTokenRequest { Token = _token };
|
if (!result.Canceled) await LoadStatus();
|
||||||
var response = await Http.PostAsJsonAsync("/api/yandextoken/set", request);
|
|
||||||
if (response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
Snackbar.Add("Токен сохранён", Severity.Success);
|
|
||||||
await LoadStatus();
|
|
||||||
_token = "";
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Snackbar.Add("Ошибка сохранения токена", Severity.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class YandexTokenStatus { public bool HasToken { get; set; } public bool IsValid { get; set; } }
|
|
||||||
public class SetYandexTokenRequest { public string Token { get; set; } }
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user