| @@ -15,7 +15,7 @@ namespace GrpcShared.Interfaces | |||
| [Service] | |||
| public interface ITrackService | |||
| { | |||
| Task<SearchResponse> ListSearchAsync(SearchRequest request); | |||
| Task<IEnumerable<SearchResponse>> ListSearchAsync(SearchRequest request); | |||
| Task<SingleTrackResponse> ListSingleTrackAsync(SingleTrackRequest request); | |||
| Task<MultipleTrackResponse> ListMultipleTrackAsync(MultipleTrackRequest request); | |||
| Task SaveTracks(SaveTracksRequest request); | |||
| @@ -11,7 +11,6 @@ | |||
| "IIS Express": { | |||
| "commandName": "IISExpress", | |||
| "launchBrowser": true, | |||
| "environmentVariables": { | |||
| "ASPNETCORE_ENVIRONMENT": "Development" | |||
| }, | |||
| @@ -16,11 +16,14 @@ | |||
| <ItemGroup> | |||
| <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.Web" Version="2.47.0" /> | |||
| <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="protobuf-net.Grpc" Version="1.0.171" /> | |||
| </ItemGroup> | |||
| @@ -36,34 +36,34 @@ | |||
| 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; | |||
| //} | |||
| } | |||
| @@ -11,22 +11,22 @@ | |||
| @using System.Security.Claims | |||
| @inject Blazored.LocalStorage.ILocalStorageService localStorage | |||
| @*<Heading Size="HeadingSize.Is1" Margin="Margin.Is3.FromBottom">Dashboard</Heading>*@ | |||
| <AuthorizeView> | |||
| <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. | |||
| <button class="btn btn-primary" @onclick="LoginUser">Login</button> | |||
| </NotAuthorized> | |||
| </Paragraph> | |||
| <button class="btn btn-success" @onclick="LoginUser">Login</button> | |||
| </NotAuthorized>*@ | |||
| </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> | |||
| @@ -1,5 +1,8 @@ | |||
| global using Microsoft.AspNetCore.Components.Authorization; | |||
| using Blazored.LocalStorage; | |||
| using Blazorise; | |||
| using Blazorise.Bootstrap; | |||
| using Blazorise.Icons.FontAwesome; | |||
| using Grpc.Net.Client; | |||
| using Grpc.Net.Client.Web; | |||
| using Microsoft.AspNetCore.Components; | |||
| @@ -25,7 +28,13 @@ builder.Services.AddScoped(_ => | |||
| }); | |||
| builder.Services.AddAuthorizationCore(); | |||
| //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<IAuthClientService, AuthClientService>(); | |||
| @@ -1,16 +1,17 @@ | |||
| { | |||
| "iisSettings": { | |||
| "windowsAuthentication": false, | |||
| "anonymousAuthentication": true | |||
| }, | |||
| "profiles": { | |||
| "NemAnCore": { | |||
| "commandName": "Project", | |||
| "launchBrowser": true, | |||
| "environmentVariables": { | |||
| "ASPNETCORE_ENVIRONMENT": "Development" | |||
| } | |||
| }, | |||
| "hotReloadProfile": "aspnetcore", | |||
| "hotReloadEnabled": false | |||
| } | |||
| }, | |||
| "iisSettings": { | |||
| "windowsAuthentication": false, | |||
| "anonymousAuthentication": true | |||
| } | |||
| } | |||
| } | |||
| @@ -8,7 +8,7 @@ namespace NemAnBlazor.Services.Interfaces | |||
| { | |||
| public interface ITrackClientService | |||
| { | |||
| Task<SearchResponse> GetListSearchAsync(SearchRequest request); | |||
| Task<IEnumerable<SearchResponse>> GetListSearchAsync(SearchRequest request); | |||
| Task<SingleTrackResponse> GetListSingleTrackAsync(SingleTrackRequest request); | |||
| Task<MultipleTrackResponse> GetListMultipleTrackAsync(MultipleTrackRequest request); | |||
| Task PutSaveTracks(SaveTracksRequest request); | |||
| @@ -20,7 +20,7 @@ namespace NemAnBlazor.Services | |||
| } | |||
| public async Task<SearchResponse> GetListSearchAsync(SearchRequest request) | |||
| public async Task<IEnumerable<SearchResponse>> GetListSearchAsync(SearchRequest request) | |||
| { | |||
| return await _serviceClient.ListSearchAsync(request); | |||
| } | |||
| @@ -0,0 +1,87 @@ | |||
| @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; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,31 +1,17 @@ | |||
| @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 | |||
| </MudContainer> | |||
| </MudMainContent> | |||
| </MudLayout> | |||
| @code { | |||
| bool _drawerOpen = true; | |||
| void DrawerToggle() | |||
| { | |||
| _drawerOpen = !_drawerOpen; | |||
| } | |||
| } | |||
| </LayoutContent> | |||
| </Layout> | |||
| </Layout> | |||
| @@ -1,16 +1,36 @@ | |||
| <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 { | |||
| private bool collapseNavMenu = true; | |||
| private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; | |||
| private void ToggleNavMenu() | |||
| { | |||
| collapseNavMenu = !collapseNavMenu; | |||
| } | |||
| } | |||
| @@ -10,4 +10,5 @@ | |||
| @using NemAnBlazor.Shared | |||
| @using System.Web | |||
| @using Microsoft.AspNetCore.Components.Authorization | |||
| @using MudBlazor | |||
| @using Blazorise | |||
| @using Blazorise.Components | |||
| @@ -9,8 +9,11 @@ | |||
| <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" /> | |||
| <link href="css/app.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> | |||
| <body> | |||
| @@ -22,7 +25,9 @@ | |||
| <a class="dismiss">🗙</a> | |||
| </div> | |||
| <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> | |||
| </html> | |||
| @@ -26,14 +26,14 @@ namespace SpotifyService.Services | |||
| { | |||
| _httpClientFactory = httpClientFactory; | |||
| } | |||
| public async Task<SearchResponse> ListSearchAsync(SearchRequest request) | |||
| public async Task<IEnumerable<SearchResponse>> ListSearchAsync(SearchRequest request) | |||
| { | |||
| var client = _httpClientFactory.CreateClient("HttpClient"); | |||
| 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!); | |||
| } | |||