| [Service] | [Service] | ||||
| public interface ITrackService | public interface ITrackService | ||||
| { | { | ||||
| Task<SearchResponse> ListSearchAsync(SearchRequest request); | |||||
| Task<IEnumerable<SearchResponse>> ListSearchAsync(SearchRequest request); | |||||
| Task<SingleTrackResponse> ListSingleTrackAsync(SingleTrackRequest request); | Task<SingleTrackResponse> ListSingleTrackAsync(SingleTrackRequest request); | ||||
| Task<MultipleTrackResponse> ListMultipleTrackAsync(MultipleTrackRequest request); | Task<MultipleTrackResponse> ListMultipleTrackAsync(MultipleTrackRequest request); | ||||
| Task SaveTracks(SaveTracksRequest request); | Task SaveTracks(SaveTracksRequest request); |
| "IIS Express": { | "IIS Express": { | ||||
| "commandName": "IISExpress", | "commandName": "IISExpress", | ||||
| "launchBrowser": true, | "launchBrowser": true, | ||||
| "environmentVariables": { | "environmentVariables": { | ||||
| "ASPNETCORE_ENVIRONMENT": "Development" | "ASPNETCORE_ENVIRONMENT": "Development" | ||||
| }, | }, |
| <ItemGroup> | <ItemGroup> | ||||
| <PackageReference Include="Blazored.LocalStorage" Version="4.2.0" /> | <PackageReference Include="Blazored.LocalStorage" Version="4.2.0" /> | ||||
| <PackageReference Include="Blazorise.Bootstrap" Version="1.0.6" /> | |||||
| <PackageReference Include="Blazorise.Components" Version="1.0.6" /> | |||||
| <PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.0.6" /> | |||||
| <PackageReference Include="Grpc.Net.Client" Version="2.47.0" /> | <PackageReference Include="Grpc.Net.Client" Version="2.47.0" /> | ||||
| <PackageReference Include="Grpc.Net.Client.Web" Version="2.47.0" /> | <PackageReference Include="Grpc.Net.Client.Web" Version="2.47.0" /> | ||||
| <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="6.0.8" /> | <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="6.0.8" /> | ||||
| <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.7" /> | |||||
| <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.7" PrivateAssets="all" /> | |||||
| <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.8" /> | |||||
| <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.8" PrivateAssets="all" /> | |||||
| <PackageReference Include="MudBlazor" Version="6.0.14" /> | <PackageReference Include="MudBlazor" Version="6.0.14" /> | ||||
| <PackageReference Include="protobuf-net.Grpc" Version="1.0.171" /> | <PackageReference Include="protobuf-net.Grpc" Version="1.0.171" /> | ||||
| </ItemGroup> | </ItemGroup> |
| private async Task Click() | private async Task Click() | ||||
| { | { | ||||
| var userInfo = await localStorage.GetItemAsync<string>("user_info"); | |||||
| var user = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage | |||||
| { | |||||
| Id = userInfo | |||||
| }); | |||||
| TokenMessage tokenM = new TokenMessage { Token = user.Token, RefreshToken = user.RefreshToken }; | |||||
| SearchRequest request = new() { Query = "aitch", Type = "track", Token = user.Token }; | |||||
| try | |||||
| { | |||||
| SearchResponse searchResponse = await SearchService.GetListSearchAsync(request); | |||||
| if (searchResponse.ResponseMsg == System.Net.HttpStatusCode.Unauthorized) | |||||
| { | |||||
| string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, tokenM, user, localStorage, identityService); | |||||
| } | |||||
| } | |||||
| catch (RpcException e) | |||||
| { | |||||
| if (e.StatusCode == StatusCode.Cancelled) | |||||
| { | |||||
| return; | |||||
| } | |||||
| throw; | |||||
| } | |||||
| //var userInfo = await localStorage.GetItemAsync<string>("user_info"); | |||||
| //var user = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage | |||||
| // { | |||||
| // Id = userInfo | |||||
| // }); | |||||
| //TokenMessage tokenM = new TokenMessage { Token = user.Token, RefreshToken = user.RefreshToken }; | |||||
| //SearchRequest request = new() { Query = "aitch", Type = "track", Token = user.Token }; | |||||
| //try | |||||
| //{ | |||||
| // SearchResponse searchResponse = await SearchService.GetListSearchAsync(request); | |||||
| // if (searchResponse.ResponseMsg == System.Net.HttpStatusCode.Unauthorized) | |||||
| // { | |||||
| // string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, tokenM, user, localStorage, identityService); | |||||
| // } | |||||
| //} | |||||
| //catch (RpcException e) | |||||
| //{ | |||||
| // if (e.StatusCode == StatusCode.Cancelled) | |||||
| // { | |||||
| // return; | |||||
| // } | |||||
| // throw; | |||||
| //} | |||||
| } | } | ||||
| @using System.Security.Claims | @using System.Security.Claims | ||||
| @inject Blazored.LocalStorage.ILocalStorageService localStorage | @inject Blazored.LocalStorage.ILocalStorageService localStorage | ||||
| @*<Heading Size="HeadingSize.Is1" Margin="Margin.Is3.FromBottom">Dashboard</Heading>*@ | |||||
| <AuthorizeView> | <AuthorizeView> | ||||
| <Authorized> | <Authorized> | ||||
| <p>Dobrodosli @context.User.Claims.FirstOrDefault(x => x.Type == "name")?.Value.ToUpper()</p> | |||||
| </Authorized> | |||||
| <NotAuthorized> | |||||
| <Paragraph>Dobrodošli @context.User.Claims.FirstOrDefault(x => x.Type == "name")?.Value.ToUpper()</Paragraph> | |||||
| </Authorized> | |||||
| @*<NotAuthorized> | |||||
| <Paragraph> | |||||
| Nisi autorizovan. | Nisi autorizovan. | ||||
| <button class="btn btn-primary" @onclick="LoginUser">Login</button> | |||||
| </NotAuthorized> | |||||
| </Paragraph> | |||||
| <button class="btn btn-success" @onclick="LoginUser">Login</button> | |||||
| </NotAuthorized>*@ | |||||
| </AuthorizeView> | </AuthorizeView> | ||||
| <PageTitle>Index</PageTitle> | |||||
| <MudText Typo="Typo.h3" GutterBottom="true">Pozdrav Diligent!</MudText> | |||||
| <MudText Class="mb-8"> | |||||
| Dobrodošli u našu NemAn aplikaciju. | |||||
| </MudText> | |||||
| global using Microsoft.AspNetCore.Components.Authorization; | global using Microsoft.AspNetCore.Components.Authorization; | ||||
| using Blazored.LocalStorage; | using Blazored.LocalStorage; | ||||
| using Blazorise; | |||||
| using Blazorise.Bootstrap; | |||||
| using Blazorise.Icons.FontAwesome; | |||||
| using Grpc.Net.Client; | using Grpc.Net.Client; | ||||
| using Grpc.Net.Client.Web; | using Grpc.Net.Client.Web; | ||||
| using Microsoft.AspNetCore.Components; | using Microsoft.AspNetCore.Components; | ||||
| }); | }); | ||||
| builder.Services.AddAuthorizationCore(); | builder.Services.AddAuthorizationCore(); | ||||
| //builder.Services.AddScoped<AuthenticationStateProvider, AuthClientService>(); | //builder.Services.AddScoped<AuthenticationStateProvider, AuthClientService>(); | ||||
| builder.Services.AddMudServices(); | |||||
| builder.Services | |||||
| .AddBlazorise(options => | |||||
| { | |||||
| options.Immediate = true; | |||||
| }) | |||||
| .AddBootstrapProviders() | |||||
| .AddFontAwesomeIcons(); | |||||
| builder.Services.AddScoped<ITrackClientService, TrackClientService>(); | builder.Services.AddScoped<ITrackClientService, TrackClientService>(); | ||||
| builder.Services.AddScoped<IAuthClientService, AuthClientService>(); | builder.Services.AddScoped<IAuthClientService, AuthClientService>(); |
| { | { | ||||
| "iisSettings": { | |||||
| "windowsAuthentication": false, | |||||
| "anonymousAuthentication": true | |||||
| }, | |||||
| "profiles": { | "profiles": { | ||||
| "NemAnCore": { | "NemAnCore": { | ||||
| "commandName": "Project", | "commandName": "Project", | ||||
| "launchBrowser": true, | "launchBrowser": true, | ||||
| "environmentVariables": { | "environmentVariables": { | ||||
| "ASPNETCORE_ENVIRONMENT": "Development" | "ASPNETCORE_ENVIRONMENT": "Development" | ||||
| } | |||||
| }, | |||||
| "hotReloadProfile": "aspnetcore", | |||||
| "hotReloadEnabled": false | |||||
| } | } | ||||
| }, | |||||
| "iisSettings": { | |||||
| "windowsAuthentication": false, | |||||
| "anonymousAuthentication": true | |||||
| } | } | ||||
| } | |||||
| } |
| { | { | ||||
| public interface ITrackClientService | public interface ITrackClientService | ||||
| { | { | ||||
| Task<SearchResponse> GetListSearchAsync(SearchRequest request); | |||||
| Task<IEnumerable<SearchResponse>> GetListSearchAsync(SearchRequest request); | |||||
| Task<SingleTrackResponse> GetListSingleTrackAsync(SingleTrackRequest request); | Task<SingleTrackResponse> GetListSingleTrackAsync(SingleTrackRequest request); | ||||
| Task<MultipleTrackResponse> GetListMultipleTrackAsync(MultipleTrackRequest request); | Task<MultipleTrackResponse> GetListMultipleTrackAsync(MultipleTrackRequest request); | ||||
| Task PutSaveTracks(SaveTracksRequest request); | Task PutSaveTracks(SaveTracksRequest request); |
| } | } | ||||
| public async Task<SearchResponse> GetListSearchAsync(SearchRequest request) | |||||
| public async Task<IEnumerable<SearchResponse>> GetListSearchAsync(SearchRequest request) | |||||
| { | { | ||||
| return await _serviceClient.ListSearchAsync(request); | return await _serviceClient.ListSearchAsync(request); | ||||
| } | } |
| @using Grpc.Core | |||||
| @using GrpcShared.DTO | |||||
| @using GrpcShared.DTO.Auth | |||||
| @using GrpcShared.DTO.Search | |||||
| @using NemAnBlazor.Services.Interfaces | |||||
| @using System.Net | |||||
| @inject NavigationManager NavigationManager | |||||
| @inject Blazored.LocalStorage.ILocalStorageService localStorage | |||||
| @inject ITrackClientService SearchService | |||||
| @inject IAuthClientService AuthService | |||||
| @inject IIdentityClientService identityService | |||||
| <Bar Breakpoint="Breakpoint.Desktop" | |||||
| Background="Background.Body" | |||||
| ThemeContrast="ThemeContrast.Light"> | |||||
| <BarToggler /> | |||||
| <BarMenu> | |||||
| <BarStart> | |||||
| <Autocomplete TItem="SearchResponse" | |||||
| TValue="string" | |||||
| Data="@Searches" | |||||
| TextField="@(( item ) => item!.Tracks!.Items!.FirstOrDefault()!.Name)" | |||||
| ValueField="@(( item ) => item!.Tracks!.Items!.FirstOrDefault()!.Name)" | |||||
| Filter="AutocompleteFilter.StartsWith" | |||||
| Placeholder="Search..." | |||||
| FreeTyping> | |||||
| </Autocomplete> | |||||
| </BarStart> | |||||
| <BarEnd> | |||||
| <BarItem> | |||||
| <Button class="btn btn-success" @onclick="LoginUser">Log in</Button> | |||||
| </BarItem> | |||||
| </BarEnd> | |||||
| </BarMenu> | |||||
| </Bar> | |||||
| @code { | |||||
| public IEnumerable<SearchResponse>? Searches; | |||||
| private string? search; | |||||
| private async Task LoginUser() | |||||
| { | |||||
| CodeRequest authParams = await AuthService.GetAuthParams(); | |||||
| string url = $"https://accounts.spotify.com/en/authorize?client_id={authParams.ClientId}&redirect_uri={authParams.RedirectURI}&response_type={authParams.ResponseType}&scope={authParams.Scope}&show_dialog={authParams.ShowDialog}"; | |||||
| NavigationManager.NavigateTo(url); | |||||
| } | |||||
| protected override async Task OnInitializedAsync(){ | |||||
| var userInfo = await localStorage.GetItemAsync<string>("user_info"); | |||||
| if (userInfo != null) NavigationManager.NavigateTo("/home"); | |||||
| var user = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage | |||||
| { | |||||
| Id = userInfo! | |||||
| }); | |||||
| TokenMessage tokenM = new TokenMessage { Token = user.Token, RefreshToken = user.RefreshToken }; | |||||
| SearchRequest request = new() { Query = search, Type = "track", Token = user.Token }; | |||||
| try | |||||
| { | |||||
| Searches = await SearchService.GetListSearchAsync(request); | |||||
| if (Searches == null) | |||||
| { | |||||
| string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, tokenM, user, localStorage, identityService); | |||||
| } | |||||
| await base.OnInitializedAsync(); | |||||
| } | |||||
| catch (RpcException e) | |||||
| { | |||||
| if (e.StatusCode == StatusCode.Cancelled) | |||||
| { | |||||
| return; | |||||
| } | |||||
| throw; | |||||
| } | |||||
| } | |||||
| } |
| @inherits LayoutComponentBase | @inherits LayoutComponentBase | ||||
| <MudThemeProvider /> | |||||
| <MudDialogProvider /> | |||||
| <MudSnackbarProvider /> | |||||
| <MudLayout> | |||||
| <MudAppBar Elevation="0"> | |||||
| <MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@((e) => DrawerToggle())" /> | |||||
| </MudAppBar> | |||||
| <MudDrawer @bind-Open="_drawerOpen" Elevation="1"> | |||||
| <MudDrawerHeader> | |||||
| <MudText Typo="Typo.h6">Spotify</MudText> | |||||
| </MudDrawerHeader> | |||||
| <NavMenu /> | |||||
| </MudDrawer> | |||||
| <MudMainContent> | |||||
| <MudContainer MaxWidth="MaxWidth.Large" Class="my-16 pt-16"> | |||||
| <Layout Sider> | |||||
| <LayoutSider> | |||||
| <LayoutSiderContent> | |||||
| <NavMenu /> | |||||
| </LayoutSiderContent> | |||||
| </LayoutSider> | |||||
| <Layout> | |||||
| <LayoutHeader Fixed> | |||||
| <HeaderBar /> | |||||
| </LayoutHeader> | |||||
| <LayoutContent Padding="Padding.Is4.OnX" Background="Background.Light"> | |||||
| @Body | @Body | ||||
| </MudContainer> | |||||
| </MudMainContent> | |||||
| </MudLayout> | |||||
| @code { | |||||
| bool _drawerOpen = true; | |||||
| void DrawerToggle() | |||||
| { | |||||
| _drawerOpen = !_drawerOpen; | |||||
| } | |||||
| } | |||||
| </LayoutContent> | |||||
| </Layout> | |||||
| </Layout> |
| <MudNavMenu> | |||||
| <MudNavLink Href="" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Home">Dashboard</MudNavLink> | |||||
| <MudNavLink Href="home" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.Add">Home</MudNavLink> | |||||
| <MudNavLink Href="search" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.List">Search</MudNavLink> | |||||
| </MudNavMenu> | |||||
| <Bar Mode="BarMode.VerticalInline" | |||||
| CollapseMode="BarCollapseMode.Small" | |||||
| Breakpoint="Breakpoint.Desktop" | |||||
| NavigationBreakpoint="Breakpoint.Tablet" | |||||
| ThemeContrast="ThemeContrast.Dark" | |||||
| Background="Background.Body"> | |||||
| <BarToggler/> | |||||
| <BarBrand> | |||||
| <BarItem> | |||||
| <BarLink To=""> | |||||
| <img src="/spotify-icon-marilyn-scott-0.png" style="width: 32px; height: 32px; padding: 1px" /> | |||||
| Spotify | |||||
| </BarLink> | |||||
| </BarItem> | |||||
| </BarBrand> | |||||
| <BarMenu> | |||||
| <BarStart> | |||||
| <BarItem> | |||||
| <BarLink To=""> | |||||
| <BarIcon IconName="IconName.Dashboard" /> | |||||
| Dashboard | |||||
| </BarLink> | |||||
| </BarItem> | |||||
| <BarItem> | |||||
| <BarLink To="/home"> | |||||
| <BarIcon IconName="IconName.Home" /> | |||||
| Home | |||||
| </BarLink> | |||||
| </BarItem> | |||||
| </BarStart> | |||||
| </BarMenu> | |||||
| </Bar> | |||||
| @code { | @code { | ||||
| private bool collapseNavMenu = true; | |||||
| private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; | |||||
| private void ToggleNavMenu() | |||||
| { | |||||
| collapseNavMenu = !collapseNavMenu; | |||||
| } | |||||
| } | } |
| @using NemAnBlazor.Shared | @using NemAnBlazor.Shared | ||||
| @using System.Web | @using System.Web | ||||
| @using Microsoft.AspNetCore.Components.Authorization | @using Microsoft.AspNetCore.Components.Authorization | ||||
| @using MudBlazor | |||||
| @using Blazorise | |||||
| @using Blazorise.Components |
| <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" /> | <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" /> | ||||
| <link href="css/app.css" rel="stylesheet" /> | <link href="css/app.css" rel="stylesheet" /> | ||||
| <link href="NemAnBlazor.styles.css" rel="stylesheet" /> | <link href="NemAnBlazor.styles.css" rel="stylesheet" /> | ||||
| <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" /> | |||||
| <link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" /> | |||||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"> | |||||
| <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css"> | |||||
| <link href="_content/Blazorise/blazorise.css" rel="stylesheet" /> | |||||
| <link href="_content/Blazorise.Bootstrap/blazorise.bootstrap.css" rel="stylesheet" /> | |||||
| </head> | </head> | ||||
| <body> | <body> | ||||
| <a class="dismiss">🗙</a> | <a class="dismiss">🗙</a> | ||||
| </div> | </div> | ||||
| <script src="_framework/blazor.webassembly.js"></script> | <script src="_framework/blazor.webassembly.js"></script> | ||||
| <script src="_content/MudBlazor/MudBlazor.min.js"></script> | |||||
| <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> | |||||
| <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> | |||||
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script> | |||||
| </body> | </body> | ||||
| </html> | </html> |
| { | { | ||||
| _httpClientFactory = httpClientFactory; | _httpClientFactory = httpClientFactory; | ||||
| } | } | ||||
| public async Task<SearchResponse> ListSearchAsync(SearchRequest request) | |||||
| public async Task<IEnumerable<SearchResponse>> ListSearchAsync(SearchRequest request) | |||||
| { | { | ||||
| var client = _httpClientFactory.CreateClient("HttpClient"); | var client = _httpClientFactory.CreateClient("HttpClient"); | ||||
| string url = $"search?q={request.Query}&type={request.Type}"; | string url = $"search?q={request.Query}&type={request.Type}"; | ||||
| return await HttpUtils.HttpUtils<SearchResponse>.GetData(client, url, request.Token!); | |||||
| return await HttpUtils.HttpUtils<IEnumerable<SearchResponse>>.GetData(client, url, request.Token!); | |||||
| } | } |