Merge pull request #85 from mateuszskoczek/features/searching
Features/searching
This commit is contained in:
@@ -2,20 +2,8 @@
|
|||||||
|
|
||||||
namespace WatchIt.Common.Model.Accounts;
|
namespace WatchIt.Common.Model.Accounts;
|
||||||
|
|
||||||
public abstract class AccountProfilePicture
|
public abstract class AccountProfilePicture : Picture
|
||||||
{
|
{
|
||||||
#region PROPERTIES
|
|
||||||
|
|
||||||
[JsonPropertyName("image")]
|
|
||||||
public required byte[] Image { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("mime_type")]
|
|
||||||
public required string MimeType { get; set; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region CONSTRUCTORS
|
#region CONSTRUCTORS
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace WatchIt.Common.Model.Media;
|
namespace WatchIt.Common.Model.Rating;
|
||||||
|
|
||||||
public class MediaRatingRequest
|
public class RatingRequest
|
||||||
{
|
{
|
||||||
#region PROPERTIES
|
#region PROPERTIES
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ public class MediaRatingRequest
|
|||||||
#region CONSTRUCTORS
|
#region CONSTRUCTORS
|
||||||
|
|
||||||
[SetsRequiredMembers]
|
[SetsRequiredMembers]
|
||||||
public MediaRatingRequest(short rating)
|
public RatingRequest(short rating)
|
||||||
{
|
{
|
||||||
Rating = rating;
|
Rating = rating;
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace WatchIt.Common.Model.Media;
|
namespace WatchIt.Common.Model.Rating;
|
||||||
|
|
||||||
public class MediaRatingResponse
|
public class RatingResponse
|
||||||
{
|
{
|
||||||
#region PROPERTIES
|
#region PROPERTIES
|
||||||
|
|
||||||
@@ -20,10 +20,10 @@ public class MediaRatingResponse
|
|||||||
#region CONSTRUCTORS
|
#region CONSTRUCTORS
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public MediaRatingResponse() {}
|
public RatingResponse() {}
|
||||||
|
|
||||||
[SetsRequiredMembers]
|
[SetsRequiredMembers]
|
||||||
public MediaRatingResponse(double ratingAverage, long ratingCount)
|
public RatingResponse(double ratingAverage, long ratingCount)
|
||||||
{
|
{
|
||||||
RatingAverage = ratingAverage;
|
RatingAverage = ratingAverage;
|
||||||
RatingCount = ratingCount;
|
RatingCount = ratingCount;
|
||||||
@@ -66,7 +66,7 @@ public abstract class QueryParameters
|
|||||||
(
|
(
|
||||||
!string.IsNullOrEmpty(property)
|
!string.IsNullOrEmpty(property)
|
||||||
&&
|
&&
|
||||||
new Regex(regexQuery).IsMatch(property)
|
Regex.IsMatch(property, regexQuery, RegexOptions.IgnoreCase)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using WatchIt.Common.Model.Genres;
|
using WatchIt.Common.Model.Genres;
|
||||||
using WatchIt.Common.Model.Media;
|
using WatchIt.Common.Model.Media;
|
||||||
using WatchIt.Common.Model.Photos;
|
using WatchIt.Common.Model.Photos;
|
||||||
|
using WatchIt.Common.Model.Rating;
|
||||||
using WatchIt.WebAPI.Services.Controllers.Media;
|
using WatchIt.WebAPI.Services.Controllers.Media;
|
||||||
|
|
||||||
namespace WatchIt.WebAPI.Controllers;
|
namespace WatchIt.WebAPI.Controllers;
|
||||||
@@ -74,7 +75,7 @@ public class MediaController : ControllerBase
|
|||||||
|
|
||||||
[HttpGet("{id}/rating")]
|
[HttpGet("{id}/rating")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[ProducesResponseType(typeof(MediaRatingResponse), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(RatingResponse), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult> GetMediaRating([FromRoute] long id) => await _mediaControllerService.GetMediaRating(id);
|
public async Task<ActionResult> GetMediaRating([FromRoute] long id) => await _mediaControllerService.GetMediaRating(id);
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ public class MediaController : ControllerBase
|
|||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult> PutMediaRating([FromRoute] long id, [FromBody] MediaRatingRequest data) => await _mediaControllerService.PutMediaRating(id, data);
|
public async Task<ActionResult> PutMediaRating([FromRoute] long id, [FromBody] RatingRequest data) => await _mediaControllerService.PutMediaRating(id, data);
|
||||||
|
|
||||||
[HttpDelete("{id}/rating")]
|
[HttpDelete("{id}/rating")]
|
||||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using WatchIt.Common.Model.Media;
|
using WatchIt.Common.Model.Media;
|
||||||
using WatchIt.Common.Model.Photos;
|
using WatchIt.Common.Model.Photos;
|
||||||
|
using WatchIt.Common.Model.Rating;
|
||||||
using WatchIt.WebAPI.Services.Controllers.Common;
|
using WatchIt.WebAPI.Services.Controllers.Common;
|
||||||
|
|
||||||
namespace WatchIt.WebAPI.Services.Controllers.Media;
|
namespace WatchIt.WebAPI.Services.Controllers.Media;
|
||||||
@@ -14,7 +15,7 @@ public interface IMediaControllerService
|
|||||||
|
|
||||||
Task<RequestResult> GetMediaRating(long mediaId);
|
Task<RequestResult> GetMediaRating(long mediaId);
|
||||||
Task<RequestResult> GetMediaRatingByUser(long mediaId, long userId);
|
Task<RequestResult> GetMediaRatingByUser(long mediaId, long userId);
|
||||||
Task<RequestResult> PutMediaRating(long mediaId, MediaRatingRequest data);
|
Task<RequestResult> PutMediaRating(long mediaId, RatingRequest data);
|
||||||
Task<RequestResult> DeleteMediaRating(long mediaId);
|
Task<RequestResult> DeleteMediaRating(long mediaId);
|
||||||
|
|
||||||
Task<RequestResult> PostMediaView(long mediaId);
|
Task<RequestResult> PostMediaView(long mediaId);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using SimpleToolkit.Extensions;
|
|||||||
using WatchIt.Common.Model.Genres;
|
using WatchIt.Common.Model.Genres;
|
||||||
using WatchIt.Common.Model.Media;
|
using WatchIt.Common.Model.Media;
|
||||||
using WatchIt.Common.Model.Photos;
|
using WatchIt.Common.Model.Photos;
|
||||||
|
using WatchIt.Common.Model.Rating;
|
||||||
using WatchIt.Database;
|
using WatchIt.Database;
|
||||||
using WatchIt.Database.Model.Media;
|
using WatchIt.Database.Model.Media;
|
||||||
using WatchIt.Database.Model.Rating;
|
using WatchIt.Database.Model.Rating;
|
||||||
@@ -109,7 +110,7 @@ public class MediaControllerService(DatabaseContext database, IUserService userS
|
|||||||
|
|
||||||
double ratingAverage = item.RatingMedia.Any() ? item.RatingMedia.Average(x => x.Rating) : 0;
|
double ratingAverage = item.RatingMedia.Any() ? item.RatingMedia.Average(x => x.Rating) : 0;
|
||||||
long ratingCount = item.RatingMedia.Count();
|
long ratingCount = item.RatingMedia.Count();
|
||||||
MediaRatingResponse ratingResponse = new MediaRatingResponse(ratingAverage, ratingCount);
|
RatingResponse ratingResponse = new RatingResponse(ratingAverage, ratingCount);
|
||||||
|
|
||||||
return RequestResult.Ok(ratingResponse);
|
return RequestResult.Ok(ratingResponse);
|
||||||
}
|
}
|
||||||
@@ -131,7 +132,7 @@ public class MediaControllerService(DatabaseContext database, IUserService userS
|
|||||||
return RequestResult.Ok(rating.Value);
|
return RequestResult.Ok(rating.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RequestResult> PutMediaRating(long mediaId, MediaRatingRequest data)
|
public async Task<RequestResult> PutMediaRating(long mediaId, RatingRequest data)
|
||||||
{
|
{
|
||||||
short ratingValue = data.Rating;
|
short ratingValue = data.Rating;
|
||||||
if (ratingValue < 1 || ratingValue > 10)
|
if (ratingValue < 1 || ratingValue > 10)
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ public class MoviesControllerService : IMoviesControllerService
|
|||||||
|
|
||||||
public async Task<RequestResult> GetAllMovies(MovieQueryParameters query)
|
public async Task<RequestResult> GetAllMovies(MovieQueryParameters query)
|
||||||
{
|
{
|
||||||
IEnumerable<MovieResponse> data = await _database.MediaMovies.Select(x => new MovieResponse(x)).ToListAsync();
|
IEnumerable<MediaMovie> rawData = await _database.MediaMovies.ToListAsync();
|
||||||
|
IEnumerable<MovieResponse> data = rawData.Select(x => new MovieResponse(x));
|
||||||
data = query.PrepareData(data);
|
data = query.PrepareData(data);
|
||||||
return RequestResult.Ok(data);
|
return RequestResult.Ok(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ public class SeriesControllerService : ISeriesControllerService
|
|||||||
|
|
||||||
public async Task<RequestResult> GetAllSeries(SeriesQueryParameters query)
|
public async Task<RequestResult> GetAllSeries(SeriesQueryParameters query)
|
||||||
{
|
{
|
||||||
IEnumerable<SeriesResponse> data = await _database.MediaSeries.Select(x => new SeriesResponse(x)).ToListAsync();
|
IEnumerable<MediaSeries> rawData = await _database.MediaSeries.ToListAsync();
|
||||||
|
IEnumerable<SeriesResponse> data = rawData.Select(x => new SeriesResponse(x));
|
||||||
data = query.PrepareData(data);
|
data = query.PrepareData(data);
|
||||||
return RequestResult.Ok(data);
|
return RequestResult.Ok(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using WatchIt.Common.Model.Genres;
|
using WatchIt.Common.Model.Genres;
|
||||||
using WatchIt.Common.Model.Media;
|
using WatchIt.Common.Model.Media;
|
||||||
using WatchIt.Common.Model.Photos;
|
using WatchIt.Common.Model.Photos;
|
||||||
|
using WatchIt.Common.Model.Rating;
|
||||||
|
|
||||||
namespace WatchIt.Website.Services.WebAPI.Media;
|
namespace WatchIt.Website.Services.WebAPI.Media;
|
||||||
|
|
||||||
@@ -12,9 +13,9 @@ public interface IMediaWebAPIService
|
|||||||
Task PostMediaGenre(long mediaId, long genreId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null);
|
Task PostMediaGenre(long mediaId, long genreId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null);
|
||||||
Task DeleteMediaGenre(long mediaId, long genreId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null);
|
Task DeleteMediaGenre(long mediaId, long genreId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null);
|
||||||
|
|
||||||
Task GetMediaRating(long mediaId, Action<MediaRatingResponse>? successAction = null, Action? notFoundAction = null);
|
Task GetMediaRating(long mediaId, Action<RatingResponse>? successAction = null, Action? notFoundAction = null);
|
||||||
Task GetMediaRatingByUser(long mediaId, long userId, Action<short>? successAction = null, Action? notFoundAction = null);
|
Task GetMediaRatingByUser(long mediaId, long userId, Action<short>? successAction = null, Action? notFoundAction = null);
|
||||||
Task PutMediaRating(long mediaId, MediaRatingRequest body, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null);
|
Task PutMediaRating(long mediaId, RatingRequest body, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null);
|
||||||
Task DeleteMediaRating(long mediaId, Action? successAction = null, Action? unauthorizedAction = null);
|
Task DeleteMediaRating(long mediaId, Action? successAction = null, Action? unauthorizedAction = null);
|
||||||
|
|
||||||
Task PostMediaView(long mediaId, Action? successAction = null, Action? notFoundAction = null);
|
Task PostMediaView(long mediaId, Action? successAction = null, Action? notFoundAction = null);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using WatchIt.Common.Model.Genres;
|
using WatchIt.Common.Model.Genres;
|
||||||
using WatchIt.Common.Model.Media;
|
using WatchIt.Common.Model.Media;
|
||||||
using WatchIt.Common.Model.Photos;
|
using WatchIt.Common.Model.Photos;
|
||||||
|
using WatchIt.Common.Model.Rating;
|
||||||
using WatchIt.Common.Services.HttpClient;
|
using WatchIt.Common.Services.HttpClient;
|
||||||
using WatchIt.Website.Services.Utility.Configuration;
|
using WatchIt.Website.Services.Utility.Configuration;
|
||||||
using WatchIt.Website.Services.Utility.Configuration.Model;
|
using WatchIt.Website.Services.Utility.Configuration.Model;
|
||||||
@@ -93,7 +94,7 @@ public class MediaWebAPIService : BaseWebAPIService, IMediaWebAPIService
|
|||||||
|
|
||||||
#region Rating
|
#region Rating
|
||||||
|
|
||||||
public async Task GetMediaRating(long mediaId, Action<MediaRatingResponse>? successAction = null, Action? notFoundAction = null)
|
public async Task GetMediaRating(long mediaId, Action<RatingResponse>? successAction = null, Action? notFoundAction = null)
|
||||||
{
|
{
|
||||||
string url = GetUrl(EndpointsConfiguration.Media.GetMediaRating, mediaId);
|
string url = GetUrl(EndpointsConfiguration.Media.GetMediaRating, mediaId);
|
||||||
|
|
||||||
@@ -117,7 +118,7 @@ public class MediaWebAPIService : BaseWebAPIService, IMediaWebAPIService
|
|||||||
.ExecuteAction();
|
.ExecuteAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PutMediaRating(long mediaId, MediaRatingRequest body, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null)
|
public async Task PutMediaRating(long mediaId, RatingRequest body, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null)
|
||||||
{
|
{
|
||||||
string url = GetUrl(EndpointsConfiguration.Media.PutMediaRating, mediaId);
|
string url = GetUrl(EndpointsConfiguration.Media.PutMediaRating, mediaId);
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,18 @@
|
|||||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||||
|
|
||||||
<!-- CSS -->
|
<!-- CSS -->
|
||||||
<link rel="stylesheet" href="app.css?version=0.2.0.2"/>
|
<link rel="stylesheet" href="css/general.css?version=0.2.0.3"/>
|
||||||
<link rel="stylesheet" href="WatchIt.Website.styles.css?version=0.2.0.9"/>
|
<link rel="stylesheet" href="css/main_button.css?version=0.2.0.0"/>
|
||||||
|
<link rel="stylesheet" href="WatchIt.Website.styles.css?version=0.2.0.11"/>
|
||||||
|
|
||||||
<!-- BOOTSTRAP -->
|
<!-- BOOTSTRAP -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<!-- BLAZORISE -->
|
||||||
|
<link href="_content/Blazorise.Icons.FontAwesome/v6/css/all.min.css" rel="stylesheet">
|
||||||
|
<link href="_content/Blazorise/blazorise.css" rel="stylesheet" />
|
||||||
|
<link href="_content/Blazorise.Bootstrap5/blazorise.bootstrap5.css" rel="stylesheet" />
|
||||||
|
|
||||||
<!-- FONTS -->
|
<!-- FONTS -->
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
|||||||
@@ -1,32 +1,28 @@
|
|||||||
<div class="row">
|
<div class="rounded-3 panel panel-regular p-4">
|
||||||
<div class="col">
|
<div class="container-grid">
|
||||||
<div class="rounded-3 panel panel-regular p-4">
|
<div class="row">
|
||||||
<div class="container-fluid">
|
<div class="col">
|
||||||
<div class="row">
|
<div class="d-flex justify-content-center">
|
||||||
<div class="col">
|
<div class="text-danger icon-size">⚠︎</div>
|
||||||
<div class="d-flex justify-content-center">
|
|
||||||
<div class="text-danger icon-size">⚠︎</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<div class="d-flex justify-content-center">
|
|
||||||
<h3 class="text-danger">An error occured while loading a page</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@if (!string.IsNullOrWhiteSpace(ErrorMessage))
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<div class="d-flex justify-content-center">
|
|
||||||
<p>@ErrorMessage</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<h3 class="text-danger">An error occured while loading a page</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (!string.IsNullOrWhiteSpace(ErrorMessage))
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<p>@ErrorMessage</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<div class="container-grid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto">
|
||||||
|
<img id="picture" class="rounded-2 shadow object-fit-cover picture-aspect-ratio" src="@(_picture is not null ? _picture.ToString() : "assets/poster.png")" alt="picture" height="@(PictureHeight)"/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex align-items-start flex-column h-100">
|
||||||
|
<div class="mb-auto">
|
||||||
|
<span id="nameText">
|
||||||
|
<strong>@(Name)</strong>@(string.IsNullOrWhiteSpace(AdditionalNameInfo) ? string.Empty : AdditionalNameInfo)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-inline-flex gap-2">
|
||||||
|
<span id="ratingStar">★</span>
|
||||||
|
<div class="d-inline-flex flex-column justify-content-center">
|
||||||
|
<span id="ratingValue">@(_rating is not null && _rating.RatingCount > 0 ? _rating.RatingAverage : "--")/10</span>
|
||||||
|
@if (_rating is not null && _rating.RatingCount > 0)
|
||||||
|
{
|
||||||
|
<span id="ratingCount">@(_rating.RatingCount)</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using WatchIt.Common.Model;
|
||||||
|
using WatchIt.Common.Model.Rating;
|
||||||
|
|
||||||
|
namespace WatchIt.Website.Components;
|
||||||
|
|
||||||
|
public partial class ListItemComponent : ComponentBase
|
||||||
|
{
|
||||||
|
#region PARAMETERS
|
||||||
|
|
||||||
|
[Parameter] public required long Id { get; set; }
|
||||||
|
[Parameter] public required string Name { get; set; }
|
||||||
|
[Parameter] public string? AdditionalNameInfo { get; set; }
|
||||||
|
[Parameter] public required Func<long, Action<Picture>, Task> PictureDownloadingTask { get; set; }
|
||||||
|
[Parameter] public required Func<long, Action<RatingResponse>, Task> RatingDownloadingTask { get; set; }
|
||||||
|
[Parameter] public int PictureHeight { get; set; } = 150;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region FIELDS
|
||||||
|
|
||||||
|
private bool _loaded;
|
||||||
|
|
||||||
|
private Picture? _picture;
|
||||||
|
private RatingResponse? _rating;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PRIVATE METHODS
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
List<Task> endTasks = new List<Task>();
|
||||||
|
|
||||||
|
// STEP 0
|
||||||
|
endTasks.AddRange(
|
||||||
|
[
|
||||||
|
PictureDownloadingTask(Id, picture => _picture = picture),
|
||||||
|
RatingDownloadingTask(Id, rating => _rating = rating)
|
||||||
|
]);
|
||||||
|
|
||||||
|
await Task.WhenAll(endTasks);
|
||||||
|
|
||||||
|
_loaded = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/* IDS */
|
||||||
|
|
||||||
|
#nameText {
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ratingStar {
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ratingValue {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ratingCount {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
<div class="row">
|
<div class="d-flex flex-column">
|
||||||
<div class="col">
|
<div class="d-flex justify-content-center">
|
||||||
<div class="d-flex flex-column m-5">
|
<div id="spinner" class="spinner-border text-@(Color)"></div>
|
||||||
<div class="d-flex justify-content-center">
|
</div>
|
||||||
<div id="spinner" class="spinner-border text-dark"></div>
|
<div class="d-flex justify-content-center">
|
||||||
</div>
|
<p id="text" class="text-@(Color)" m-0>Loading...</p>
|
||||||
<div class="d-flex justify-content-center">
|
|
||||||
<p id="text" class="text-dark">Loading...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace WatchIt.Website.Components;
|
||||||
|
|
||||||
|
public partial class LoadingComponent : ComponentBase
|
||||||
|
{
|
||||||
|
#region PARAMETERS
|
||||||
|
|
||||||
|
[Parameter] public string Color { get; set; } = "dark";
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
@using Microsoft.IdentityModel.Tokens
|
||||||
|
|
||||||
|
@typeparam TItem
|
||||||
|
@typeparam TQuery where TQuery : WatchIt.Common.Query.QueryParameters
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="rounded-3 panel panel-regular p-4">
|
||||||
|
<div class="container-grid">
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col">
|
||||||
|
<h4 class="m-0"><strong>@(Title)</strong></h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (_loaded)
|
||||||
|
{
|
||||||
|
if (!_items.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _items.Count; i++)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<a class="text-reset text-decoration-none" href="@(string.Format(UrlIdTemplate, IdSource(_items[i])))">
|
||||||
|
<ListItemComponent Id="@(IdSource(_items[i]))"
|
||||||
|
Name="@(NameSource(_items[i]))"
|
||||||
|
AdditionalNameInfo="@(AdditionalNameInfoSource(_items[i]))"
|
||||||
|
PictureDownloadingTask="@(PictureDownloadingTask)"
|
||||||
|
RatingDownloadingTask="@(RatingDownloadingTask)"/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
if (!_allItemsLoaded)
|
||||||
|
{
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<button class="btn btn-secondary" @onclick="DownloadItems">
|
||||||
|
@if (!_itemsLoading)
|
||||||
|
{
|
||||||
|
<span>Load more</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
<span>Saving...</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
No items found
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<LoadingComponent Color="light"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using WatchIt.Common.Model;
|
||||||
|
using WatchIt.Common.Model.Rating;
|
||||||
|
using WatchIt.Common.Query;
|
||||||
|
|
||||||
|
namespace WatchIt.Website.Components.SearchPage;
|
||||||
|
|
||||||
|
public partial class SearchResultComponent<TItem, TQuery> : ComponentBase where TQuery : QueryParameters
|
||||||
|
{
|
||||||
|
#region PARAMETERS
|
||||||
|
|
||||||
|
[Parameter] public required string Title { get; set; }
|
||||||
|
[Parameter] public required TQuery Query { get; set; }
|
||||||
|
[Parameter] public required Func<TItem, long> IdSource { get; set; }
|
||||||
|
[Parameter] public required Func<TItem, string> NameSource { get; set; }
|
||||||
|
[Parameter] public Func<TItem, string?> AdditionalNameInfoSource { get; set; } = _ => null;
|
||||||
|
[Parameter] public required string UrlIdTemplate { get; set; }
|
||||||
|
[Parameter] public required Func<TQuery, Action<IEnumerable<TItem>>, Task> ItemDownloadingTask { get; set; }
|
||||||
|
[Parameter] public required Func<long, Action<Picture>, Task> PictureDownloadingTask { get; set; }
|
||||||
|
[Parameter] public required Func<long, Action<RatingResponse>, Task> RatingDownloadingTask { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region FIELDS
|
||||||
|
|
||||||
|
private bool _loaded;
|
||||||
|
|
||||||
|
private List<TItem> _items = [];
|
||||||
|
private bool _allItemsLoaded;
|
||||||
|
private bool _itemsLoading;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PRIVATE METHODS
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
// INIT
|
||||||
|
Query.First = 5;
|
||||||
|
|
||||||
|
List<Task> endTasks = new List<Task>();
|
||||||
|
|
||||||
|
// STEP 0
|
||||||
|
endTasks.AddRange(
|
||||||
|
[
|
||||||
|
ItemDownloadingTask(Query, data =>
|
||||||
|
{
|
||||||
|
_items.AddRange(data);
|
||||||
|
if (data.Count() < 5)
|
||||||
|
{
|
||||||
|
_allItemsLoaded = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Query.After = 5;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
// END
|
||||||
|
await Task.WhenAll(endTasks);
|
||||||
|
|
||||||
|
_loaded = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DownloadItems()
|
||||||
|
{
|
||||||
|
_itemsLoading = true;
|
||||||
|
await ItemDownloadingTask(Query, data =>
|
||||||
|
{
|
||||||
|
_items.AddRange(data);
|
||||||
|
if (data.Count() < 5)
|
||||||
|
{
|
||||||
|
_allItemsLoaded = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Query.After += 5;
|
||||||
|
}
|
||||||
|
_itemsLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -1,178 +1,97 @@
|
|||||||
@using WatchIt.Common.Model.Photos
|
@using WatchIt.Common.Model.Photos
|
||||||
@using WatchIt.Website.Services.WebAPI.Photos
|
@using WatchIt.Website.Services.WebAPI.Photos
|
||||||
|
|
||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
@if (_loaded)
|
|
||||||
{
|
|
||||||
<div class="container-xl">
|
<CascadingValue Value="this">
|
||||||
<div class="row align-items-center m-1 my-2 mb-3 rounded-3 header panel panel-header z-3">
|
@if (_loaded)
|
||||||
<div class="col-2">
|
{
|
||||||
<a class="logo" href="/">
|
<div class="container-xl">
|
||||||
WatchIt
|
<div class="row sticky-top top-3 mb-2rem">
|
||||||
</a>
|
<div class="col">
|
||||||
</div>
|
<div class="panel panel-header rounded-3 px-3">
|
||||||
<div class="col">
|
<div class="container-grid">
|
||||||
<p>Menu</p>
|
<div class="row align-items-center">
|
||||||
</div>
|
<div class="col">
|
||||||
<div class="col-auto">
|
<a id="logo" class="logo" href="/">
|
||||||
<div class="d-flex flex-row-reverse">
|
WatchIt
|
||||||
@if (_user is null)
|
</a>
|
||||||
{
|
</div>
|
||||||
<a class="main-button" href="/auth">Sign in</a>
|
<div class="col-auto">
|
||||||
}
|
<div class="d-flex gap-2 align-items-center">
|
||||||
else
|
@if (_searchbarVisible)
|
||||||
{
|
{
|
||||||
<div class="dropdown z-3">
|
<div class="input-group input-group-sm">
|
||||||
<a class="dropdown-toggle align-items-center text-decoration-none d-flex" id="dropdownUser" aria-expanded="false" @onclick="() => _userMenuIsActive = !_userMenuIsActive">
|
<InputText class="form-control" placeholder="Search with regex" @bind-Value="@(_searchbarText)"/>
|
||||||
<img class="rounded-circle" alt="avatar" height="30" src="@(_userProfilePicture)"/>
|
</div>
|
||||||
<div class="text-decoration-none mx-2 text-white">@(_user.Username)</div>
|
<button type="button" class="btn btn-sm btn-outline-secondary" @onclick="@(SearchStart)">⌕</button>
|
||||||
</a>
|
<a id="searchbarCancel" @onclick="@(() => _searchbarVisible = false)">
|
||||||
<ul class="dropdown-menu dropdown-menu-right text-small z-3" id="user-menu" aria-labelledby="dropdownUser">
|
<img src="assets/icons/cancel.png" alt="cancel_icon" height="20" width="20"/>
|
||||||
<li>
|
</a>
|
||||||
@if (_user.IsAdmin)
|
}
|
||||||
{
|
else
|
||||||
<a class="dropdown-item" href="/admin">Administrator panel</a>
|
{
|
||||||
}
|
<button type="button" class="btn btn-sm">Rankings</button>
|
||||||
<div class="dropdown-menu-separator"></div>
|
<button type="button" class="btn btn-sm" @onclick="@(() => _searchbarVisible = true)">⌕</button>
|
||||||
<a class="dropdown-item text-danger" @onclick="UserMenuLogOut">Log out</a>
|
}
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="float-end">
|
||||||
|
@if (_user is null)
|
||||||
|
{
|
||||||
|
<a id="signInButton" class="main-button" href="/auth">Sign in</a>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<Dropdown RightAligned>
|
||||||
|
<Button Color="Color.Default">
|
||||||
|
<div class="d-flex gap-2 align-items-center">
|
||||||
|
<img class="rounded-circle" alt="avatar" height="30" src="@(_userProfilePicture is null ? "assets/user_placeholder.png" : _userProfilePicture.ToString())"/>
|
||||||
|
<span>@(_user.Username)</span>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
<DropdownToggle Color="Color.Default" Split />
|
||||||
|
<DropdownMenu >
|
||||||
|
@if (_user.IsAdmin)
|
||||||
|
{
|
||||||
|
<DropdownItem Clicked="@(() => NavigationManager.NavigateTo("/admin"))">Administrator panel</DropdownItem>
|
||||||
|
}
|
||||||
|
<DropdownDivider/>
|
||||||
|
<DropdownItem Clicked="UserMenuLogOut"><span class="text-danger">Log out</span></DropdownItem>
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
@Body
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
|
||||||
<div class="col z-0 p-1">
|
|
||||||
@Body
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
html {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-image: url('@_background');
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
background-attachment: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo, .main-button {
|
|
||||||
background-image: linear-gradient(45deg, @_firstGradientColor, @_secondGradientColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
#user-menu {
|
|
||||||
display: @(_userMenuIsActive ? "block" : "none");
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* TAGS */
|
||||||
|
body {
|
||||||
@code
|
background-image: url('@(GetBackgroundPhoto() is null ? "assets/background_temp.jpg": GetBackgroundPhoto().ToString())');
|
||||||
{
|
|
||||||
#region SERVICES
|
|
||||||
|
|
||||||
[Inject] public ILogger<MainLayout> Logger { get; set; } = default!;
|
|
||||||
[Inject] public NavigationManager NavigationManager { get; set; } = default!;
|
|
||||||
[Inject] public ITokensService TokensService { get; set; } = default!;
|
|
||||||
[Inject] public IAuthenticationService AuthenticationService { get; set; } = default!;
|
|
||||||
[Inject] public IAccountsWebAPIService AccountsWebAPIService { get; set; } = default!;
|
|
||||||
[Inject] public IMediaWebAPIService MediaWebAPIService { get; set; } = default!;
|
|
||||||
[Inject] public IPhotosWebAPIService PhotosWebAPIService { get; set; } = default!;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region FIELDS
|
|
||||||
|
|
||||||
private bool _loaded = false;
|
|
||||||
|
|
||||||
private string _background = "assets/background_temp.jpg";
|
|
||||||
private string _firstGradientColor = "#c6721c";
|
|
||||||
private string _secondGradientColor = "#85200c";
|
|
||||||
|
|
||||||
private User? _user = null;
|
|
||||||
private string _userProfilePicture = "assets/user_placeholder.png";
|
|
||||||
private bool _userMenuIsActive = false;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region METHODS
|
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
||||||
{
|
|
||||||
if (firstRender)
|
|
||||||
{
|
|
||||||
List<Task> bgTasks = new List<Task>();
|
|
||||||
|
|
||||||
bgTasks.Add(GetBackground());
|
|
||||||
|
|
||||||
await GetAuthenticatedUser();
|
|
||||||
|
|
||||||
if (_user is not null)
|
|
||||||
{
|
|
||||||
bgTasks.Add(GetProfilePicture());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.WhenAll(bgTasks);
|
|
||||||
|
|
||||||
_loaded = true;
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task GetBackground()
|
/* IDS */
|
||||||
{
|
|
||||||
Action<PhotoResponse> backgroundSuccess = (data) =>
|
|
||||||
{
|
|
||||||
string imageBase64 = Convert.ToBase64String(data.Image);
|
|
||||||
string firstColor = BitConverter.ToString(data.Background.FirstGradientColor)
|
|
||||||
.Replace("-", string.Empty);
|
|
||||||
string secondColor = BitConverter.ToString(data.Background.SecondGradientColor)
|
|
||||||
.Replace("-", string.Empty);
|
|
||||||
|
|
||||||
_background = $"data:{data.MimeType};base64,{imageBase64}";
|
|
||||||
_firstGradientColor = $"#{firstColor}";
|
|
||||||
_secondGradientColor = $"#{secondColor}";
|
|
||||||
};
|
|
||||||
await PhotosWebAPIService.GetPhotoRandomBackground(backgroundSuccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task GetAuthenticatedUser()
|
#logo, #signInButton {
|
||||||
{
|
background-image: linear-gradient(45deg, @(GetBackgroundPhoto() is null ? "#c6721c, #85200c" : $"#{Convert.ToHexString(GetBackgroundPhoto().Background.FirstGradientColor)}, #{Convert.ToHexString(GetBackgroundPhoto().Background.SecondGradientColor)}"));
|
||||||
_user = await AuthenticationService.GetUserAsync();
|
}
|
||||||
|
</style>
|
||||||
}
|
}
|
||||||
|
</CascadingValue>
|
||||||
private async Task GetProfilePicture()
|
|
||||||
{
|
|
||||||
Action<AccountProfilePictureResponse> successAction = (data) =>
|
|
||||||
{
|
|
||||||
string imageBase64 = Convert.ToBase64String(data.Image);
|
|
||||||
_userProfilePicture = $"data:{data.MimeType};base64,{imageBase64}";
|
|
||||||
};
|
|
||||||
await AccountsWebAPIService.GetAccountProfilePicture(_user.Id, successAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UserMenuLogOut()
|
|
||||||
{
|
|
||||||
await AuthenticationService.LogoutAsync();
|
|
||||||
await TokensService.RemoveAuthenticationData();
|
|
||||||
NavigationManager.Refresh(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
140
WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs
Normal file
140
WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using WatchIt.Common.Model.Accounts;
|
||||||
|
using WatchIt.Common.Model.Photos;
|
||||||
|
using WatchIt.Website.Services.Utility.Authentication;
|
||||||
|
using WatchIt.Website.Services.Utility.Tokens;
|
||||||
|
using WatchIt.Website.Services.WebAPI.Accounts;
|
||||||
|
using WatchIt.Website.Services.WebAPI.Media;
|
||||||
|
using WatchIt.Website.Services.WebAPI.Photos;
|
||||||
|
|
||||||
|
namespace WatchIt.Website.Layout;
|
||||||
|
|
||||||
|
public partial class MainLayout : LayoutComponentBase
|
||||||
|
{
|
||||||
|
#region SERVICES
|
||||||
|
|
||||||
|
[Inject] public ILogger<MainLayout> Logger { get; set; } = default!;
|
||||||
|
[Inject] public NavigationManager NavigationManager { get; set; } = default!;
|
||||||
|
[Inject] public ITokensService TokensService { get; set; } = default!;
|
||||||
|
[Inject] public IAuthenticationService AuthenticationService { get; set; } = default!;
|
||||||
|
[Inject] public IAccountsWebAPIService AccountsWebAPIService { get; set; } = default!;
|
||||||
|
[Inject] public IMediaWebAPIService MediaWebAPIService { get; set; } = default!;
|
||||||
|
[Inject] public IPhotosWebAPIService PhotosWebAPIService { get; set; } = default!;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region FIELDS
|
||||||
|
|
||||||
|
private bool _loaded;
|
||||||
|
|
||||||
|
private User? _user;
|
||||||
|
private PhotoResponse? _defaultBackgroundPhoto;
|
||||||
|
private AccountProfilePictureResponse? _userProfilePicture;
|
||||||
|
|
||||||
|
private bool _searchbarVisible;
|
||||||
|
private string _searchbarText = string.Empty;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PROPERTIES
|
||||||
|
|
||||||
|
private PhotoResponse? _backgroundPhoto;
|
||||||
|
public PhotoResponse? BackgroundPhoto
|
||||||
|
{
|
||||||
|
get => _backgroundPhoto;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_backgroundPhoto = value;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PRIVATE METHODS
|
||||||
|
|
||||||
|
#region Main
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
List<Task> endTasks = new List<Task>();
|
||||||
|
List<Task> step1Tasks = new List<Task>();
|
||||||
|
|
||||||
|
// STEP 0
|
||||||
|
step1Tasks.AddRange(
|
||||||
|
[
|
||||||
|
Task.Run(async () => _user = await AuthenticationService.GetUserAsync())
|
||||||
|
]);
|
||||||
|
endTasks.AddRange(
|
||||||
|
[
|
||||||
|
PhotosWebAPIService.GetPhotoRandomBackground(data => _defaultBackgroundPhoto = data)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// STEP 1
|
||||||
|
await Task.WhenAll(step1Tasks);
|
||||||
|
if (_user is not null)
|
||||||
|
{
|
||||||
|
endTasks.AddRange(
|
||||||
|
[
|
||||||
|
AccountsWebAPIService.GetAccountProfilePicture(_user.Id, data => _userProfilePicture = data)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// END
|
||||||
|
await Task.WhenAll(endTasks);
|
||||||
|
|
||||||
|
_loaded = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PhotoResponse? GetBackgroundPhoto()
|
||||||
|
{
|
||||||
|
if (BackgroundPhoto?.Background is not null)
|
||||||
|
{
|
||||||
|
return BackgroundPhoto;
|
||||||
|
}
|
||||||
|
else if (_defaultBackgroundPhoto?.Background is not null)
|
||||||
|
{
|
||||||
|
return _defaultBackgroundPhoto;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Search
|
||||||
|
|
||||||
|
private void SearchStart()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(_searchbarText))
|
||||||
|
{
|
||||||
|
string query = WebUtility.UrlEncode(_searchbarText);
|
||||||
|
NavigationManager.NavigateTo($"/search/{query}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region User menu
|
||||||
|
|
||||||
|
private async Task UserMenuLogOut()
|
||||||
|
{
|
||||||
|
await AuthenticationService.LogoutAsync();
|
||||||
|
await TokensService.RemoveAuthenticationData();
|
||||||
|
NavigationManager.Refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -1,21 +1,18 @@
|
|||||||
body {
|
/* TAGS */
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
font-size: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
position: sticky;
|
|
||||||
top: 10px;
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.body-content {
|
|
||||||
padding-top: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* IDS */
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchbarCancel {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #6c757d;
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PageTitle>WatchIt administrator panel</PageTitle>
|
<PageTitle>WatchIt administrator panel</PageTitle>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-grid">
|
||||||
@if (_loaded)
|
@if (_loaded)
|
||||||
{
|
{
|
||||||
if (_authenticated)
|
if (_authenticated)
|
||||||
@@ -26,11 +26,21 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ErrorComponent ErrorMessage="You do not have permission to view this site"/>
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<ErrorComponent ErrorMessage="You do not have permission to view this site"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<LoadingComponent/>
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="m-5">
|
||||||
|
<LoadingComponent/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -1,15 +1,24 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using WatchIt.Website.Layout;
|
||||||
using WatchIt.Website.Services.Utility.Authentication;
|
using WatchIt.Website.Services.Utility.Authentication;
|
||||||
|
|
||||||
namespace WatchIt.Website.Pages;
|
namespace WatchIt.Website.Pages;
|
||||||
|
|
||||||
public partial class AdminPage
|
public partial class AdminPage
|
||||||
{
|
{
|
||||||
#region SERVICE
|
#region SERVICES
|
||||||
|
|
||||||
[Inject] public IAuthenticationService AuthenticationService { get; set; } = default!;
|
[Inject] public IAuthenticationService AuthenticationService { get; set; } = default!;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PARAMETERS
|
||||||
|
|
||||||
|
[CascadingParameter] public MainLayout Layout { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -28,6 +37,8 @@ public partial class AdminPage
|
|||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
|
Layout.BackgroundPhoto = null;
|
||||||
|
|
||||||
User? user = await AuthenticationService.GetUserAsync();
|
User? user = await AuthenticationService.GetUserAsync();
|
||||||
if (user is not null && user.IsAdmin)
|
if (user is not null && user.IsAdmin)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
@using WatchIt.Common.Model.Movies
|
|
||||||
|
|
||||||
<PageTitle>WatchIt</PageTitle>
|
<PageTitle>WatchIt</PageTitle>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-grid">
|
||||||
@if (_loaded)
|
@if (_loaded)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(_error))
|
if (string.IsNullOrWhiteSpace(_error))
|
||||||
@@ -11,7 +10,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="rounded-3 panel panel-regular p-4">
|
<div class="rounded-3 panel panel-regular p-4">
|
||||||
<div class="container-fluid p-0">
|
<div class="container-grid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h4><strong>Top 5 movies this week by popularity</strong></h4>
|
<h4><strong>Top 5 movies this week by popularity</strong></h4>
|
||||||
@@ -26,7 +25,7 @@
|
|||||||
<a class="text-reset text-decoration-none" href="/media/@_topMovies.ToArray()[i].Key.Id">
|
<a class="text-reset text-decoration-none" href="/media/@_topMovies.ToArray()[i].Key.Id">
|
||||||
<div class="d-flex flex-column align-items-center gap-2 h-100">
|
<div class="d-flex flex-column align-items-center gap-2 h-100">
|
||||||
<img class="rounded-2 shadow object-fit-cover poster-aspect-ratio" src="@(_topMovies.ToArray()[i].Value is not null ? _topMovies.ToArray()[i].Value.ToString() : "assets/poster.png")" alt="poster" width="100%"/>
|
<img class="rounded-2 shadow object-fit-cover poster-aspect-ratio" src="@(_topMovies.ToArray()[i].Value is not null ? _topMovies.ToArray()[i].Value.ToString() : "assets/poster.png")" alt="poster" width="100%"/>
|
||||||
<div class="container-fluid p-0">
|
<div class="container-grid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="text-center border border-2 border-light rounded-circle place-circle"><strong>@(i + 1)</strong></div>
|
<div class="text-center border border-2 border-light rounded-circle place-circle"><strong>@(i + 1)</strong></div>
|
||||||
@@ -49,7 +48,7 @@
|
|||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="rounded-3 panel panel-regular p-4">
|
<div class="rounded-3 panel panel-regular p-4">
|
||||||
<div class="container-fluid p-0">
|
<div class="container-grid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h4><strong>Top 5 TV series this week by popularity</strong></h4>
|
<h4><strong>Top 5 TV series this week by popularity</strong></h4>
|
||||||
@@ -64,7 +63,7 @@
|
|||||||
<a class="text-reset text-decoration-none" href="/media/@_topSeries.ToArray()[i].Key.Id">
|
<a class="text-reset text-decoration-none" href="/media/@_topSeries.ToArray()[i].Key.Id">
|
||||||
<div class="d-flex flex-column align-items-center gap-2 h-100">
|
<div class="d-flex flex-column align-items-center gap-2 h-100">
|
||||||
<img class="rounded-2 shadow object-fit-cover poster-aspect-ratio" src="@(_topSeries.ToArray()[i].Value is not null ? _topSeries.ToArray()[i].Value.ToString() : "assets/poster.png")" alt="poster" width="100%"/>
|
<img class="rounded-2 shadow object-fit-cover poster-aspect-ratio" src="@(_topSeries.ToArray()[i].Value is not null ? _topSeries.ToArray()[i].Value.ToString() : "assets/poster.png")" alt="poster" width="100%"/>
|
||||||
<div class="container-fluid p-0">
|
<div class="container-grid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="text-center border border-2 border-light rounded-circle place-circle"><strong>@(i + 1)</strong></div>
|
<div class="text-center border border-2 border-light rounded-circle place-circle"><strong>@(i + 1)</strong></div>
|
||||||
@@ -87,11 +86,21 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ErrorComponent ErrorMessage="@_error"/>
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<ErrorComponent ErrorMessage="@_error"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<LoadingComponent/>
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="m-5">
|
||||||
|
<LoadingComponent/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using WatchIt.Common.Model.Media;
|
using WatchIt.Common.Model.Media;
|
||||||
using WatchIt.Common.Model.Movies;
|
using WatchIt.Common.Model.Movies;
|
||||||
using WatchIt.Common.Model.Series;
|
using WatchIt.Common.Model.Series;
|
||||||
|
using WatchIt.Website.Layout;
|
||||||
using WatchIt.Website.Services.WebAPI.Media;
|
using WatchIt.Website.Services.WebAPI.Media;
|
||||||
using WatchIt.Website.Services.WebAPI.Movies;
|
using WatchIt.Website.Services.WebAPI.Movies;
|
||||||
using WatchIt.Website.Services.WebAPI.Series;
|
using WatchIt.Website.Services.WebAPI.Series;
|
||||||
@@ -21,6 +22,14 @@ public partial class HomePage
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PARAMETERS
|
||||||
|
|
||||||
|
[CascadingParameter] public MainLayout Layout { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region FIELDS
|
#region FIELDS
|
||||||
|
|
||||||
private bool _loaded;
|
private bool _loaded;
|
||||||
@@ -39,6 +48,8 @@ public partial class HomePage
|
|||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
|
Layout.BackgroundPhoto = null;
|
||||||
|
|
||||||
List<Task> step1Tasks = new List<Task>();
|
List<Task> step1Tasks = new List<Task>();
|
||||||
List<Task> endTasks = new List<Task>();
|
List<Task> endTasks = new List<Task>();
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
}
|
}
|
||||||
</PageTitle>
|
</PageTitle>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-grid">
|
||||||
@if (_loaded)
|
@if (_loaded)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(_error))
|
if (string.IsNullOrWhiteSpace(_error))
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
<div class="row mt-3 gx-3">
|
<div class="row mt-3 gx-3">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="rounded-3 panel panel-regular p-4 h-100">
|
<div class="rounded-3 panel panel-regular p-4 h-100">
|
||||||
<div class="container-fluid p-0">
|
<div class="container-grid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<img class="rounded-2 shadow object-fit-cover" src="@(_mediaPosterRequest is not null ? _mediaPosterRequest.ToString() : "assets/poster.png")" alt="poster" width="300" height="500"/>
|
<img class="rounded-2 shadow object-fit-cover" src="@(_mediaPosterRequest is not null ? _mediaPosterRequest.ToString() : "assets/poster.png")" alt="poster" width="300" height="500"/>
|
||||||
@@ -77,12 +77,12 @@
|
|||||||
<button type="button" class="btn btn-secondary btn-block btn-stretch-x" @onclick="SavePoster" disabled=@(!Id.HasValue || _mediaPosterSaving || _mediaPosterDeleting) autocomplete="off">
|
<button type="button" class="btn btn-secondary btn-block btn-stretch-x" @onclick="SavePoster" disabled=@(!Id.HasValue || _mediaPosterSaving || _mediaPosterDeleting) autocomplete="off">
|
||||||
@if (!_mediaPosterSaving)
|
@if (!_mediaPosterSaving)
|
||||||
{
|
{
|
||||||
<span class="sr-only">Save poster</span>
|
<span>Save poster</span>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
<span class="sr-only">Saving...</span>
|
<span>Saving...</span>
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -96,12 +96,12 @@
|
|||||||
<button type="button" class="btn btn-danger btn-block btn-stretch-x" @onclick="DeletePoster" disabled=@(!Id.HasValue || _mediaPosterSaving || _mediaPosterDeleting) autocomplete="off">
|
<button type="button" class="btn btn-danger btn-block btn-stretch-x" @onclick="DeletePoster" disabled=@(!Id.HasValue || _mediaPosterSaving || _mediaPosterDeleting) autocomplete="off">
|
||||||
@if (!_mediaPosterSaving)
|
@if (!_mediaPosterSaving)
|
||||||
{
|
{
|
||||||
<span class="sr-only">Delete poster</span>
|
<span>Delete poster</span>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
<span class="sr-only">Deleting...</span>
|
<span>Deleting...</span>
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
<div class="rounded-3 panel panel-regular p-4 h-100">
|
<div class="rounded-3 panel panel-regular p-4 h-100">
|
||||||
<EditForm Model="_mediaRequest">
|
<EditForm Model="_mediaRequest">
|
||||||
<AntiforgeryToken/>
|
<AntiforgeryToken/>
|
||||||
<div class="container-fluid p-0">
|
<div class="container-grid">
|
||||||
<div class="row form-group mb-1">
|
<div class="row form-group mb-1">
|
||||||
<label for="title" class="col-2 col-form-label">Title*</label>
|
<label for="title" class="col-2 col-form-label">Title*</label>
|
||||||
<div class="col-10">
|
<div class="col-10">
|
||||||
@@ -183,12 +183,12 @@
|
|||||||
<button type="button" class="btn btn-secondary" @onclick="SaveBasicData">
|
<button type="button" class="btn btn-secondary" @onclick="SaveBasicData">
|
||||||
@if (!_basicDataSaving)
|
@if (!_basicDataSaving)
|
||||||
{
|
{
|
||||||
<span class="sr-only">Save</span>
|
<span>Save</span>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
<span class="sr-only">Saving...</span>
|
<span>Saving...</span>
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -202,7 +202,7 @@
|
|||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="rounded-3 panel panel-regular p-4">
|
<div class="rounded-3 panel panel-regular p-4">
|
||||||
<div class="container-fluid p-0">
|
<div class="container-grid">
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="d-flex align-items-center h-100">
|
<div class="d-flex align-items-center h-100">
|
||||||
@@ -226,12 +226,12 @@
|
|||||||
<button type="button" class="btn btn-secondary" disabled="@(_photoEditSaving)" @onclick="SaveEditPhoto">
|
<button type="button" class="btn btn-secondary" disabled="@(_photoEditSaving)" @onclick="SaveEditPhoto">
|
||||||
@if (!_photoEditSaving)
|
@if (!_photoEditSaving)
|
||||||
{
|
{
|
||||||
<span class="sr-only">Save</span>
|
<span>Save</span>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
<span class="sr-only">Saving...</span>
|
<span>Saving...</span>
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-secondary" disabled="@(_photoEditSaving)" @onclick="CancelEditPhoto">Cancel</button>
|
<button type="button" class="btn btn-secondary" disabled="@(_photoEditSaving)" @onclick="CancelEditPhoto">Cancel</button>
|
||||||
@@ -248,7 +248,7 @@
|
|||||||
<div id="scrollPhotos" class="d-flex p-3 gap-3" data-bs-spy="scroll" tabindex="0">
|
<div id="scrollPhotos" class="d-flex p-3 gap-3" data-bs-spy="scroll" tabindex="0">
|
||||||
@foreach (PhotoResponse photo in _photos)
|
@foreach (PhotoResponse photo in _photos)
|
||||||
{
|
{
|
||||||
<div class="container-fluid p-0 m-0 photo-container">
|
<div class="container-grid photo-container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<img class="rounded-1 shadow object-fit-cover photo-default-aspect-ratio" src="@(photo.ToString())" alt="photo" width="350"/>
|
<img class="rounded-1 shadow object-fit-cover photo-default-aspect-ratio" src="@(photo.ToString())" alt="photo" width="350"/>
|
||||||
@@ -301,10 +301,10 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="container-fluid p-0 m-0">
|
<div class="container-grid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="container-fluid p-0 m-0">
|
<div class="container-grid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<img class="rounded-1 shadow object-fit-cover photo-default-aspect-ratio" src="@(_photoEditRequest is null ? "assets/photo.png" : _photoEditRequest.ToString())" alt="edit_photo" width="300px"/>
|
<img class="rounded-1 shadow object-fit-cover photo-default-aspect-ratio" src="@(_photoEditRequest is null ? "assets/photo.png" : _photoEditRequest.ToString())" alt="edit_photo" width="300px"/>
|
||||||
@@ -321,7 +321,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="container-fluid p-0 m-0">
|
<div class="container-grid">
|
||||||
<div class="row form-group">
|
<div class="row form-group">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
@@ -362,31 +362,30 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ErrorComponent ErrorMessage="You do not have permission to view this site"/>
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<ErrorComponent ErrorMessage="You do not have permission to view this site"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ErrorComponent ErrorMessage="@_error"/>
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<ErrorComponent ErrorMessage="@_error"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<LoadingComponent/>
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="m-5">
|
||||||
|
<LoadingComponent/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@if (_background is not null)
|
|
||||||
{
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-image: url('@(_background.ToString())') !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo, .main-button {
|
|
||||||
background-image: linear-gradient(45deg, @($"#{BitConverter.ToString(_background.Background.FirstGradientColor).Replace("-", string.Empty)}"), @($"#{BitConverter.ToString(_background.Background.SecondGradientColor).Replace("-", string.Empty)}")) !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ using WatchIt.Common.Model.Media;
|
|||||||
using WatchIt.Common.Model.Movies;
|
using WatchIt.Common.Model.Movies;
|
||||||
using WatchIt.Common.Model.Photos;
|
using WatchIt.Common.Model.Photos;
|
||||||
using WatchIt.Common.Model.Series;
|
using WatchIt.Common.Model.Series;
|
||||||
|
using WatchIt.Website.Layout;
|
||||||
using WatchIt.Website.Services.Utility.Authentication;
|
using WatchIt.Website.Services.Utility.Authentication;
|
||||||
using WatchIt.Website.Services.WebAPI.Media;
|
using WatchIt.Website.Services.WebAPI.Media;
|
||||||
using WatchIt.Website.Services.WebAPI.Movies;
|
using WatchIt.Website.Services.WebAPI.Movies;
|
||||||
@@ -33,6 +34,8 @@ public partial class MediaEditPage : ComponentBase
|
|||||||
[Parameter] public long? Id { get; set; }
|
[Parameter] public long? Id { get; set; }
|
||||||
[Parameter] public string? Type { get; set; }
|
[Parameter] public string? Type { get; set; }
|
||||||
|
|
||||||
|
[CascadingParameter] public MainLayout Layout { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
@@ -43,8 +46,6 @@ public partial class MediaEditPage : ComponentBase
|
|||||||
private string? _error;
|
private string? _error;
|
||||||
|
|
||||||
private User? _user;
|
private User? _user;
|
||||||
|
|
||||||
private PhotoResponse? _background;
|
|
||||||
|
|
||||||
private MediaResponse? _media;
|
private MediaResponse? _media;
|
||||||
private MovieRequest? _movieRequest;
|
private MovieRequest? _movieRequest;
|
||||||
@@ -86,6 +87,8 @@ public partial class MediaEditPage : ComponentBase
|
|||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
|
Layout.BackgroundPhoto = null;
|
||||||
|
|
||||||
List<Task> step1Tasks = new List<Task>();
|
List<Task> step1Tasks = new List<Task>();
|
||||||
List<Task> step2Tasks = new List<Task>();
|
List<Task> step2Tasks = new List<Task>();
|
||||||
List<Task> endTasks = new List<Task>();
|
List<Task> endTasks = new List<Task>();
|
||||||
@@ -112,7 +115,7 @@ public partial class MediaEditPage : ComponentBase
|
|||||||
{
|
{
|
||||||
endTasks.AddRange(
|
endTasks.AddRange(
|
||||||
[
|
[
|
||||||
MediaWebAPIService.GetMediaPhotoRandomBackground(Id.Value, data => _background = data),
|
MediaWebAPIService.GetMediaPhotoRandomBackground(Id.Value, data => Layout.BackgroundPhoto = data),
|
||||||
MediaWebAPIService.GetMediaPoster(Id.Value, data =>
|
MediaWebAPIService.GetMediaPoster(Id.Value, data =>
|
||||||
{
|
{
|
||||||
_mediaPosterSaved = data;
|
_mediaPosterSaved = data;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ else
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-grid">
|
||||||
@if (_loaded)
|
@if (_loaded)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(_error))
|
if (string.IsNullOrWhiteSpace(_error))
|
||||||
@@ -35,7 +35,7 @@ else
|
|||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="d-flex h-100">
|
<div class="d-flex h-100">
|
||||||
<div class="container-fluid px-0 align-self-end">
|
<div class="container-grid align-self-end">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h1 class="align-self-end title-shadow">
|
<h1 class="align-self-end title-shadow">
|
||||||
@@ -60,7 +60,7 @@ else
|
|||||||
<div class="row mt-3 gx-3">
|
<div class="row mt-3 gx-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="rounded-3 panel panel-regular p-4 h-100">
|
<div class="rounded-3 panel panel-regular p-4 h-100">
|
||||||
<div class="container-fluid px-0">
|
<div class="container-grid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="d-flex flex-wrap gap-3">
|
<div class="d-flex flex-wrap gap-3">
|
||||||
@@ -133,7 +133,7 @@ else
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="rounded-3 panel panel-yellow p-4 h-100">
|
<div class="rounded-3 panel panel-yellow p-4 h-100">
|
||||||
<div class="container-fluid px-0">
|
<div class="container-grid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h4 class="text-dark">
|
<h4 class="text-dark">
|
||||||
@@ -193,26 +193,21 @@ else
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ErrorComponent ErrorMessage="@_error"/>
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<ErrorComponent ErrorMessage="@_error"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<LoadingComponent/>
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="m-5">
|
||||||
|
<LoadingComponent/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@if (_background is not null)
|
|
||||||
{
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-image: url('@($"data:{_background.MimeType};base64,{Convert.ToBase64String(_background.Image)}")') !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo, .main-button {
|
|
||||||
background-image: linear-gradient(45deg, @($"#{BitConverter.ToString(_background.Background.FirstGradientColor).Replace("-", string.Empty)}"), @($"#{BitConverter.ToString(_background.Background.SecondGradientColor).Replace("-", string.Empty)}")) !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,9 @@ using WatchIt.Common.Model.Genres;
|
|||||||
using WatchIt.Common.Model.Media;
|
using WatchIt.Common.Model.Media;
|
||||||
using WatchIt.Common.Model.Movies;
|
using WatchIt.Common.Model.Movies;
|
||||||
using WatchIt.Common.Model.Photos;
|
using WatchIt.Common.Model.Photos;
|
||||||
|
using WatchIt.Common.Model.Rating;
|
||||||
using WatchIt.Common.Model.Series;
|
using WatchIt.Common.Model.Series;
|
||||||
|
using WatchIt.Website.Layout;
|
||||||
using WatchIt.Website.Services.Utility.Authentication;
|
using WatchIt.Website.Services.Utility.Authentication;
|
||||||
using WatchIt.Website.Services.WebAPI.Media;
|
using WatchIt.Website.Services.WebAPI.Media;
|
||||||
using WatchIt.Website.Services.WebAPI.Movies;
|
using WatchIt.Website.Services.WebAPI.Movies;
|
||||||
@@ -30,6 +32,8 @@ public partial class MediaPage : ComponentBase
|
|||||||
|
|
||||||
[Parameter] public long Id { get; set; }
|
[Parameter] public long Id { get; set; }
|
||||||
|
|
||||||
|
[CascadingParameter] public MainLayout Layout { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
@@ -43,10 +47,9 @@ public partial class MediaPage : ComponentBase
|
|||||||
|
|
||||||
private User? _user;
|
private User? _user;
|
||||||
|
|
||||||
private PhotoResponse? _background;
|
|
||||||
private MediaPosterResponse? _poster;
|
private MediaPosterResponse? _poster;
|
||||||
private IEnumerable<GenreResponse> _genres;
|
private IEnumerable<GenreResponse> _genres;
|
||||||
private MediaRatingResponse _globalRating;
|
private RatingResponse _globalRating;
|
||||||
private MovieResponse? _movie;
|
private MovieResponse? _movie;
|
||||||
private SeriesResponse? _series;
|
private SeriesResponse? _series;
|
||||||
|
|
||||||
@@ -62,6 +65,8 @@ public partial class MediaPage : ComponentBase
|
|||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
|
Layout.BackgroundPhoto = null;
|
||||||
|
|
||||||
List<Task> step1Tasks = new List<Task>();
|
List<Task> step1Tasks = new List<Task>();
|
||||||
List<Task> step2Tasks = new List<Task>();
|
List<Task> step2Tasks = new List<Task>();
|
||||||
List<Task> endTasks = new List<Task>();
|
List<Task> endTasks = new List<Task>();
|
||||||
@@ -84,7 +89,7 @@ public partial class MediaPage : ComponentBase
|
|||||||
endTasks.AddRange(
|
endTasks.AddRange(
|
||||||
[
|
[
|
||||||
MediaWebAPIService.PostMediaView(Id),
|
MediaWebAPIService.PostMediaView(Id),
|
||||||
MediaWebAPIService.GetMediaPhotoRandomBackground(Id, data => _background = data),
|
MediaWebAPIService.GetMediaPhotoRandomBackground(Id, data => Layout.BackgroundPhoto = data),
|
||||||
MediaWebAPIService.GetMediaPoster(Id, data => _poster = data),
|
MediaWebAPIService.GetMediaPoster(Id, data => _poster = data),
|
||||||
MediaWebAPIService.GetMediaGenres(Id, data => _genres = data),
|
MediaWebAPIService.GetMediaGenres(Id, data => _genres = data),
|
||||||
MediaWebAPIService.GetMediaRating(Id, data => _globalRating = data),
|
MediaWebAPIService.GetMediaRating(Id, data => _globalRating = data),
|
||||||
@@ -119,7 +124,7 @@ public partial class MediaPage : ComponentBase
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await MediaWebAPIService.PutMediaRating(Id, new MediaRatingRequest(rating));
|
await MediaWebAPIService.PutMediaRating(Id, new RatingRequest(rating));
|
||||||
_userRating = rating;
|
_userRating = rating;
|
||||||
}
|
}
|
||||||
await MediaWebAPIService.GetMediaRating(Id, data => _globalRating = data);
|
await MediaWebAPIService.GetMediaRating(Id, data => _globalRating = data);
|
||||||
|
|||||||
79
WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor
Normal file
79
WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
@using WatchIt.Common.Model.Movies
|
||||||
|
@using WatchIt.Common.Model.Series
|
||||||
|
@using WatchIt.Common.Query
|
||||||
|
@using WatchIt.Website.Components.SearchPage
|
||||||
|
@using WatchIt.Website.Services.WebAPI.Movies
|
||||||
|
|
||||||
|
@layout MainLayout
|
||||||
|
|
||||||
|
@page "/search/{query}"
|
||||||
|
|
||||||
|
<PageTitle>WatchIt - Searching "@(Query)"</PageTitle>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container-grid">
|
||||||
|
@if (_loaded)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_error))
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="rounded-3 panel panel-regular p-3">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<h3 class="m-0">
|
||||||
|
<strong>Search results for phrase:</strong> "@(DecodedQuery)"
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col">
|
||||||
|
<SearchResultComponent TItem="MovieResponse"
|
||||||
|
TQuery="MovieQueryParameters"
|
||||||
|
Title="Movies"
|
||||||
|
UrlIdTemplate="/media/{0}"
|
||||||
|
IdSource="@(item => item.Id)"
|
||||||
|
NameSource="@(item => item.Title)"
|
||||||
|
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
|
||||||
|
Query="@(new MovieQueryParameters { Title = DecodedQuery })"
|
||||||
|
ItemDownloadingTask="@(MoviesWebAPIService.GetAllMovies)"
|
||||||
|
PictureDownloadingTask="@((id, action) => MediaWebAPIService.GetMediaPoster(id, action))"
|
||||||
|
RatingDownloadingTask="@((id, action) => MediaWebAPIService.GetMediaRating(id, action))"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col">
|
||||||
|
<SearchResultComponent TItem="SeriesResponse"
|
||||||
|
TQuery="SeriesQueryParameters"
|
||||||
|
Title="TV series"
|
||||||
|
UrlIdTemplate="/media/{0}"
|
||||||
|
IdSource="@(item => item.Id)"
|
||||||
|
NameSource="@(item => item.Title)"
|
||||||
|
AdditionalNameInfoSource="@(item => item.ReleaseDate.HasValue ? $" ({item.ReleaseDate.Value.Year})" : null)"
|
||||||
|
Query="@(new SeriesQueryParameters { Title = DecodedQuery })"
|
||||||
|
ItemDownloadingTask="@(SeriesWebAPIService.GetAllSeries)"
|
||||||
|
PictureDownloadingTask="@((id, action) => MediaWebAPIService.GetMediaPoster(id, action))"
|
||||||
|
RatingDownloadingTask="@((id, action) => MediaWebAPIService.GetMediaRating(id, action))"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<ErrorComponent ErrorMessage="@(_error)"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<LoadingComponent/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
61
WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor.cs
Normal file
61
WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using WatchIt.Website.Layout;
|
||||||
|
using WatchIt.Website.Services.WebAPI.Media;
|
||||||
|
using WatchIt.Website.Services.WebAPI.Movies;
|
||||||
|
using WatchIt.Website.Services.WebAPI.Series;
|
||||||
|
|
||||||
|
namespace WatchIt.Website.Pages;
|
||||||
|
|
||||||
|
public partial class SearchPage : ComponentBase
|
||||||
|
{
|
||||||
|
#region SERVICES
|
||||||
|
|
||||||
|
[Inject] private IMoviesWebAPIService MoviesWebAPIService { get; set; } = default!;
|
||||||
|
[Inject] private ISeriesWebAPIService SeriesWebAPIService { get; set; } = default!;
|
||||||
|
[Inject] private IMediaWebAPIService MediaWebAPIService { get; set; } = default!;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region FIELDS
|
||||||
|
|
||||||
|
private bool _loaded;
|
||||||
|
private string? _error;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PARAMETERS
|
||||||
|
|
||||||
|
[Parameter] public string Query { get; set; }
|
||||||
|
|
||||||
|
[CascadingParameter] public MainLayout Layout { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PROPERTIES
|
||||||
|
|
||||||
|
public string DecodedQuery => WebUtility.UrlDecode(Query);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PRIVATE METHODS
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
_loaded = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Blazorise;
|
||||||
|
using Blazorise.Bootstrap5;
|
||||||
|
using Blazorise.Icons.FontAwesome;
|
||||||
using Microsoft.AspNetCore.Components.Authorization;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
using WatchIt.Common.Services.HttpClient;
|
using WatchIt.Common.Services.HttpClient;
|
||||||
using WatchIt.Website.Services.Utility.Authentication;
|
using WatchIt.Website.Services.Utility.Authentication;
|
||||||
@@ -53,6 +56,12 @@ public static class Program
|
|||||||
private static WebApplicationBuilder SetupServices(this WebApplicationBuilder builder)
|
private static WebApplicationBuilder SetupServices(this WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
builder.Services.AddSingleton<HttpClient>();
|
builder.Services.AddSingleton<HttpClient>();
|
||||||
|
builder.Services.AddBlazorise(options =>
|
||||||
|
{
|
||||||
|
options.Immediate = true;
|
||||||
|
})
|
||||||
|
.AddBootstrap5Providers()
|
||||||
|
.AddFontAwesomeIcons();
|
||||||
|
|
||||||
// Utility
|
// Utility
|
||||||
builder.Services.AddSingleton<IHttpClientService, HttpClientService>();
|
builder.Services.AddSingleton<IHttpClientService, HttpClientService>();
|
||||||
|
|||||||
@@ -35,10 +35,16 @@
|
|||||||
<_ContentIncludedByDefault Remove="Components\Pages\Weather.razor" />
|
<_ContentIncludedByDefault Remove="Components\Pages\Weather.razor" />
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\bootstrap\bootstrap.min.css" />
|
<_ContentIncludedByDefault Remove="wwwroot\bootstrap\bootstrap.min.css" />
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\bootstrap\bootstrap.min.css.map" />
|
<_ContentIncludedByDefault Remove="wwwroot\bootstrap\bootstrap.min.css.map" />
|
||||||
|
<_ContentIncludedByDefault Remove="wwwroot\scripts\popper.min.js" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AdditionalFiles Include="Layout\MainLayout.razor" />
|
<AdditionalFiles Include="Layout\MainLayout.razor" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Blazorise.Bootstrap5" Version="1.6.1" />
|
||||||
|
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.6.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -14,4 +14,5 @@
|
|||||||
@using WatchIt.Website.Services.Utility.Tokens
|
@using WatchIt.Website.Services.Utility.Tokens
|
||||||
@using WatchIt.Website.Services.Utility.Authentication
|
@using WatchIt.Website.Services.Utility.Authentication
|
||||||
@using WatchIt.Website.Services.WebAPI.Accounts
|
@using WatchIt.Website.Services.WebAPI.Accounts
|
||||||
@using WatchIt.Website.Services.WebAPI.Media
|
@using WatchIt.Website.Services.WebAPI.Media
|
||||||
|
@using Blazorise
|
||||||
BIN
WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/cancel.png
Normal file
BIN
WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/cancel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 581 B |
99
WatchIt.Website/WatchIt.Website/wwwroot/css/general.css
Normal file
99
WatchIt.Website/WatchIt.Website/wwwroot/css/general.css
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/* TAGS */
|
||||||
|
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
background-attachment: fixed;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, html {
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
color: lightgray;
|
||||||
|
font-family: "PT Sans";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CLASSES */
|
||||||
|
|
||||||
|
.container-grid {
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
--bs-gutter-x: 1.5rem;
|
||||||
|
--bs-gutter-y: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-family: "Belanosima";
|
||||||
|
text-decoration: none;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.top-3 {
|
||||||
|
top: 1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-2rem {
|
||||||
|
margin-bottom: 2rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.mt-9 {
|
||||||
|
margin-top: 9rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-header {
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-regular {
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-yellow {
|
||||||
|
background-color: rgba(255, 184, 58, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
|
||||||
|
backdrop-filter: blur(25px);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.dropdown-menu-left {
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-stretch-x {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-100 {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picture-aspect-ratio {
|
||||||
|
aspect-ratio: 3/5;
|
||||||
|
}
|
||||||
@@ -1,40 +1,4 @@
|
|||||||
body, html {
|
/* CLASSES */
|
||||||
background-color: transparent;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
color: lightgray;
|
|
||||||
font-family: "PT Sans";
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
font-family: "Belanosima";
|
|
||||||
text-decoration: none;
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mt-9 {
|
|
||||||
margin-top: 9rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-header {
|
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-regular {
|
|
||||||
background-color: rgba(0, 0, 0, 0.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-yellow {
|
|
||||||
background-color: rgba(255, 184, 58, 0.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
|
|
||||||
backdrop-filter: blur(25px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-button {
|
.main-button {
|
||||||
--r:10px;
|
--r:10px;
|
||||||
@@ -83,17 +47,4 @@ body, html {
|
|||||||
|
|
||||||
.main-button:hover::before {
|
.main-button:hover::before {
|
||||||
-webkit-mask:none;
|
-webkit-mask:none;
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu-left {
|
|
||||||
right: auto;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-stretch-x {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w-100 {
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user