diff --git a/.github/config/gitversion.yml b/.github/config/gitversion.yml index 6ab3dd4..d7a5142 100644 --- a/.github/config/gitversion.yml +++ b/.github/config/gitversion.yml @@ -1,4 +1,4 @@ -next-version: 0.1.0 +next-version: 0.2.0 assembly-versioning-scheme: MajorMinorPatch assembly-file-versioning-scheme: MajorMinorPatch diff --git a/.github/workflows/dev_pull_request_build_and_test.yml b/.github/workflows/dev_pr.yml similarity index 72% rename from .github/workflows/dev_pull_request_build_and_test.yml rename to .github/workflows/dev_pr.yml index 3ccee59..99c2f76 100644 --- a/.github/workflows/dev_pull_request_build_and_test.yml +++ b/.github/workflows/dev_pr.yml @@ -1,12 +1,15 @@ -name: "[Pull request][dev] Build and test" +name: "Pull request to 'dev' branch" on: pull_request: - branches: [ "dev" ] + branches: + - "dev/**" + paths: + - 'WatchIt**' jobs: build: - name: Build and test + name: Dotnet solution build test and unit tests execution runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/dev_push.yml b/.github/workflows/dev_push.yml new file mode 100644 index 0000000..d8ff959 --- /dev/null +++ b/.github/workflows/dev_push.yml @@ -0,0 +1,26 @@ +name: "Push to 'dev' branch" + +on: + push: + branches: + - "dev/**" + paths: + - 'WatchIt**' + +jobs: + build: + name: Dotnet solution build test and unit tests execution + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal diff --git a/.github/workflows/master_pr.yml b/.github/workflows/master_pr.yml new file mode 100644 index 0000000..da7ef70 --- /dev/null +++ b/.github/workflows/master_pr.yml @@ -0,0 +1,45 @@ +name: "Pull request to 'master' branch" + +on: + pull_request: + branches: + - "master" + +jobs: + build-dotnet: + name: Dotnet solution build test and unit tests execution + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build solution + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal + build-docker: + name: Docker image build test + needs: build-dotnet + runs-on: ubuntu-latest + strategy: + matrix: + app: + - WatchIt.WebAPI + - WatchIt.Website + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Convert name to lowercase + id: name + uses: ASzc/change-string-case-action@v1 + with: + string: ${{ matrix.app }} + - name: Build image + run: docker build ${{ github.workspace }} -t ghcr.io/${{github.actor}}/${{ steps.name.outputs.lowercase }}:build-test -f ${{ github.workspace }}/${{ matrix.app }}/${{ matrix.app }}/Dockerfile diff --git a/.github/workflows/master_push.yml b/.github/workflows/master_push.yml index c1eaa6e..3dd2878 100644 --- a/.github/workflows/master_push.yml +++ b/.github/workflows/master_push.yml @@ -6,26 +6,9 @@ on: - "master" jobs: - build: - name: Build and test - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore - - name: Test - run: dotnet test --no-build --verbosity normal publish: name: Publish runs-on: ubuntu-latest - needs: build strategy: matrix: app: diff --git a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfilePicture.cs b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfilePicture.cs index 8ca5569..06114ee 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfilePicture.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Accounts/AccountProfilePicture.cs @@ -2,20 +2,8 @@ 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 [JsonConstructor] diff --git a/WatchIt.Common/WatchIt.Common.Model/Genres/GenreQueryParameters.cs b/WatchIt.Common/WatchIt.Common.Model/Genres/GenreQueryParameters.cs index f5efe40..9b2658c 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Genres/GenreQueryParameters.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Genres/GenreQueryParameters.cs @@ -21,9 +21,9 @@ public class GenreQueryParameters : QueryParameters public override bool IsMeetingConditions(GenreResponse item) => ( - TestString(item.Name, Name) + TestStringWithRegex(item.Name, Name) && - TestString(item.Description, Description) + TestStringWithRegex(item.Description, Description) ); #endregion diff --git a/WatchIt.Common/WatchIt.Common.Model/Genres/GenreResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Genres/GenreResponse.cs index 357d0d0..022fa80 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Genres/GenreResponse.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Genres/GenreResponse.cs @@ -1,12 +1,22 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; +using WatchIt.Common.Query; namespace WatchIt.Common.Model.Genres; -public class GenreResponse : Genre +public class GenreResponse : Genre, IQueryOrderable { #region PROPERTIES + [JsonIgnore] + public static IDictionary> OrderableProperties { get; } = new Dictionary> + { + { "id", x => x.Id }, + { "name", x => x.Name }, + { "description", x => x.Description } + }; + + [JsonPropertyName("id")] public long Id { get; set; } diff --git a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhoto.cs b/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhoto.cs deleted file mode 100644 index aa76ffd..0000000 --- a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhoto.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json.Serialization; - -namespace WatchIt.Common.Model.Media; - -public class MediaPhoto -{ - [JsonPropertyName("media_id")] - public required long MediaId { get; set; } - - [JsonPropertyName("image")] - public required byte[] Image { get; set; } - - [JsonPropertyName("mime_type")] - public required string MimeType { get; set; } - - [JsonPropertyName("background")] - public MediaPhotoBackground? Background { get; set; } -} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoQueryParameters.cs b/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoQueryParameters.cs deleted file mode 100644 index 83777b0..0000000 --- a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoQueryParameters.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Text; -using Microsoft.AspNetCore.Mvc; -using WatchIt.Common.Query; - -namespace WatchIt.Common.Model.Media; - -public class MediaPhotoQueryParameters : QueryParameters -{ - #region PROPERTIES - - [FromQuery(Name = "mime_type")] - public string? MimeType { get; set; } - - [FromQuery(Name = "is_background")] - public bool? IsBackground { get; set; } - - [FromQuery(Name = "is_universal_background")] - public bool? IsUniversalBackground { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - public override bool IsMeetingConditions(MediaPhotoResponse item) => - ( - TestString(item.MimeType, MimeType) - && - TestBoolean(item.Background is not null, IsBackground) - && - TestBoolean(item.Background!.IsUniversalBackground, IsUniversalBackground) - ); - - #endregion -} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoRequest.cs index 956e256..5481b2a 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoRequest.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoRequest.cs @@ -1,12 +1,31 @@ -using WatchIt.Database.Model.Media; +using System.Diagnostics.CodeAnalysis; +using WatchIt.Common.Model.Photos; +using WatchIt.Database.Model.Media; namespace WatchIt.Common.Model.Media; -public class MediaPhotoRequest : MediaPhoto +public class MediaPhotoRequest : Photo { - public MediaPhotoImage CreateMediaPhotoImage() => new MediaPhotoImage + #region CONSTRUCTORS + + public MediaPhotoRequest() {} + + [SetsRequiredMembers] + public MediaPhotoRequest(PhotoResponse response) { - MediaId = MediaId, + Image = response.Image; + MimeType = response.MimeType; + } + + #endregion + + + + #region PUBLIC METHODS + + public MediaPhotoImage CreateMediaPhotoImage(long mediaId) => new MediaPhotoImage + { + MediaId = mediaId, Image = Image, MimeType = MimeType }; @@ -18,22 +37,6 @@ public class MediaPhotoRequest : MediaPhoto FirstGradientColor = Background.FirstGradientColor, SecondGradientColor = Background.SecondGradientColor }; - - public void UpdateMediaPhotoImage(MediaPhotoImage item) - { - item.MediaId = MediaId; - item.Image = Image; - item.MimeType = MimeType; - item.UploadDate = DateTime.Now; - } - public void UpdateMediaPhotoImageBackground(MediaPhotoImageBackground item) - { - if (Background is not null) - { - item.IsUniversalBackground = Background.IsUniversalBackground; - item.FirstGradientColor = Background.FirstGradientColor; - item.SecondGradientColor = Background.SecondGradientColor; - } - } + #endregion } \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPoster.cs b/WatchIt.Common/WatchIt.Common.Model/Media/MediaPoster.cs index d4f4aab..d87668b 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPoster.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Media/MediaPoster.cs @@ -2,23 +2,6 @@ namespace WatchIt.Common.Model.Media; -public abstract class MediaPoster +public abstract class MediaPoster : Picture { - #region PROPERTIES - - [JsonPropertyName("image")] - public required byte[] Image { get; set; } - - [JsonPropertyName("mime_type")] - public required string MimeType { get; set; } - - #endregion - - - - #region PUBLIC METHODS - - public override string ToString() => $"data:{MimeType};base64,{Convert.ToBase64String(Image)}"; - - #endregion } \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Media/MediaQueryParameters.cs b/WatchIt.Common/WatchIt.Common.Model/Media/MediaQueryParameters.cs new file mode 100644 index 0000000..3977d12 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Media/MediaQueryParameters.cs @@ -0,0 +1,84 @@ +using Microsoft.AspNetCore.Mvc; +using WatchIt.Common.Query; + +namespace WatchIt.Common.Model.Media; + +public class MediaQueryParameters : QueryParameters +{ + #region PROPERTIES + + [FromQuery(Name = "type")] + public MediaType? Type { get; set; } + + [FromQuery(Name = "title")] + public string? Title { get; set; } + + [FromQuery(Name = "original_title")] + public string? OriginalTitle { get; set; } + + [FromQuery(Name = "description")] + public string? Description { get; set; } + + [FromQuery(Name = "release_date")] + public DateOnly? ReleaseDate { get; set; } + + [FromQuery(Name = "release_date_from")] + public DateOnly? ReleaseDateFrom { get; set; } + + [FromQuery(Name = "release_date_to")] + public DateOnly? ReleaseDateTo { get; set; } + + [FromQuery(Name = "length")] + public short? Length { get; set; } + + [FromQuery(Name = "length_from")] + public short? LengthFrom { get; set; } + + [FromQuery(Name = "length_to")] + public short? LengthTo { get; set; } + + [FromQuery(Name = "rating_average")] + public decimal? RatingAverage { get; set; } + + [FromQuery(Name = "rating_average_from")] + public decimal? RatingAverageFrom { get; set; } + + [FromQuery(Name = "rating_average_to")] + public decimal? RatingAverageTo { get; set; } + + [FromQuery(Name = "rating_count")] + public long? RatingCount { get; set; } + + [FromQuery(Name = "rating_count_from")] + public long? RatingCountFrom { get; set; } + + [FromQuery(Name = "rating_count_to")] + public long? RatingCountTo { get; set; } + + #endregion + + + + #region PUBLIC METHODS + + public override bool IsMeetingConditions(MediaResponse item) => + ( + Test(item.Type, Type) + && + TestStringWithRegex(item.Title, Title) + && + TestStringWithRegex(item.OriginalTitle, OriginalTitle) + && + TestStringWithRegex(item.Description, Description) + && + TestComparable(item.ReleaseDate, ReleaseDate, ReleaseDateFrom, ReleaseDateTo) + && + TestComparable(item.Length, Length, LengthFrom, LengthTo) + && + TestComparable(item.Rating.Average, RatingAverage, RatingAverageFrom, RatingAverageTo) + && + TestComparable(item.Rating.Count, RatingCount, RatingCountFrom, RatingCountTo) + ); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Media/MediaRatingResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Media/MediaRatingResponse.cs deleted file mode 100644 index e51c9e9..0000000 --- a/WatchIt.Common/WatchIt.Common.Model/Media/MediaRatingResponse.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace WatchIt.Common.Model.Media; - -public class MediaRatingResponse -{ - #region PROPERTIES - - [JsonPropertyName("rating_average")] - public required double RatingAverage { get; set; } - - [JsonPropertyName("rating_count")] - public required long RatingCount { get; set; } - - #endregion - - - - #region CONSTRUCTORS - - [JsonConstructor] - public MediaRatingResponse() {} - - [SetsRequiredMembers] - public MediaRatingResponse(double ratingAverage, long ratingCount) - { - RatingAverage = ratingAverage; - RatingCount = ratingCount; - } - - #endregion -} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Media/MediaResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Media/MediaResponse.cs index 325cfb9..4ee0ba9 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Media/MediaResponse.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Media/MediaResponse.cs @@ -1,17 +1,36 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; +using WatchIt.Common.Model.Rating; +using WatchIt.Common.Query; namespace WatchIt.Common.Model.Media; -public class MediaResponse : Media +public class MediaResponse : Media, IQueryOrderable { #region PROPERTIES + [JsonIgnore] + public static IDictionary> OrderableProperties { get; } = new Dictionary> + { + { "id", x => x.Id }, + { "title", x => x.Title }, + { "original_title", x => x.OriginalTitle }, + { "description", x => x.Description }, + { "release_date", x => x.ReleaseDate }, + { "length", x => x.Length }, + { "rating.average", x => x.Rating.Average }, + { "rating.count", x => x.Rating.Count } + }; + + [JsonPropertyName("id")] public long Id { get; set; } [JsonPropertyName("type")] public MediaType Type { get; set; } + + [JsonPropertyName("rating")] + public RatingResponse Rating { get; set; } #endregion @@ -32,6 +51,7 @@ public class MediaResponse : Media ReleaseDate = media.ReleaseDate; Length = media.Length; Type = mediaType; + Rating = new RatingResponse(media.RatingMedia); } #endregion diff --git a/WatchIt.Common/WatchIt.Common.Model/Movies/MovieQueryParameters.cs b/WatchIt.Common/WatchIt.Common.Model/Movies/MovieQueryParameters.cs index bffef31..aec1df9 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Movies/MovieQueryParameters.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Movies/MovieQueryParameters.cs @@ -43,6 +43,24 @@ public class MovieQueryParameters : QueryParameters [FromQuery(Name = "budget_to")] public decimal? BudgetTo { get; set; } + [FromQuery(Name = "rating_average")] + public decimal? RatingAverage { get; set; } + + [FromQuery(Name = "rating_average_from")] + public decimal? RatingAverageFrom { get; set; } + + [FromQuery(Name = "rating_average_to")] + public decimal? RatingAverageTo { get; set; } + + [FromQuery(Name = "rating_count")] + public long? RatingCount { get; set; } + + [FromQuery(Name = "rating_count_from")] + public long? RatingCountFrom { get; set; } + + [FromQuery(Name = "rating_count_to")] + public long? RatingCountTo { get; set; } + #endregion @@ -51,17 +69,21 @@ public class MovieQueryParameters : QueryParameters public override bool IsMeetingConditions(MovieResponse item) => ( - TestString(item.Title, Title) + TestStringWithRegex(item.Title, Title) && - TestString(item.OriginalTitle, OriginalTitle) + TestStringWithRegex(item.OriginalTitle, OriginalTitle) && - TestString(item.Description, Description) + TestStringWithRegex(item.Description, Description) && TestComparable(item.ReleaseDate, ReleaseDate, ReleaseDateFrom, ReleaseDateTo) && TestComparable(item.Length, Length, LengthFrom, LengthTo) && TestComparable(item.Budget, Budget, BudgetFrom, BudgetTo) + && + TestComparable(item.Rating.Average, RatingAverage, RatingAverageFrom, RatingAverageTo) + && + TestComparable(item.Rating.Count, RatingCount, RatingCountFrom, RatingCountTo) ); #endregion diff --git a/WatchIt.Common/WatchIt.Common.Model/Movies/MovieResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Movies/MovieResponse.cs index 9c73e0e..78a230c 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Movies/MovieResponse.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Movies/MovieResponse.cs @@ -1,15 +1,35 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; +using WatchIt.Common.Model.Rating; +using WatchIt.Common.Query; using WatchIt.Database.Model.Media; namespace WatchIt.Common.Model.Movies; -public class MovieResponse : Movie +public class MovieResponse : Movie, IQueryOrderable { #region PROPERTIES + [JsonIgnore] + public static IDictionary> OrderableProperties { get; } = new Dictionary> + { + { "id", x => x.Id }, + { "title", x => x.Title }, + { "original_title", x => x.OriginalTitle }, + { "description", x => x.Description }, + { "release_date", x => x.ReleaseDate }, + { "length", x => x.Length }, + { "budget", x => x.Budget }, + { "rating.average", x => x.Rating.Average }, + { "rating.count", x => x.Rating.Count } + }; + + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("rating")] + public RatingResponse Rating { get; set; } #endregion @@ -30,6 +50,7 @@ public class MovieResponse : Movie ReleaseDate = mediaMovie.Media.ReleaseDate; Length = mediaMovie.Media.Length; Budget = mediaMovie.Budget; + Rating = new RatingResponse(mediaMovie.Media.RatingMedia); } #endregion diff --git a/WatchIt.Common/WatchIt.Common.Model/Photos/Photo.cs b/WatchIt.Common/WatchIt.Common.Model/Photos/Photo.cs new file mode 100644 index 0000000..39cabfd --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Photos/Photo.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model.Photos; + +public abstract class Photo : Picture +{ + #region PROPERTIES + + [JsonPropertyName("background")] + public PhotoBackgroundData? Background { get; set; } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoBackground.cs b/WatchIt.Common/WatchIt.Common.Model/Photos/PhotoBackgroundData.cs similarity index 68% rename from WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoBackground.cs rename to WatchIt.Common/WatchIt.Common.Model/Photos/PhotoBackgroundData.cs index bb46b23..09c31c3 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoBackground.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Photos/PhotoBackgroundData.cs @@ -1,9 +1,11 @@ -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; -namespace WatchIt.Common.Model.Media; +namespace WatchIt.Common.Model.Photos; -public class MediaPhotoBackground +public class PhotoBackgroundData { + #region PROPERTIES + [JsonPropertyName("is_universal_background")] public required bool IsUniversalBackground { get; set; } @@ -12,4 +14,6 @@ public class MediaPhotoBackground [JsonPropertyName("second_gradient_color")] public required byte[] SecondGradientColor { get; set; } + + #endregion } \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Photos/PhotoBackgroundDataRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Photos/PhotoBackgroundDataRequest.cs new file mode 100644 index 0000000..917548b --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Photos/PhotoBackgroundDataRequest.cs @@ -0,0 +1,42 @@ +using System.Diagnostics.CodeAnalysis; +using WatchIt.Database.Model.Media; + +namespace WatchIt.Common.Model.Photos; + +public class PhotoBackgroundDataRequest : PhotoBackgroundData +{ + #region CONSTRUCTORS + + public PhotoBackgroundDataRequest() {} + + [SetsRequiredMembers] + public PhotoBackgroundDataRequest(PhotoBackgroundData photoBackgroundData) + { + IsUniversalBackground = photoBackgroundData.IsUniversalBackground; + FirstGradientColor = photoBackgroundData.FirstGradientColor; + SecondGradientColor = photoBackgroundData.SecondGradientColor; + } + + #endregion + + + + #region PUBLIC METHODS + + public MediaPhotoImageBackground CreateMediaPhotoImageBackground(Guid photoId) => new MediaPhotoImageBackground + { + Id = photoId, + IsUniversalBackground = IsUniversalBackground, + FirstGradientColor = FirstGradientColor, + SecondGradientColor = SecondGradientColor, + }; + + public void UpdateMediaPhotoImageBackground(MediaPhotoImageBackground image) + { + image.IsUniversalBackground = IsUniversalBackground; + image.FirstGradientColor = FirstGradientColor; + image.SecondGradientColor = SecondGradientColor; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Photos/PhotoQueryParameters.cs b/WatchIt.Common/WatchIt.Common.Model/Photos/PhotoQueryParameters.cs new file mode 100644 index 0000000..b9d2794 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Photos/PhotoQueryParameters.cs @@ -0,0 +1,49 @@ +using System.Text.Json.Serialization; +using Microsoft.AspNetCore.Mvc; +using WatchIt.Common.Model.Media; +using WatchIt.Common.Model.Series; +using WatchIt.Common.Query; + +namespace WatchIt.Common.Model.Photos; + +public class PhotoQueryParameters : QueryParameters +{ + #region PROPERTIES + + [FromQuery(Name = "mime_type")] + public string? MimeType { get; set; } + + [FromQuery(Name = "is_background")] + public bool? IsBackground { get; set; } + + [FromQuery(Name = "is_universal_background")] + public bool? IsUniversalBackground { get; set; } + + [FromQuery(Name = "upload_date")] + public DateOnly? UploadDate { get; set; } + + [FromQuery(Name = "upload_date_from")] + public DateOnly? UploadDateFrom { get; set; } + + [FromQuery(Name = "upload_date_to")] + public DateOnly? UploadDateTo { get; set; } + + #endregion + + + + #region PUBLIC METHODS + + public override bool IsMeetingConditions(PhotoResponse item) => + ( + TestStringWithRegex(item.MimeType, MimeType) + && + Test(item.Background is not null, IsBackground) + && + Test(item.Background is not null && item.Background.IsUniversalBackground, IsUniversalBackground) + && + TestComparable(item.UploadDate, UploadDate, UploadDateFrom, UploadDateTo) + ); + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Photos/PhotoResponse.cs similarity index 54% rename from WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoResponse.cs rename to WatchIt.Common/WatchIt.Common.Model/Photos/PhotoResponse.cs index e2e1ec5..b715235 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Media/MediaPhotoResponse.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Photos/PhotoResponse.cs @@ -1,16 +1,31 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; +using WatchIt.Common.Query; using WatchIt.Database.Model.Media; -namespace WatchIt.Common.Model.Media; +namespace WatchIt.Common.Model.Photos; -public class MediaPhotoResponse : MediaPhoto +public class PhotoResponse : Photo, IQueryOrderable { #region PROPERTIES + [JsonIgnore] + public static IDictionary> OrderableProperties { get; } = new Dictionary> + { + { "id", x => x.Id }, + { "media_id", x => x.MediaId }, + { "mime_type", x => x.MimeType }, + { "is_background", x => x.Background is not null }, + { "is_universal_background", x => x.Background is not null && x.Background.IsUniversalBackground } + }; + + [JsonPropertyName("id")] public Guid Id { get; set; } + [JsonPropertyName("media_id")] + public required long MediaId { get; set; } + [JsonPropertyName("upload_date")] public DateTime UploadDate { get; set; } @@ -21,10 +36,10 @@ public class MediaPhotoResponse : MediaPhoto #region CONSTRUCTORS [JsonConstructor] - public MediaPhotoResponse() {} + public PhotoResponse() {} [SetsRequiredMembers] - public MediaPhotoResponse(MediaPhotoImage mediaPhotoImage) + public PhotoResponse(MediaPhotoImage mediaPhotoImage) { Id = mediaPhotoImage.Id; MediaId = mediaPhotoImage.MediaId; @@ -34,7 +49,7 @@ public class MediaPhotoResponse : MediaPhoto if (mediaPhotoImage.MediaPhotoImageBackground is not null) { - Background = new MediaPhotoBackground + Background = new PhotoBackgroundData { IsUniversalBackground = mediaPhotoImage.MediaPhotoImageBackground.IsUniversalBackground, FirstGradientColor = mediaPhotoImage.MediaPhotoImageBackground.FirstGradientColor, diff --git a/WatchIt.Common/WatchIt.Common.Model/Picture.cs b/WatchIt.Common/WatchIt.Common.Model/Picture.cs new file mode 100644 index 0000000..ab0e8ee --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Picture.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Model; + +public abstract class Picture +{ + #region PROPERTIES + + [JsonPropertyName("image")] + public required byte[] Image { get; set; } + + [JsonPropertyName("mime_type")] + public required string MimeType { get; set; } + + #endregion + + + + #region PUBLIC METHODS + + public override string ToString() => $"data:{MimeType};base64,{Convert.ToBase64String(Image)}"; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Media/MediaRatingRequest.cs b/WatchIt.Common/WatchIt.Common.Model/Rating/RatingRequest.cs similarity index 74% rename from WatchIt.Common/WatchIt.Common.Model/Media/MediaRatingRequest.cs rename to WatchIt.Common/WatchIt.Common.Model/Rating/RatingRequest.cs index 6358b4a..54b7ac2 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Media/MediaRatingRequest.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Rating/RatingRequest.cs @@ -1,9 +1,9 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; -namespace WatchIt.Common.Model.Media; +namespace WatchIt.Common.Model.Rating; -public class MediaRatingRequest +public class RatingRequest { #region PROPERTIES @@ -17,7 +17,7 @@ public class MediaRatingRequest #region CONSTRUCTORS [SetsRequiredMembers] - public MediaRatingRequest(short rating) + public RatingRequest(short rating) { Rating = rating; } diff --git a/WatchIt.Common/WatchIt.Common.Model/Rating/RatingResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Rating/RatingResponse.cs new file mode 100644 index 0000000..cc86fb2 --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Model/Rating/RatingResponse.cs @@ -0,0 +1,37 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; +using WatchIt.Database.Model.Rating; + +namespace WatchIt.Common.Model.Rating; + +public class RatingResponse +{ + #region PROPERTIES + + [JsonPropertyName("average")] + public required decimal Average { get; set; } + + [JsonPropertyName("count")] + public required long Count { get; set; } + + #endregion + + + + #region CONSTRUCTORS + + [JsonConstructor] + public RatingResponse() {} + + [SetsRequiredMembers] + public RatingResponse(IEnumerable ratingMedia) : this(ratingMedia.Any() ? (decimal)ratingMedia.Average(x => x.Rating) : 0, ratingMedia.Count()) {} + + [SetsRequiredMembers] + public RatingResponse(decimal ratingAverage, long ratingCount) + { + Average = ratingAverage; + Count = ratingCount; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Model/Series/SeriesQueryParameters.cs b/WatchIt.Common/WatchIt.Common.Model/Series/SeriesQueryParameters.cs index d8797be..f4594c0 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Series/SeriesQueryParameters.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Series/SeriesQueryParameters.cs @@ -37,6 +37,24 @@ public class SeriesQueryParameters : QueryParameters [FromQuery(Name = "has_ended")] public bool? HasEnded { get; set; } + [FromQuery(Name = "rating_average")] + public decimal? RatingAverage { get; set; } + + [FromQuery(Name = "rating_average_from")] + public decimal? RatingAverageFrom { get; set; } + + [FromQuery(Name = "rating_average_to")] + public decimal? RatingAverageTo { get; set; } + + [FromQuery(Name = "rating_count")] + public long? RatingCount { get; set; } + + [FromQuery(Name = "rating_count_from")] + public long? RatingCountFrom { get; set; } + + [FromQuery(Name = "rating_count_to")] + public long? RatingCountTo { get; set; } + #endregion @@ -45,17 +63,21 @@ public class SeriesQueryParameters : QueryParameters public override bool IsMeetingConditions(SeriesResponse item) => ( - TestString(item.Title, Title) + TestStringWithRegex(item.Title, Title) && - TestString(item.OriginalTitle, OriginalTitle) + TestStringWithRegex(item.OriginalTitle, OriginalTitle) && - TestString(item.Description, Description) + TestStringWithRegex(item.Description, Description) && TestComparable(item.ReleaseDate, ReleaseDate, ReleaseDateFrom, ReleaseDateTo) && TestComparable(item.Length, Length, LengthFrom, LengthTo) && - TestBoolean(item.HasEnded, HasEnded) + Test(item.HasEnded, HasEnded) + && + TestComparable(item.Rating.Average, RatingAverage, RatingAverageFrom, RatingAverageTo) + && + TestComparable(item.Rating.Count, RatingCount, RatingCountFrom, RatingCountTo) ); #endregion diff --git a/WatchIt.Common/WatchIt.Common.Model/Series/SeriesResponse.cs b/WatchIt.Common/WatchIt.Common.Model/Series/SeriesResponse.cs index 2e44003..862d6e0 100644 --- a/WatchIt.Common/WatchIt.Common.Model/Series/SeriesResponse.cs +++ b/WatchIt.Common/WatchIt.Common.Model/Series/SeriesResponse.cs @@ -1,15 +1,35 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; +using WatchIt.Common.Model.Rating; +using WatchIt.Common.Query; using WatchIt.Database.Model.Media; namespace WatchIt.Common.Model.Series; -public class SeriesResponse : Series +public class SeriesResponse : Series, IQueryOrderable { #region PROPERTIES + [JsonIgnore] + public static IDictionary> OrderableProperties { get; } = new Dictionary> + { + { "id", x => x.Id }, + { "title", x => x.Title }, + { "original_title", x => x.OriginalTitle }, + { "description", x => x.Description }, + { "release_date", x => x.ReleaseDate }, + { "length", x => x.Length }, + { "has_ended", x => x.HasEnded }, + { "rating.average", x => x.Rating.Average }, + { "rating.count", x => x.Rating.Count } + }; + + [JsonPropertyName("id")] public long Id { get; set; } + + [JsonPropertyName("rating")] + public RatingResponse Rating { get; set; } #endregion @@ -30,6 +50,7 @@ public class SeriesResponse : Series ReleaseDate = mediaSeries.Media.ReleaseDate; Length = mediaSeries.Media.Length; HasEnded = mediaSeries.HasEnded; + Rating = new RatingResponse(mediaSeries.Media.RatingMedia); } #endregion diff --git a/WatchIt.Common/WatchIt.Common.Query/IQueryOrderable.cs b/WatchIt.Common/WatchIt.Common.Query/IQueryOrderable.cs new file mode 100644 index 0000000..4d4cdae --- /dev/null +++ b/WatchIt.Common/WatchIt.Common.Query/IQueryOrderable.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace WatchIt.Common.Query; + +public interface IQueryOrderable +{ + [JsonIgnore] + public static abstract IDictionary> OrderableProperties { get; } +} \ No newline at end of file diff --git a/WatchIt.Common/WatchIt.Common.Query/QueryParameters.cs b/WatchIt.Common/WatchIt.Common.Query/QueryParameters.cs index 685da26..d946e85 100644 --- a/WatchIt.Common/WatchIt.Common.Query/QueryParameters.cs +++ b/WatchIt.Common/WatchIt.Common.Query/QueryParameters.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Globalization; +using System.Reflection; using System.Text; using System.Text.Json.Serialization; using System.Text.RegularExpressions; @@ -38,7 +39,12 @@ public abstract class QueryParameters FromQueryAttribute? attribute = property.GetCustomAttributes(true).FirstOrDefault(); if (value is not null && attribute is not null) { - string query = $"{attribute.Name}={value}"; + string valueString = (value switch + { + decimal d => d.ToString(CultureInfo.InvariantCulture), + _ => value.ToString() + })!; + string query = $"{attribute.Name}={valueString}"; queries.Add(query); } } @@ -52,21 +58,25 @@ public abstract class QueryParameters #region PRIVATE METHODS - protected static bool TestBoolean(bool property, bool? query) => + protected static bool Test(T? property, T? query) => ( query is null || - property == query + ( + property is not null + && + property.Equals(query) + ) ); - protected static bool TestString(string? property, string? regexQuery) => + protected static bool TestStringWithRegex(string? property, string? regexQuery) => ( string.IsNullOrEmpty(regexQuery) || ( !string.IsNullOrEmpty(property) && - new Regex(regexQuery).IsMatch(property) + Regex.IsMatch(property, regexQuery, RegexOptions.IgnoreCase) ) ); @@ -88,7 +98,7 @@ public abstract class QueryParameters ( property is not null && - property.CompareTo(from) > 0 + property.CompareTo(from) >= 0 ) ) && @@ -108,7 +118,7 @@ public abstract class QueryParameters -public abstract class QueryParameters : QueryParameters where T : class +public abstract class QueryParameters : QueryParameters where T : IQueryOrderable { #region PUBLIC METHODS @@ -120,22 +130,9 @@ public abstract class QueryParameters : QueryParameters where T : class if (OrderBy is not null) { - PropertyInfo[] properties = typeof(T).GetProperties(); - foreach (PropertyInfo property in properties) + if (T.OrderableProperties.TryGetValue(OrderBy, out Func? orderFunc)) { - JsonPropertyNameAttribute? attribute = property.GetCustomAttributes(true).FirstOrDefault(); - if (attribute is not null && attribute.Name == OrderBy) - { - if (Order == "asc") - { - data = data.OrderBy(property.GetValue); - } - else - { - data = data.OrderByDescending(property.GetValue); - } - break; - } + data = Order == "asc" ? data.OrderBy(orderFunc) : data.OrderByDescending(orderFunc); } } if (After is not null) diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MediaController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MediaController.cs index 045308c..0a095e1 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MediaController.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/MediaController.cs @@ -4,33 +4,54 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using WatchIt.Common.Model.Genres; using WatchIt.Common.Model.Media; +using WatchIt.Common.Model.Photos; +using WatchIt.Common.Model.Rating; using WatchIt.WebAPI.Services.Controllers.Media; namespace WatchIt.WebAPI.Controllers; [ApiController] [Route("media")] -public class MediaController(IMediaControllerService mediaControllerService) +public class MediaController : ControllerBase { - #region MAIN + #region FIELDS - [HttpGet("{id}")] - [AllowAnonymous] - [ProducesResponseType(typeof(MediaResponse), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetMedia([FromRoute] long id) => await mediaControllerService.GetMedia(id); + private readonly IMediaControllerService _mediaControllerService; #endregion - #region GENRES + #region CONSTRUCTORS + + public MediaController(IMediaControllerService mediaControllerService) + { + _mediaControllerService = mediaControllerService; + } + + #endregion + + + + #region METHODS + + #region Main + + [HttpGet("{id}")] + [AllowAnonymous] + [ProducesResponseType(typeof(MediaResponse), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetMedia([FromRoute] long id) => await _mediaControllerService.GetMedia(id); + + #endregion + + #region Genres [HttpGet("{id}/genres")] [AllowAnonymous] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetMediaGenres([FromRoute]long id) => await mediaControllerService.GetMediaGenres(id); + public async Task GetMediaGenres([FromRoute]long id) => await _mediaControllerService.GetMediaGenres(id); [HttpPost("{id}/genres/{genre_id}")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] @@ -38,7 +59,7 @@ public class MediaController(IMediaControllerService mediaControllerService) [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task PostMediaGenre([FromRoute]long id, [FromRoute(Name = "genre_id")]short genreId) => await mediaControllerService.PostMediaGenre(id, genreId); + public async Task PostMediaGenre([FromRoute]long id, [FromRoute(Name = "genre_id")]short genreId) => await _mediaControllerService.PostMediaGenre(id, genreId); [HttpDelete("{id}/genres/{genre_id}")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] @@ -46,25 +67,23 @@ public class MediaController(IMediaControllerService mediaControllerService) [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task DeleteMediaGenre([FromRoute]long id, [FromRoute(Name = "genre_id")]short genreId) => await mediaControllerService.DeleteMediaGenre(id, genreId); + public async Task DeleteMediaGenre([FromRoute]long id, [FromRoute(Name = "genre_id")]short genreId) => await _mediaControllerService.DeleteMediaGenre(id, genreId); #endregion - - - #region RATING + #region Rating [HttpGet("{id}/rating")] [AllowAnonymous] - [ProducesResponseType(typeof(MediaRatingResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(RatingResponse), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetMediaRating([FromRoute] long id) => await mediaControllerService.GetMediaRating(id); + public async Task GetMediaRating([FromRoute] long id) => await _mediaControllerService.GetMediaRating(id); [HttpGet("{id}/rating/{user_id}")] [AllowAnonymous] [ProducesResponseType(typeof(short), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetMediaRatingByUser([FromRoute] long id, [FromRoute(Name = "user_id")]long userId) => await mediaControllerService.GetMediaRatingByUser(id, userId); + public async Task GetMediaRatingByUser([FromRoute] long id, [FromRoute(Name = "user_id")]long userId) => await _mediaControllerService.GetMediaRatingByUser(id, userId); [HttpPut("{id}/rating")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] @@ -72,38 +91,34 @@ public class MediaController(IMediaControllerService mediaControllerService) [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task PutMediaRating([FromRoute] long id, [FromBody] MediaRatingRequest data) => await mediaControllerService.PutMediaRating(id, data); + public async Task PutMediaRating([FromRoute] long id, [FromBody] RatingRequest data) => await _mediaControllerService.PutMediaRating(id, data); [HttpDelete("{id}/rating")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] - public async Task DeleteMediaRating([FromRoute] long id) => await mediaControllerService.DeleteMediaRating(id); + public async Task DeleteMediaRating([FromRoute] long id) => await _mediaControllerService.DeleteMediaRating(id); #endregion - - - #region VIEW COUNT + #region View count [HttpPost("{id}/view")] [AllowAnonymous] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task PostMediaView([FromRoute] long id) => await mediaControllerService.PostMediaView(id); + public async Task PostMediaView([FromRoute] long id) => await _mediaControllerService.PostMediaView(id); #endregion - - - #region POSTER + #region Poster [HttpGet("{id}/poster")] [AllowAnonymous] [ProducesResponseType(typeof(MediaPosterResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetMediaPoster([FromRoute] long id) => await mediaControllerService.GetMediaPoster(id); + public async Task GetMediaPoster([FromRoute] long id) => await _mediaControllerService.GetMediaPoster(id); [HttpPut("{id}/poster")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] @@ -111,68 +126,41 @@ public class MediaController(IMediaControllerService mediaControllerService) [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] - public async Task PutMediaPoster([FromRoute]long id, [FromBody]MediaPosterRequest body) => await mediaControllerService.PutMediaPoster(id, body); + public async Task PutMediaPoster([FromRoute]long id, [FromBody]MediaPosterRequest body) => await _mediaControllerService.PutMediaPoster(id, body); [HttpDelete("{id}/poster")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)] [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] - public async Task DeleteMediaPoster([FromRoute]long id) => await mediaControllerService.DeleteMediaPoster(id); + public async Task DeleteMediaPoster([FromRoute]long id) => await _mediaControllerService.DeleteMediaPoster(id); #endregion + #region Photos - - #region PHOTOS + [HttpGet("{id}/photos")] + [AllowAnonymous] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetMediaPhotos([FromRoute]long id, PhotoQueryParameters query) => await _mediaControllerService.GetMediaPhotos(id, query); [HttpGet("{id}/photos/random_background")] [AllowAnonymous] - [ProducesResponseType(typeof(MediaPhotoResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(PhotoResponse), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetMediaPhotoRandomBackground([FromRoute]long id) => await mediaControllerService.GetMediaRandomBackgroundPhoto(id); + public async Task GetMediaPhotoRandomBackground([FromRoute]long id) => await _mediaControllerService.GetMediaPhotoRandomBackground(id); - [HttpGet("photos/{photo_id}")] - [AllowAnonymous] - [ProducesResponseType(typeof(MediaPhotoResponse), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetPhoto([FromRoute(Name = "photo_id")] Guid photoId) => await mediaControllerService.GetPhoto(photoId); - - [HttpGet("photos")] - [AllowAnonymous] - [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task GetPhotos(MediaPhotoQueryParameters query) => await mediaControllerService.GetPhotos(query); - - [HttpGet("photos/random_background")] - [AllowAnonymous] - [ProducesResponseType(typeof(MediaPhotoResponse), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetPhotoRandomBackground() => await mediaControllerService.GetRandomBackgroundPhoto(); - - [HttpPost("photos")] + [HttpPost("{id}/photos")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - [ProducesResponseType(typeof(MediaPhotoResponse), StatusCodes.Status201Created)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] - [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] - public async Task PostPhoto([FromBody]MediaPhotoRequest body) => await mediaControllerService.PostPhoto(body); - - [HttpPut("photos/{photo_id}")] - [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(PhotoResponse), StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task PutPhoto([FromRoute(Name = "photo_id")]Guid photoId, [FromBody]MediaPhotoRequest body) => await mediaControllerService.PutPhoto(photoId, body); + public async Task PostPhoto([FromRoute]long id, [FromBody]MediaPhotoRequest body) => await _mediaControllerService.PostMediaPhoto(id, body); - [HttpDelete("photos/{photo_id}")] - [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] - [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task DeletePhoto([FromRoute(Name = "photo_id")]Guid photoId) => await mediaControllerService.DeletePhoto(photoId); + #endregion #endregion } \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/PhotosController.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/PhotosController.cs new file mode 100644 index 0000000..1b9316a --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/PhotosController.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using WatchIt.Common.Model.Photos; +using WatchIt.WebAPI.Services.Controllers.Photos; + +namespace WatchIt.WebAPI.Controllers; + +[ApiController] +[Route("photos")] +public class PhotosController : ControllerBase +{ + #region FIELDS + + private readonly IPhotosControllerService _photosControllerService; + + #endregion + + + + #region CONSTRUCTORS + + public PhotosController(IPhotosControllerService photosControllerService) + { + _photosControllerService = photosControllerService; + } + + #endregion + + + + #region METHODS + + #region Main + + [HttpGet("random_background")] + [AllowAnonymous] + [ProducesResponseType(typeof(PhotoResponse), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetPhotoRandomBackground() => await _photosControllerService.GetPhotoRandomBackground(); + + [HttpDelete("{id}")] + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task DeletePhoto([FromRoute] Guid id) => await _photosControllerService.DeletePhoto(id); + + #endregion + + #region Background data + + [HttpPut("{id}/background_data")] + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task PutPhotoBackgroundData([FromRoute]Guid id, [FromBody] PhotoBackgroundDataRequest body) => await _photosControllerService.PutPhotoBackgroundData(id, body); + + [HttpDelete("{id}/background_data")] + [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] + [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task DeletePhotoBackgroundData([FromRoute]Guid id) => await _photosControllerService.DeletePhotoBackgroundData(id); + + #endregion + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj index 1b46756..809068e 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Controllers/WatchIt.WebAPI.Controllers.csproj @@ -17,6 +17,7 @@ + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Media/IMediaControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Media/IMediaControllerService.cs index 7e26c60..a93a938 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Media/IMediaControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Media/IMediaControllerService.cs @@ -1,4 +1,6 @@ using WatchIt.Common.Model.Media; +using WatchIt.Common.Model.Photos; +using WatchIt.Common.Model.Rating; using WatchIt.WebAPI.Services.Controllers.Common; namespace WatchIt.WebAPI.Services.Controllers.Media; @@ -13,7 +15,7 @@ public interface IMediaControllerService Task GetMediaRating(long mediaId); Task GetMediaRatingByUser(long mediaId, long userId); - Task PutMediaRating(long mediaId, MediaRatingRequest data); + Task PutMediaRating(long mediaId, RatingRequest data); Task DeleteMediaRating(long mediaId); Task PostMediaView(long mediaId); @@ -21,12 +23,8 @@ public interface IMediaControllerService Task GetMediaPoster(long mediaId); Task PutMediaPoster(long mediaId, MediaPosterRequest data); Task DeleteMediaPoster(long mediaId); - - Task GetPhoto(Guid id); - Task GetPhotos(MediaPhotoQueryParameters query); - Task GetRandomBackgroundPhoto(); - Task GetMediaRandomBackgroundPhoto(long id); - Task PostPhoto(MediaPhotoRequest data); - Task PutPhoto(Guid photoId, MediaPhotoRequest data); - Task DeletePhoto(Guid photoId); + + Task GetMediaPhotos(long mediaId, PhotoQueryParameters queryParameters); + Task GetMediaPhotoRandomBackground(long mediaId); + Task PostMediaPhoto(long mediaId, MediaPhotoRequest data); } \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Media/MediaControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Media/MediaControllerService.cs index d33cea2..295d923 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Media/MediaControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Media/MediaControllerService.cs @@ -2,6 +2,8 @@ using SimpleToolkit.Extensions; using WatchIt.Common.Model.Genres; using WatchIt.Common.Model.Media; +using WatchIt.Common.Model.Photos; +using WatchIt.Common.Model.Rating; using WatchIt.Database; using WatchIt.Database.Model.Media; using WatchIt.Database.Model.Rating; @@ -105,10 +107,8 @@ public class MediaControllerService(DatabaseContext database, IUserService userS { return RequestResult.NotFound(); } - - double ratingAverage = item.RatingMedia.Any() ? item.RatingMedia.Average(x => x.Rating) : 0; - long ratingCount = item.RatingMedia.Count(); - MediaRatingResponse ratingResponse = new MediaRatingResponse(ratingAverage, ratingCount); + + RatingResponse ratingResponse = new RatingResponse(item.RatingMedia); return RequestResult.Ok(ratingResponse); } @@ -130,7 +130,7 @@ public class MediaControllerService(DatabaseContext database, IUserService userS return RequestResult.Ok(rating.Value); } - public async Task PutMediaRating(long mediaId, MediaRatingRequest data) + public async Task PutMediaRating(long mediaId, RatingRequest data) { short ratingValue = data.Rating; if (ratingValue < 1 || ratingValue > 10) @@ -294,59 +294,48 @@ public class MediaControllerService(DatabaseContext database, IUserService userS #endregion #region Photos - - public async Task GetPhoto(Guid id) + + public async Task GetMediaPhotos(long mediaId, PhotoQueryParameters queryParameters) { - MediaPhotoImage? item = await database.MediaPhotoImages.FirstOrDefaultAsync(x => x.Id == id); - if (item is null) + Database.Model.Media.Media? media = await database.Media.FirstOrDefaultAsync(x => x.Id == mediaId); + if (media is null) { return RequestResult.NotFound(); } - - MediaPhotoResponse data = new MediaPhotoResponse(item); - return RequestResult.Ok(data); + + IEnumerable imagesRaw = await database.MediaPhotoImages.Where(x => x.MediaId == mediaId).ToListAsync(); + IEnumerable images = imagesRaw.Select(x => new PhotoResponse(x)); + images = queryParameters.PrepareData(images); + return RequestResult.Ok(images); } - public async Task GetPhotos(MediaPhotoQueryParameters query) + public Task GetMediaPhotoRandomBackground(long mediaId) { - IEnumerable data = await database.MediaPhotoImages.Select(x => new MediaPhotoResponse(x)).ToListAsync(); - data = query.PrepareData(data); - return RequestResult.Ok(data); - } - - public Task GetRandomBackgroundPhoto() - { - MediaPhotoImage? image = database.MediaPhotoImages.Where(x => x.MediaPhotoImageBackground != null && x.MediaPhotoImageBackground.IsUniversalBackground).Random(); + MediaPhotoImage? image = database.MediaPhotoImages.Where(x => x.MediaId == mediaId && x.MediaPhotoImageBackground != null).Random(); if (image is null) { return Task.FromResult(RequestResult.NotFound()); } - MediaPhotoResponse data = new MediaPhotoResponse(image); - return Task.FromResult(RequestResult.Ok(data)); - } - - public Task GetMediaRandomBackgroundPhoto(long id) - { - MediaPhotoImage? image = database.MediaPhotoImages.Where(x => x.MediaId == id && x.MediaPhotoImageBackground != null).Random(); - if (image is null) - { - return Task.FromResult(RequestResult.NotFound()); - } - - MediaPhotoResponse data = new MediaPhotoResponse(image); + PhotoResponse data = new PhotoResponse(image); return Task.FromResult(RequestResult.Ok(data)); } - public async Task PostPhoto(MediaPhotoRequest data) + public async Task PostMediaPhoto(long mediaId, MediaPhotoRequest data) { UserValidator validator = userService.GetValidator().MustBeAdmin(); if (!validator.IsValid) { return RequestResult.Forbidden(); } + + Database.Model.Media.Media? media = await database.Media.FirstOrDefaultAsync(x => x.Id == mediaId); + if (media is null) + { + return RequestResult.NotFound(); + } - MediaPhotoImage item = data.CreateMediaPhotoImage(); + MediaPhotoImage item = data.CreateMediaPhotoImage(mediaId); await database.MediaPhotoImages.AddAsync(item); await database.SaveChangesAsync(); @@ -357,69 +346,7 @@ public class MediaControllerService(DatabaseContext database, IUserService userS await database.SaveChangesAsync(); } - return RequestResult.Created($"photos/{item.Id}", new MediaPhotoResponse(item)); - } - - public async Task PutPhoto(Guid photoId, MediaPhotoRequest data) - { - UserValidator validator = userService.GetValidator().MustBeAdmin(); - if (!validator.IsValid) - { - return RequestResult.Forbidden(); - } - - MediaPhotoImage? item = await database.MediaPhotoImages.FirstOrDefaultAsync(x => x.Id == photoId); - if (item is null) - { - return RequestResult.NotFound(); - } - - data.UpdateMediaPhotoImage(item); - if (item.MediaPhotoImageBackground is null && data.Background is not null) - { - MediaPhotoImageBackground background = data.CreateMediaPhotoImageBackground(item.Id)!; - await database.MediaPhotoImageBackgrounds.AddAsync(background); - } - else if (item.MediaPhotoImageBackground is not null && data.Background is null) - { - database.MediaPhotoImageBackgrounds.Attach(item.MediaPhotoImageBackground); - database.MediaPhotoImageBackgrounds.Remove(item.MediaPhotoImageBackground); - } - else if (item.MediaPhotoImageBackground is not null && data.Background is not null) - { - data.UpdateMediaPhotoImageBackground(item.MediaPhotoImageBackground); - } - await database.SaveChangesAsync(); - - return RequestResult.Ok(); - } - - public async Task DeletePhoto(Guid photoId) - { - UserValidator validator = userService.GetValidator().MustBeAdmin(); - if (!validator.IsValid) - { - return RequestResult.Forbidden(); - } - - MediaPhotoImage? item = await database.MediaPhotoImages.FirstOrDefaultAsync(x => x.Id == photoId); - if (item is null) - { - return RequestResult.NotFound(); - } - - if (item.MediaPhotoImageBackground is not null) - { - database.MediaPhotoImageBackgrounds.Attach(item.MediaPhotoImageBackground); - database.MediaPhotoImageBackgrounds.Remove(item.MediaPhotoImageBackground); - await database.SaveChangesAsync(); - } - - database.MediaPhotoImages.Attach(item); - database.MediaPhotoImages.Remove(item); - await database.SaveChangesAsync(); - - return RequestResult.Ok(); + return RequestResult.Created($"photos/{item.Id}", new PhotoResponse(item)); } #endregion diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs index 50256b0..f94ce06 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Movies/MoviesControllerService.cs @@ -38,7 +38,8 @@ public class MoviesControllerService : IMoviesControllerService public async Task GetAllMovies(MovieQueryParameters query) { - IEnumerable data = await _database.MediaMovies.Select(x => new MovieResponse(x)).ToListAsync(); + IEnumerable rawData = await _database.MediaMovies.ToListAsync(); + IEnumerable data = rawData.Select(x => new MovieResponse(x)); data = query.PrepareData(data); return RequestResult.Ok(data); } diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Photos/IPhotosControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Photos/IPhotosControllerService.cs new file mode 100644 index 0000000..8b17b02 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Photos/IPhotosControllerService.cs @@ -0,0 +1,13 @@ +using WatchIt.Common.Model.Photos; +using WatchIt.WebAPI.Services.Controllers.Common; + +namespace WatchIt.WebAPI.Services.Controllers.Photos; + +public interface IPhotosControllerService +{ + Task GetPhotoRandomBackground(); + Task DeletePhoto(Guid photoId); + + Task PutPhotoBackgroundData(Guid id, PhotoBackgroundDataRequest data); + Task DeletePhotoBackgroundData(Guid id); +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Photos/PhotosControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Photos/PhotosControllerService.cs new file mode 100644 index 0000000..68b0627 --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Photos/PhotosControllerService.cs @@ -0,0 +1,140 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using SimpleToolkit.Extensions; +using WatchIt.Common.Model.Photos; +using WatchIt.Database; +using WatchIt.Database.Model.Media; +using WatchIt.WebAPI.Services.Controllers.Common; +using WatchIt.WebAPI.Services.Utility.User; + +namespace WatchIt.WebAPI.Services.Controllers.Photos; + +public class PhotosControllerService : IPhotosControllerService +{ + #region FIELDS + + private readonly DatabaseContext _database; + private readonly IUserService _userService; + + #endregion + + + + #region CONTRUCTORS + + public PhotosControllerService(DatabaseContext database, IUserService userService) + { + _database = database; + _userService = userService; + } + + #endregion + + + + #region PUBLIC METHODS + + #region Main + + public Task GetPhotoRandomBackground() + { + MediaPhotoImage? image = _database.MediaPhotoImages.Where(x => x.MediaPhotoImageBackground != null && x.MediaPhotoImageBackground.IsUniversalBackground).Random(); + if (image is null) + { + return Task.FromResult(RequestResult.NotFound()); + } + + PhotoResponse data = new PhotoResponse(image); + return Task.FromResult(RequestResult.Ok(data)); + } + + public async Task DeletePhoto(Guid photoId) + { + UserValidator validator = _userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + MediaPhotoImage? item = await _database.MediaPhotoImages.FirstOrDefaultAsync(x => x.Id == photoId); + if (item is null) + { + return RequestResult.NotFound(); + } + + if (item.MediaPhotoImageBackground is not null) + { + _database.MediaPhotoImageBackgrounds.Attach(item.MediaPhotoImageBackground); + _database.MediaPhotoImageBackgrounds.Remove(item.MediaPhotoImageBackground); + await _database.SaveChangesAsync(); + } + + _database.MediaPhotoImages.Attach(item); + _database.MediaPhotoImages.Remove(item); + await _database.SaveChangesAsync(); + + return RequestResult.Ok(); + } + + #endregion + + #region Background data + + public async Task PutPhotoBackgroundData(Guid id, PhotoBackgroundDataRequest data) + { + UserValidator validator = _userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + MediaPhotoImage? image = await _database.MediaPhotoImages.FirstOrDefaultAsync(x => x.Id == id); + if (image is null) + { + return RequestResult.NotFound(); + } + + MediaPhotoImageBackground? imageBackground = image.MediaPhotoImageBackground; + if (imageBackground is null) + { + imageBackground = data.CreateMediaPhotoImageBackground(id); + await _database.MediaPhotoImageBackgrounds.AddAsync(imageBackground); + } + else + { + data.UpdateMediaPhotoImageBackground(imageBackground); + } + await _database.SaveChangesAsync(); + + return RequestResult.Ok(); + } + + public async Task DeletePhotoBackgroundData(Guid id) + { + UserValidator validator = _userService.GetValidator().MustBeAdmin(); + if (!validator.IsValid) + { + return RequestResult.Forbidden(); + } + + MediaPhotoImage? image = await _database.MediaPhotoImages.FirstOrDefaultAsync(x => x.Id == id); + if (image is null) + { + return RequestResult.NotFound(); + } + + MediaPhotoImageBackground? imageBackground = image.MediaPhotoImageBackground; + if (imageBackground is not null) + { + _database.MediaPhotoImageBackgrounds.Attach(imageBackground); + _database.MediaPhotoImageBackgrounds.Remove(imageBackground); + await _database.SaveChangesAsync(); + } + + return RequestResult.Ok(); + } + + #endregion + + #endregion +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Photos/WatchIt.WebAPI.Services.Controllers.Photos.csproj b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Photos/WatchIt.WebAPI.Services.Controllers.Photos.csproj new file mode 100644 index 0000000..f5730de --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Photos/WatchIt.WebAPI.Services.Controllers.Photos.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/SeriesControllerService.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/SeriesControllerService.cs index 79527ad..15cee28 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/SeriesControllerService.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Services/WatchIt.WebAPI.Services.Controllers/WatchIt.WebAPI.Services.Controllers.Series/SeriesControllerService.cs @@ -38,7 +38,8 @@ public class SeriesControllerService : ISeriesControllerService public async Task GetAllSeries(SeriesQueryParameters query) { - IEnumerable data = await _database.MediaSeries.Select(x => new SeriesResponse(x)).ToListAsync(); + IEnumerable rawData = await _database.MediaSeries.ToListAsync(); + IEnumerable data = rawData.Select(x => new SeriesResponse(x)); data = query.PrepareData(data); return RequestResult.Ok(data); } diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Media/MediaPhotoRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Media/MediaPhotoRequestValidator.cs index 6b0d92d..4561c75 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Media/MediaPhotoRequestValidator.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Media/MediaPhotoRequestValidator.cs @@ -2,20 +2,19 @@ using Microsoft.EntityFrameworkCore.Scaffolding.Metadata; using WatchIt.Common.Model.Media; using WatchIt.Database; +using WatchIt.WebAPI.Validators.Photos; namespace WatchIt.WebAPI.Validators.Media; public class MediaPhotoRequestValidator : AbstractValidator { - public MediaPhotoRequestValidator(DatabaseContext database) + public MediaPhotoRequestValidator() { - RuleFor(x => x.MediaId).MustBeIn(database.Media, x => x.Id).WithMessage("Media does not exists"); RuleFor(x => x.Image).NotEmpty(); RuleFor(x => x.MimeType).Matches(@"\w+/.+").WithMessage("Incorrect mimetype"); When(x => x.Background is not null, () => { - RuleFor(x => x.Background!.FirstGradientColor).Must(x => x.Length == 3).WithMessage("First gradient color has to be 3 byte long"); - RuleFor(x => x.Background!.SecondGradientColor).Must(x => x.Length == 3).WithMessage("Second gradient color has to be 3 byte long"); + RuleFor(x => x.Background!).SetValidator(new PhotoBackgroundDataValidator()); }); } } \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Photos/PhotoBackgroundDataRequestValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Photos/PhotoBackgroundDataRequestValidator.cs new file mode 100644 index 0000000..bbf414e --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Photos/PhotoBackgroundDataRequestValidator.cs @@ -0,0 +1,12 @@ +using FluentValidation; +using WatchIt.Common.Model.Photos; + +namespace WatchIt.WebAPI.Validators.Photos; + +public class PhotoBackgroundDataRequestValidator : AbstractValidator +{ + public PhotoBackgroundDataRequestValidator() + { + RuleFor(x => x).SetValidator(new PhotoBackgroundDataValidator()); + } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Photos/PhotoBackgroundDataValidator.cs b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Photos/PhotoBackgroundDataValidator.cs new file mode 100644 index 0000000..58f0d8a --- /dev/null +++ b/WatchIt.WebAPI/WatchIt.WebAPI.Validators/Photos/PhotoBackgroundDataValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; +using WatchIt.Common.Model.Photos; + +namespace WatchIt.WebAPI.Validators.Photos; + +public class PhotoBackgroundDataValidator : AbstractValidator +{ + public PhotoBackgroundDataValidator() + { + RuleFor(x => x.FirstGradientColor).Must(x => x.Length == 3).WithMessage("First gradient color has to be 3 byte long"); + RuleFor(x => x.SecondGradientColor).Must(x => x.Length == 3).WithMessage("Second gradient color has to be 3 byte long"); + } +} \ No newline at end of file diff --git a/WatchIt.WebAPI/WatchIt.WebAPI/Program.cs b/WatchIt.WebAPI/WatchIt.WebAPI/Program.cs index d52813c..7bd902b 100644 --- a/WatchIt.WebAPI/WatchIt.WebAPI/Program.cs +++ b/WatchIt.WebAPI/WatchIt.WebAPI/Program.cs @@ -13,6 +13,7 @@ using WatchIt.WebAPI.Services.Controllers.Accounts; using WatchIt.WebAPI.Services.Controllers.Genres; using WatchIt.WebAPI.Services.Controllers.Media; using WatchIt.WebAPI.Services.Controllers.Movies; +using WatchIt.WebAPI.Services.Controllers.Photos; using WatchIt.WebAPI.Services.Controllers.Series; using WatchIt.WebAPI.Services.Utility.Configuration; using WatchIt.WebAPI.Services.Utility.Tokens; @@ -156,6 +157,7 @@ public static class Program builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddTransient(); return builder; } diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Endpoints.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Endpoints.cs index a71bdbf..765dd46 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Endpoints.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Endpoints.cs @@ -8,4 +8,5 @@ public class Endpoints public Media Media { get; set; } public Movies Movies { get; set; } public Series Series { get; set; } + public Photos Photos { get; set; } } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Media.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Media.cs index fb215e2..3774ebd 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Media.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Media.cs @@ -3,23 +3,19 @@ public class Media { public string Base { get; set; } - public string Get { get; set; } - public string GetGenres { get; set; } - public string PostGenre { get; set; } - public string DeleteGenre { get; set; } + public string GetMedia { get; set; } + public string GetMediaGenres { get; set; } + public string PostMediaGenre { get; set; } + public string DeleteMediaGenre { get; set; } public string GetMediaRating { get; set; } public string GetMediaRatingByUser { get; set; } public string PutMediaRating { get; set; } public string DeleteMediaRating { get; set; } public string PostMediaView { get; set; } - public string GetPhotoMediaRandomBackground { get; set; } - public string GetPoster { get; set; } - public string PutPoster { get; set; } - public string DeletePoster { get; set; } - public string GetPhoto { get; set; } - public string GetPhotos { get; set; } - public string GetPhotoRandomBackground { get; set; } - public string PostPhoto { get; set; } - public string PutPhoto { get; set; } - public string DeletePhoto { get; set; } + public string GetMediaPoster { get; set; } + public string PutMediaPoster { get; set; } + public string DeleteMediaPoster { get; set; } + public string GetMediaPhotos { get; set; } + public string GetMediaPhotoRandomBackground { get; set; } + public string PostMediaPhoto { get; set; } } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Photos.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Photos.cs new file mode 100644 index 0000000..46384b4 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.Utility/WatchIt.Website.Services.Utility.Configuration/Model/Photos.cs @@ -0,0 +1,10 @@ +namespace WatchIt.Website.Services.Utility.Configuration.Model; + +public class Photos +{ + public string Base { get; set; } + public string GetPhotoRandomBackground { get; set; } + public string DeletePhoto { get; set; } + public string PutPhotoBackgroundData { get; set; } + public string DeletePhotoBackgroundData { get; set; } +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Media/IMediaWebAPIService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Media/IMediaWebAPIService.cs index 923607d..9e5c9b4 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Media/IMediaWebAPIService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Media/IMediaWebAPIService.cs @@ -1,5 +1,7 @@ using WatchIt.Common.Model.Genres; using WatchIt.Common.Model.Media; +using WatchIt.Common.Model.Photos; +using WatchIt.Common.Model.Rating; namespace WatchIt.Website.Services.WebAPI.Media; @@ -9,17 +11,20 @@ public interface IMediaWebAPIService Task GetMediaGenres(long mediaId, Action>? successAction = 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 GetMediaRating(long mediaId, Action? successAction = null, Action? notFoundAction = null); + Task GetMediaRating(long mediaId, Action? successAction = null, Action? notFoundAction = null); Task GetMediaRatingByUser(long mediaId, long userId, Action? successAction = null, Action? notFoundAction = null); - Task PutMediaRating(long mediaId, MediaRatingRequest body, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null); + Task PutMediaRating(long mediaId, RatingRequest body, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null); Task DeleteMediaRating(long mediaId, Action? successAction = null, Action? unauthorizedAction = null); Task PostMediaView(long mediaId, Action? successAction = null, Action? notFoundAction = null); - Task GetPhotoMediaRandomBackground(long mediaId, Action? successAction = null, Action? notFoundAction = null); - Task GetPhotoRandomBackground(Action? successAction = null, Action? notFoundAction = null); - Task GetPoster(long mediaId, Action? successAction = null, Action>? badRequestAction = null, Action? notFoundAction = null); - Task PutPoster(long mediaId, MediaPosterRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null); - Task DeletePoster(long mediaId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null); + Task GetMediaPoster(long mediaId, Action? successAction = null, Action>? badRequestAction = null, Action? notFoundAction = null); + Task PutMediaPoster(long mediaId, MediaPosterRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null); + Task DeleteMediaPoster(long mediaId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null); + + Task GetMediaPhotos(long mediaId, PhotoQueryParameters? query = null, Action>? successAction = null, Action? notFoundAction = null); + Task GetMediaPhotoRandomBackground(long mediaId, Action? successAction = null, Action? notFoundAction = null); + Task PostMediaPhoto(long mediaId, MediaPhotoRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null); } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Media/MediaWebAPIService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Media/MediaWebAPIService.cs index b8780fd..f95ae52 100644 --- a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Media/MediaWebAPIService.cs +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Media/MediaWebAPIService.cs @@ -1,5 +1,7 @@ using WatchIt.Common.Model.Genres; using WatchIt.Common.Model.Media; +using WatchIt.Common.Model.Photos; +using WatchIt.Common.Model.Rating; using WatchIt.Common.Services.HttpClient; using WatchIt.Website.Services.Utility.Configuration; using WatchIt.Website.Services.Utility.Configuration.Model; @@ -7,19 +9,38 @@ using WatchIt.Website.Services.WebAPI.Common; namespace WatchIt.Website.Services.WebAPI.Media; -public class MediaWebAPIService(IHttpClientService httpClientService, IConfigurationService configurationService) : BaseWebAPIService(configurationService), IMediaWebAPIService +public class MediaWebAPIService : BaseWebAPIService, IMediaWebAPIService { + #region FIELDS + + private readonly IHttpClientService _httpClientService; + + #endregion + + + + #region CONSTRUCTORS + + public MediaWebAPIService(IHttpClientService httpClientService, IConfigurationService configurationService) : base(configurationService) + { + _httpClientService = httpClientService; + } + + #endregion + + + #region PUBLIC METHODS #region Main public async Task GetMedia(long mediaId, Action? successAction = null, Action? notFoundAction = null) { - string url = GetUrl(EndpointsConfiguration.Media.Get, mediaId); + string url = GetUrl(EndpointsConfiguration.Media.GetMedia, mediaId); HttpRequest request = new HttpRequest(HttpMethodType.Get, url); - HttpResponse response = await httpClientService.SendRequestAsync(request); + HttpResponse response = await _httpClientService.SendRequestAsync(request); response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor404NotFound(notFoundAction) .ExecuteAction(); @@ -31,11 +52,11 @@ public class MediaWebAPIService(IHttpClientService httpClientService, IConfigura public async Task GetMediaGenres(long mediaId, Action>? successAction = null, Action? notFoundAction = null) { - string url = GetUrl(EndpointsConfiguration.Media.GetGenres, mediaId); + string url = GetUrl(EndpointsConfiguration.Media.GetMediaGenres, mediaId); HttpRequest request = new HttpRequest(HttpMethodType.Get, url); - HttpResponse response = await httpClientService.SendRequestAsync(request); + HttpResponse response = await _httpClientService.SendRequestAsync(request); response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor404NotFound(notFoundAction) .ExecuteAction(); @@ -43,11 +64,25 @@ public class MediaWebAPIService(IHttpClientService httpClientService, IConfigura public async Task PostMediaGenre(long mediaId, long genreId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null) { - string url = GetUrl(EndpointsConfiguration.Media.PostGenre, mediaId, genreId); + string url = GetUrl(EndpointsConfiguration.Media.PostMediaGenre, mediaId, genreId); - HttpRequest request = new HttpRequest(HttpMethodType.Get, url); + HttpRequest request = new HttpRequest(HttpMethodType.Post, url); - HttpResponse response = await httpClientService.SendRequestAsync(request); + HttpResponse response = await _httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor401Unauthorized(unauthorizedAction) + .RegisterActionFor403Forbidden(forbiddenAction) + .RegisterActionFor404NotFound(notFoundAction) + .ExecuteAction(); + } + + public async Task DeleteMediaGenre(long mediaId, long genreId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Media.DeleteMediaGenre, mediaId, genreId); + + HttpRequest request = new HttpRequest(HttpMethodType.Delete, url); + + HttpResponse response = await _httpClientService.SendRequestAsync(request); response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor401Unauthorized(unauthorizedAction) .RegisterActionFor403Forbidden(forbiddenAction) @@ -59,13 +94,13 @@ public class MediaWebAPIService(IHttpClientService httpClientService, IConfigura #region Rating - public async Task GetMediaRating(long mediaId, Action? successAction = null, Action? notFoundAction = null) + public async Task GetMediaRating(long mediaId, Action? successAction = null, Action? notFoundAction = null) { string url = GetUrl(EndpointsConfiguration.Media.GetMediaRating, mediaId); HttpRequest request = new HttpRequest(HttpMethodType.Get, url); - HttpResponse response = await httpClientService.SendRequestAsync(request); + HttpResponse response = await _httpClientService.SendRequestAsync(request); response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor404NotFound(notFoundAction) .ExecuteAction(); @@ -77,13 +112,13 @@ public class MediaWebAPIService(IHttpClientService httpClientService, IConfigura HttpRequest request = new HttpRequest(HttpMethodType.Get, url); - HttpResponse response = await httpClientService.SendRequestAsync(request); + HttpResponse response = await _httpClientService.SendRequestAsync(request); response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor404NotFound(notFoundAction) .ExecuteAction(); } - public async Task PutMediaRating(long mediaId, MediaRatingRequest body, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null) + public async Task PutMediaRating(long mediaId, RatingRequest body, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? notFoundAction = null) { string url = GetUrl(EndpointsConfiguration.Media.PutMediaRating, mediaId); @@ -92,7 +127,7 @@ public class MediaWebAPIService(IHttpClientService httpClientService, IConfigura Body = body }; - HttpResponse response = await httpClientService.SendRequestAsync(request); + HttpResponse response = await _httpClientService.SendRequestAsync(request); response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor400BadRequest(badRequestAction) .RegisterActionFor401Unauthorized(unauthorizedAction) @@ -106,7 +141,7 @@ public class MediaWebAPIService(IHttpClientService httpClientService, IConfigura HttpRequest request = new HttpRequest(HttpMethodType.Delete, url); - HttpResponse response = await httpClientService.SendRequestAsync(request); + HttpResponse response = await _httpClientService.SendRequestAsync(request); response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor401Unauthorized(unauthorizedAction) .ExecuteAction(); @@ -122,7 +157,7 @@ public class MediaWebAPIService(IHttpClientService httpClientService, IConfigura HttpRequest request = new HttpRequest(HttpMethodType.Post, url); - HttpResponse response = await httpClientService.SendRequestAsync(request); + HttpResponse response = await _httpClientService.SendRequestAsync(request); response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor404NotFound(notFoundAction) .ExecuteAction(); @@ -130,55 +165,31 @@ public class MediaWebAPIService(IHttpClientService httpClientService, IConfigura #endregion + #region Poster - - public async Task GetPhotoMediaRandomBackground(long mediaId, Action? successAction = null, Action? notFoundAction = null) + public async Task GetMediaPoster(long mediaId, Action? successAction = null, Action>? badRequestAction = null, Action? notFoundAction = null) { - string url = GetUrl(EndpointsConfiguration.Media.GetPhotoMediaRandomBackground, mediaId); + string url = GetUrl(EndpointsConfiguration.Media.GetMediaPoster, mediaId); HttpRequest request = new HttpRequest(HttpMethodType.Get, url); - HttpResponse response = await httpClientService.SendRequestAsync(request); - response.RegisterActionFor2XXSuccess(successAction) - .RegisterActionFor404NotFound(notFoundAction) - .ExecuteAction(); - } - - public async Task GetPhotoRandomBackground(Action? successAction = null, Action? notFoundAction = null) - { - string url = GetUrl(EndpointsConfiguration.Media.GetPhotoRandomBackground); - - HttpRequest request = new HttpRequest(HttpMethodType.Get, url); - - HttpResponse response = await httpClientService.SendRequestAsync(request); - response.RegisterActionFor2XXSuccess(successAction) - .RegisterActionFor404NotFound(notFoundAction) - .ExecuteAction(); - } - - public async Task GetPoster(long mediaId, Action? successAction = null, Action>? badRequestAction = null, Action? notFoundAction = null) - { - string url = GetUrl(EndpointsConfiguration.Media.GetPoster, mediaId); - - HttpRequest request = new HttpRequest(HttpMethodType.Get, url); - - HttpResponse response = await httpClientService.SendRequestAsync(request); + HttpResponse response = await _httpClientService.SendRequestAsync(request); response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor400BadRequest(badRequestAction) .RegisterActionFor404NotFound(notFoundAction) .ExecuteAction(); } - public async Task PutPoster(long mediaId, MediaPosterRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null) + public async Task PutMediaPoster(long mediaId, MediaPosterRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null) { - string url = GetUrl(EndpointsConfiguration.Media.PutPoster, mediaId); + string url = GetUrl(EndpointsConfiguration.Media.PutMediaPoster, mediaId); HttpRequest request = new HttpRequest(HttpMethodType.Put, url) { Body = data }; - HttpResponse response = await httpClientService.SendRequestAsync(request); + HttpResponse response = await _httpClientService.SendRequestAsync(request); response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor400BadRequest(badRequestAction) .RegisterActionFor401Unauthorized(unauthorizedAction) @@ -186,18 +197,66 @@ public class MediaWebAPIService(IHttpClientService httpClientService, IConfigura .ExecuteAction(); } - public async Task DeletePoster(long mediaId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null) + public async Task DeleteMediaPoster(long mediaId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null) { - string url = GetUrl(EndpointsConfiguration.Media.DeletePoster, mediaId); + string url = GetUrl(EndpointsConfiguration.Media.DeleteMediaPoster, mediaId); HttpRequest request = new HttpRequest(HttpMethodType.Delete, url); - HttpResponse response = await httpClientService.SendRequestAsync(request); + HttpResponse response = await _httpClientService.SendRequestAsync(request); response.RegisterActionFor2XXSuccess(successAction) .RegisterActionFor401Unauthorized(unauthorizedAction) .RegisterActionFor403Forbidden(forbiddenAction) .ExecuteAction(); } + + #endregion + + #region Photos + + public async Task GetMediaPhotos(long mediaId, PhotoQueryParameters? query = null, Action>? successAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Media.GetMediaPhotos, mediaId); + + HttpRequest request = new HttpRequest(HttpMethodType.Get, url); + + HttpResponse response = await _httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor404NotFound(notFoundAction) + .ExecuteAction(); + } + + public async Task GetMediaPhotoRandomBackground(long mediaId, Action? successAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Media.GetMediaPhotoRandomBackground, mediaId); + + HttpRequest request = new HttpRequest(HttpMethodType.Get, url); + + HttpResponse response = await _httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor404NotFound(notFoundAction) + .ExecuteAction(); + } + + public async Task PostMediaPhoto(long mediaId, MediaPhotoRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Media.PostMediaPhoto, mediaId); + + HttpRequest request = new HttpRequest(HttpMethodType.Post, url) + { + Body = data + }; + + HttpResponse response = await _httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor400BadRequest(badRequestAction) + .RegisterActionFor401Unauthorized(unauthorizedAction) + .RegisterActionFor403Forbidden(forbiddenAction) + .RegisterActionFor404NotFound(notFoundAction) + .ExecuteAction(); + } + + #endregion #endregion diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Photos/IPhotosWebAPIService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Photos/IPhotosWebAPIService.cs new file mode 100644 index 0000000..560574b --- /dev/null +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Photos/IPhotosWebAPIService.cs @@ -0,0 +1,11 @@ +using WatchIt.Common.Model.Photos; + +namespace WatchIt.Website.Services.WebAPI.Photos; + +public interface IPhotosWebAPIService +{ + Task GetPhotoRandomBackground(Action? successAction = null, Action? notFoundAction = null); + Task DeletePhoto(Guid id, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null); + Task PutPhotoBackgroundData(Guid id, PhotoBackgroundDataRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null); + Task DeletePhotoBackgroundData(Guid id, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null); +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Photos/PhotosWebAPIService.cs b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Photos/PhotosWebAPIService.cs new file mode 100644 index 0000000..3ca5a1d --- /dev/null +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Photos/PhotosWebAPIService.cs @@ -0,0 +1,106 @@ +using WatchIt.Common.Model.Photos; +using WatchIt.Common.Services.HttpClient; +using WatchIt.Website.Services.Utility.Configuration; +using WatchIt.Website.Services.WebAPI.Common; + +namespace WatchIt.Website.Services.WebAPI.Photos; + +public class PhotosWebAPIService : BaseWebAPIService, IPhotosWebAPIService +{ + #region FIELDS + + private readonly IHttpClientService _httpClientService; + + #endregion + + + + #region CONSTRUCTORS + + public PhotosWebAPIService(IHttpClientService httpClientService, IConfigurationService configurationService) : base(configurationService) + { + _httpClientService = httpClientService; + } + + #endregion + + + + #region PUBLIC METHODS + + #region Main + + public async Task GetPhotoRandomBackground(Action? successAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Photos.GetPhotoRandomBackground); + + HttpRequest request = new HttpRequest(HttpMethodType.Get, url); + + HttpResponse response = await _httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor404NotFound(notFoundAction) + .ExecuteAction(); + } + + public async Task DeletePhoto(Guid id, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Photos.DeletePhoto, id); + + HttpRequest request = new HttpRequest(HttpMethodType.Delete, url); + + HttpResponse response = await _httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor401Unauthorized(unauthorizedAction) + .RegisterActionFor403Forbidden(forbiddenAction) + .RegisterActionFor404NotFound(notFoundAction) + .ExecuteAction(); + } + + #endregion + + #region Background data + + public async Task PutPhotoBackgroundData(Guid id, PhotoBackgroundDataRequest data, Action? successAction = null, Action>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Photos.PutPhotoBackgroundData, id); + + HttpRequest request = new HttpRequest(HttpMethodType.Put, url) + { + Body = data + }; + + HttpResponse response = await _httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor400BadRequest(badRequestAction) + .RegisterActionFor401Unauthorized(unauthorizedAction) + .RegisterActionFor403Forbidden(forbiddenAction) + .RegisterActionFor404NotFound(notFoundAction) + .ExecuteAction(); + } + + public async Task DeletePhotoBackgroundData(Guid id, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null, Action? notFoundAction = null) + { + string url = GetUrl(EndpointsConfiguration.Photos.DeletePhotoBackgroundData, id); + + HttpRequest request = new HttpRequest(HttpMethodType.Delete, url); + + HttpResponse response = await _httpClientService.SendRequestAsync(request); + response.RegisterActionFor2XXSuccess(successAction) + .RegisterActionFor401Unauthorized(unauthorizedAction) + .RegisterActionFor403Forbidden(forbiddenAction) + .RegisterActionFor404NotFound(notFoundAction) + .ExecuteAction(); + } + + #endregion + + #endregion + + + + #region PRIVATE METHODS + + protected override string GetServiceBase() => EndpointsConfiguration.Photos.Base; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Photos/WatchIt.Website.Services.WebAPI.Photos.csproj b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Photos/WatchIt.Website.Services.WebAPI.Photos.csproj new file mode 100644 index 0000000..79bbe83 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website.Services/WatchIt.Website.Services.WebAPI/WatchIt.Website.Services.WebAPI.Photos/WatchIt.Website.Services.WebAPI.Photos.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + + + + + + + + + diff --git a/WatchIt.Website/WatchIt.Website/App.razor b/WatchIt.Website/WatchIt.Website/App.razor index 0e62374..fb9b08f 100644 --- a/WatchIt.Website/WatchIt.Website/App.razor +++ b/WatchIt.Website/WatchIt.Website/App.razor @@ -9,12 +9,19 @@ - - + + + + + + + + + diff --git a/WatchIt.Website/WatchIt.Website/Components/DatabasePage/DatabasePageComponent.razor b/WatchIt.Website/WatchIt.Website/Components/DatabasePage/DatabasePageComponent.razor new file mode 100644 index 0000000..10b6ed6 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/DatabasePage/DatabasePageComponent.razor @@ -0,0 +1,91 @@ +@typeparam TItem where TItem : WatchIt.Common.Query.IQueryOrderable +@typeparam TQuery where TQuery : WatchIt.Common.Query.QueryParameters + + + + +
+
+
+
+
+

@(Title)

+
+
+
+ Order by + + + +
+
+
+
+ + + + + + Filters + + @(ChildContent) + + + + +
+ +
+
+
+
+ @if (_loaded) + { + if (string.IsNullOrWhiteSpace(_error)) + { + foreach (TItem item in _items) + { +
+ +
+ } + if (!_allItemsLoaded) + { +
+
+ @if (!_itemsLoading) + { + Load more + } + else + { + + Loading... + } +
+
+ } + } + else + { + + } + } + else + { +
+ +
+ } +
+
+ \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/DatabasePage/DatabasePageComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/DatabasePage/DatabasePageComponent.razor.cs new file mode 100644 index 0000000..84ff2b8 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/DatabasePage/DatabasePageComponent.razor.cs @@ -0,0 +1,139 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Model; +using WatchIt.Common.Model.Movies; +using WatchIt.Common.Model.Rating; +using WatchIt.Common.Query; + +namespace WatchIt.Website.Components.DatabasePage; + +public partial class DatabasePageComponent : ComponentBase where TItem : IQueryOrderable where TQuery : QueryParameters +{ + #region SERVICES + + [Inject] private NavigationManager NavigationManager { get; set; } = default!; + + #endregion + + + + #region PARAMETERS + + [Parameter] public required string Title { get; set; } + [Parameter] public required Func IdSource { get; set; } + [Parameter] public required Func NameSource { get; set; } + [Parameter] public Func AdditionalNameInfoSource { get; set; } = _ => null; + [Parameter] public required Func RatingSource { get; set; } + [Parameter] public required string UrlIdTemplate { get; set; } + [Parameter] public required Func, Task> PictureDownloadingTask { get; set; } + [Parameter] public required Func>, Task> ItemDownloadingTask { get; set; } + [Parameter] public required Dictionary SortingOptions { get; set; } + [Parameter] public required RenderFragment ChildContent { get; set; } + + #endregion + + + + #region FIELDS + + private bool _loaded; + private string? _error; + + private List _items = new List(); + private bool _allItemsLoaded; + private bool _itemsLoading; + + #endregion + + + + #region PROPERTIES + + public TQuery Query { get; set; } = Activator.CreateInstance()!; + + #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + // INIT + Query.OrderBy = SortingOptions.Keys.First(); + Query.First = 100; + + List endTasks = new List(); + + // STEP 0 + endTasks.AddRange( + [ + ItemDownloadingTask(Query, data => + { + _items.AddRange(data); + if (data.Count() < 100) + { + _allItemsLoaded = true; + } + else + { + Query.After = 100; + } + }) + ]); + + // END + await Task.WhenAll(endTasks); + + _loaded = true; + StateHasChanged(); + } + } + + private async Task DownloadItems() + { + _itemsLoading = true; + await ItemDownloadingTask(Query, AppendNewItems); + } + + private async Task SortingAscendingChanged(ChangeEventArgs args) + { + Query.Order = (bool)args.Value! ? "asc" : "desc"; + await UpdateItems(); + } + + private async Task SortingOptionChanged(ChangeEventArgs args) + { + Query.OrderBy = args.Value!.ToString(); + await UpdateItems(); + } + + private async Task FilterApplied() => await UpdateItems(); + + private async Task UpdateItems() + { + _loaded = false; + Query.First = 100; + Query.After = null; + _items.Clear(); + await ItemDownloadingTask(Query, AppendNewItems); + _loaded = true; + } + + private void AppendNewItems(IEnumerable items) + { + _items.AddRange(items); + if (items.Count() < 100) + { + _allItemsLoaded = true; + } + else + { + Query.After += 100; + } + _itemsLoading = false; + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/DatabasePage/DatabasePageComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/DatabasePage/DatabasePageComponent.razor.css new file mode 100644 index 0000000..e69de29 diff --git a/WatchIt.Website/WatchIt.Website/Components/DatabasePage/FilterFormComponent.cs b/WatchIt.Website/WatchIt.Website/Components/DatabasePage/FilterFormComponent.cs new file mode 100644 index 0000000..26fa16d --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/DatabasePage/FilterFormComponent.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Common.Query; + +namespace WatchIt.Website.Components.DatabasePage; + +public abstract class FilterFormComponent : ComponentBase where TItem : IQueryOrderable where TQuery : QueryParameters +{ + #region PARAMETERS + + [CascadingParameter] + protected DatabasePageComponent Parent { get; set; } + + #endregion + + + + #region FIELDS + + protected TQuery? Query => Parent?.Query; + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/DatabasePage/MoviesFilterFormComponent.razor b/WatchIt.Website/WatchIt.Website/Components/DatabasePage/MoviesFilterFormComponent.razor new file mode 100644 index 0000000..d24e473 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/DatabasePage/MoviesFilterFormComponent.razor @@ -0,0 +1,66 @@ +@inherits FilterFormComponent + + + + +
+
+
+ Title + +
+
+
+
+ Original title + +
+
+
+
+ Description + +
+
+
+
+ Release date + + - + +
+
+
+
+ Length + + - + +
+
+
+
+ Budget + + - + +
+
+
+
+ Rating (count) + + - + +
+
+
+
+ Rating (average) + + - + +
+
+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/DatabasePage/SeriesFilterFormComponent.razor b/WatchIt.Website/WatchIt.Website/Components/DatabasePage/SeriesFilterFormComponent.razor new file mode 100644 index 0000000..1bad8cc --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/DatabasePage/SeriesFilterFormComponent.razor @@ -0,0 +1,69 @@ +@inherits FilterFormComponent + + +
+
+
+ Title + +
+
+
+
+ Original title + +
+
+
+
+ Description + +
+
+
+
+ Release date + + - + +
+
+
+
+ Length + + - + +
+
+
+
+ Has ended +
+ + + + + + +
+
+
+
+
+ Rating (count) + + - + +
+
+
+
+ Rating (average) + + - + +
+
+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/ErrorComponent.razor b/WatchIt.Website/WatchIt.Website/Components/ErrorComponent.razor index 6f1c699..bc34f73 100644 --- a/WatchIt.Website/WatchIt.Website/Components/ErrorComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/ErrorComponent.razor @@ -1,32 +1,28 @@ -
-
-
-
-
-
-
-
⚠︎
-
-
+
+
+
+
+
+
⚠︎
-
-
-
-

An error occured while loading a page

-
-
-
- @if (!string.IsNullOrWhiteSpace(ErrorMessage)) - { -
-
-
-

@ErrorMessage

-
-
-
- }
+
+
+
+

An error occured while loading a page

+
+
+
+ @if (!string.IsNullOrWhiteSpace(ErrorMessage)) + { +
+
+
+

@ErrorMessage

+
+
+
+ }
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/ListItemComponent.razor b/WatchIt.Website/WatchIt.Website/Components/ListItemComponent.razor new file mode 100644 index 0000000..566d09b --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/ListItemComponent.razor @@ -0,0 +1,26 @@ +
+
+
+ picture +
+
+
+
+ + @(Name)@(string.IsNullOrWhiteSpace(AdditionalNameInfo) ? string.Empty : AdditionalNameInfo) + +
+
+ +
+ @(Rating.Count > 0 ? Rating.Average : "--")/10 + @if (Rating.Count > 0) + { + @(Rating.Count) + } +
+
+
+
+
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/ListItemComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/ListItemComponent.razor.cs new file mode 100644 index 0000000..bd6713f --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/ListItemComponent.razor.cs @@ -0,0 +1,54 @@ +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 RatingResponse Rating { get; set; } + [Parameter] public required Func, Task> PictureDownloadingTask { get; set; } + [Parameter] public int PictureHeight { get; set; } = 150; + + #endregion + + + + #region FIELDS + + private bool _loaded; + + private Picture? _picture; + + #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + List endTasks = new List(); + + // STEP 0 + endTasks.AddRange( + [ + PictureDownloadingTask(Id, picture => _picture = picture), + ]); + + await Task.WhenAll(endTasks); + + _loaded = true; + StateHasChanged(); + } + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/ListItemComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/ListItemComponent.razor.css new file mode 100644 index 0000000..91b920c --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/ListItemComponent.razor.css @@ -0,0 +1,17 @@ +/* IDS */ + +#nameText { + font-size: 25px; +} + +#ratingStar { + font-size: 30px; +} + +#ratingValue { + font-size: 20px; +} + +#ratingCount { + font-size: 10px; +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/LoadingComponent.razor b/WatchIt.Website/WatchIt.Website/Components/LoadingComponent.razor index ea3d99a..4803ba5 100644 --- a/WatchIt.Website/WatchIt.Website/Components/LoadingComponent.razor +++ b/WatchIt.Website/WatchIt.Website/Components/LoadingComponent.razor @@ -1,12 +1,8 @@ -
-
-
-
-
-
-
-

Loading...

-
-
+
+
+
+
+
+

Loading...

\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/LoadingComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/LoadingComponent.razor.cs new file mode 100644 index 0000000..2ab6e66 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/LoadingComponent.razor.cs @@ -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 +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/SearchPage/SearchResultComponent.razor b/WatchIt.Website/WatchIt.Website/Components/SearchPage/SearchResultComponent.razor new file mode 100644 index 0000000..a448ee8 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/SearchPage/SearchResultComponent.razor @@ -0,0 +1,82 @@ +@using Microsoft.IdentityModel.Tokens + +@typeparam TItem +@typeparam TQuery where TQuery : WatchIt.Common.Query.QueryParameters + + + +
+
+
+
+

@(Title)

+
+
+ @if (_loaded) + { + if (!_items.IsNullOrEmpty()) + { + for (int i = 0; i < _items.Count; i++) + { + if (i > 0) + { +
+
+
+
+
+ } +
+
+ + + +
+
+ } + if (!_allItemsLoaded) + { +
+
+
+ +
+
+
+ } + } + else + { +
+
+
+ No items found +
+
+
+ } + } + else + { +
+
+ +
+
+ } +
+
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/SearchPage/SearchResultComponent.razor.cs b/WatchIt.Website/WatchIt.Website/Components/SearchPage/SearchResultComponent.razor.cs new file mode 100644 index 0000000..2e93844 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Components/SearchPage/SearchResultComponent.razor.cs @@ -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 : ComponentBase where TQuery : QueryParameters +{ + #region PARAMETERS + + [Parameter] public required string Title { get; set; } + [Parameter] public required TQuery Query { get; set; } + [Parameter] public required Func IdSource { get; set; } + [Parameter] public required Func NameSource { get; set; } + [Parameter] public Func AdditionalNameInfoSource { get; set; } = _ => null; + [Parameter] public required Func RatingSource { get; set; } + [Parameter] public required string UrlIdTemplate { get; set; } + [Parameter] public required Func>, Task> ItemDownloadingTask { get; set; } + [Parameter] public required Func, Task> PictureDownloadingTask { get; set; } + + #endregion + + + + #region FIELDS + + private bool _loaded; + + private List _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 endTasks = new List(); + + // 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 +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Components/SearchPage/SearchResultComponent.razor.css b/WatchIt.Website/WatchIt.Website/Components/SearchPage/SearchResultComponent.razor.css new file mode 100644 index 0000000..e69de29 diff --git a/WatchIt.Website/WatchIt.Website/Layout/EmptyLayout.razor.css b/WatchIt.Website/WatchIt.Website/Layout/EmptyLayout.razor.css deleted file mode 100644 index 5f28270..0000000 --- a/WatchIt.Website/WatchIt.Website/Layout/EmptyLayout.razor.css +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor index 0b21658..6e23112 100644 --- a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor +++ b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor @@ -1,175 +1,102 @@ -@inherits LayoutComponentBase +@using System.Net +@using WatchIt.Common.Model.Photos +@using WatchIt.Website.Services.WebAPI.Photos -@if (_loaded) -{ -
-
- -
-

Menu

-
-
-
- @if (_user is null) - { - Sign in - } - else - { - - - -} - - - -@code -{ - #region SERVICES - - [Inject] public ILogger 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!; - - #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 bgTasks = new List(); - - bgTasks.Add(GetBackground()); - - await GetAuthenticatedUser(); - - if (_user is not null) - { - bgTasks.Add(GetProfilePicture()); + } - - private async Task GetProfilePicture() - { - Action 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 -} \ No newline at end of file + \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs new file mode 100644 index 0000000..db6a271 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.cs @@ -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 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 endTasks = new List(); + List step1Tasks = new List(); + + // 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 +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.css b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.css index 350d80d..9a467a1 100644 --- a/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.css +++ b/WatchIt.Website/WatchIt.Website/Layout/MainLayout.razor.css @@ -1,21 +1,18 @@ -body { - background-size: cover; -} - -.logo { - font-size: 40px; -} - -.header { - position: sticky; - top: 10px; - height: 60px; -} - -.body-content { - padding-top: 100px; -} +/* TAGS */ h1 { margin: 0; +} + + + +/* IDS */ + +#logo { + font-size: 40px; +} + +#searchbarCancel { + cursor: pointer; + color: #6c757d; } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/AdminPage.razor b/WatchIt.Website/WatchIt.Website/Pages/AdminPage.razor index fed8def..c13c42f 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/AdminPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/AdminPage.razor @@ -2,7 +2,7 @@ WatchIt administrator panel -
+
@if (_loaded) { if (_authenticated) @@ -26,11 +26,21 @@ } else { - +
+
+ +
+
} } else { - +
+
+
+ +
+
+
}
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/AdminPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/AdminPage.razor.cs index 2a9306a..3817611 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/AdminPage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/AdminPage.razor.cs @@ -1,15 +1,24 @@ using Microsoft.AspNetCore.Components; +using WatchIt.Website.Layout; using WatchIt.Website.Services.Utility.Authentication; namespace WatchIt.Website.Pages; public partial class AdminPage { - #region SERVICE + #region SERVICES [Inject] public IAuthenticationService AuthenticationService { get; set; } = default!; #endregion + + + + #region PARAMETERS + + [CascadingParameter] public MainLayout Layout { get; set; } + + #endregion @@ -28,6 +37,8 @@ public partial class AdminPage { if (firstRender) { + Layout.BackgroundPhoto = null; + User? user = await AuthenticationService.GetUserAsync(); if (user is not null && user.IsAdmin) { diff --git a/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor b/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor index 42b5202..0becab5 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor @@ -1,117 +1,116 @@ @page "/auth" @layout EmptyLayout -WatchIt - @(_authType == AuthType.SignIn ? "Sign in" : "Sign up") - +WatchIt - @(_isSingUp ? "Sign up" : "Sign in") @if (_loaded) {
-
- -
- @if (_authType == AuthType.SignIn) +
+
+ + @if (_isSingUp) { -
+ -
- +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ @_formMessage +
+
+ +
+
-
- -
-
- -
-
- -
- + } else { -
+ -
- +
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ + +
+
+
+
+
+ @_formMessage +
+
+ +
+
-
- -
-
- -
-
- -
-
- -
- + } -
- @if (_errors is not null) - { -
- @foreach (string error in _errors) - { - @error -
- } +
+ + + +
- } -
- -
- - - + + + } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor.cs index f45313f..d646264 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor.cs @@ -1,10 +1,13 @@ -using Microsoft.AspNetCore.Components; +using System.Net; +using Microsoft.AspNetCore.Components; using WatchIt.Common.Model.Accounts; using WatchIt.Common.Model.Media; +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.Pages; @@ -17,48 +20,45 @@ public partial class AuthPage [Inject] public ITokensService TokensService { get; set; } = default!; [Inject] public IMediaWebAPIService MediaWebAPIService { get; set; } = default!; [Inject] public IAccountsWebAPIService AccountsWebAPIService { get; set; } = default!; + [Inject] public IPhotosWebAPIService PhotosWebAPIService { get; set; } = default!; [Inject] public NavigationManager NavigationManager { get; set; } = default!; #endregion - #region ENUMS - - private enum AuthType - { - SignIn, - SignUp - } + #region PARAMETERS + [SupplyParameterFromQuery(Name = "redirect_to")] + private string RedirectTo { get; set; } = "/"; + #endregion #region FIELDS - private bool _loaded = false; + private bool _loaded; - private AuthType _authType = AuthType.SignIn; - private string _background = "assets/background_temp.jpg"; - private string _firstGradientColor = "#c6721c"; - private string _secondGradientColor = "#85200c"; - - private AuthenticateRequest _loginModel = new AuthenticateRequest - { - UsernameOrEmail = null, - Password = null - }; + private PhotoResponse? _background; + private bool _isSingUp; + private string? _formMessage; + private bool _formMessageIsSuccess; + private RegisterRequest _registerModel = new RegisterRequest { Username = null, Email = null, Password = null }; - private string _passwordConfirmation; - - private IEnumerable _errors; + private string _registerPasswordConfirmation; + + private AuthenticateRequest _loginModel = new AuthenticateRequest + { + UsernameOrEmail = null, + Password = null + }; #endregion @@ -72,22 +72,19 @@ public partial class AuthPage { if (await AuthenticationService.GetAuthenticationStatusAsync()) { - NavigationManager.NavigateTo("/"); + NavigationManager.NavigateTo(WebUtility.UrlDecode(RedirectTo)); } - - Action 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 MediaWebAPIService.GetPhotoRandomBackground(backgroundSuccess); + List endTasks = new List(); + + // STEP 0 + endTasks.AddRange( + [ + PhotosWebAPIService.GetPhotoRandomBackground(data => _background = data) + ]); + + // END + await Task.WhenAll(endTasks); _loaded = true; StateHasChanged(); @@ -96,44 +93,51 @@ public partial class AuthPage private async Task Login() { - await AccountsWebAPIService.Authenticate(_loginModel, LoginSuccess, LoginBadRequest, LoginUnauthorized); - - async void LoginSuccess(AuthenticateResponse data) - { - await TokensService.SaveAuthenticationData(data); - NavigationManager.NavigateTo("/"); - } - void LoginBadRequest(IDictionary data) { - _errors = data.SelectMany(x => x.Value).Select(x => $"• {x}"); + _formMessageIsSuccess = false; + _formMessage = data.SelectMany(x => x.Value).FirstOrDefault(); } - + void LoginUnauthorized() { - _errors = [ "Incorrect account data" ]; + _formMessageIsSuccess = false; + _formMessage = "Incorrect account data"; } + + async Task LoginSuccess(AuthenticateResponse data) + { + await TokensService.SaveAuthenticationData(data); + NavigationManager.NavigateTo(RedirectTo); + } + + + await AccountsWebAPIService.Authenticate(_loginModel, async (data) => await LoginSuccess(data), LoginBadRequest, LoginUnauthorized); } private async Task Register() { - if (_registerModel.Password != _passwordConfirmation) - { - _errors = [ "Password fields don't match" ]; - return; - } - - await AccountsWebAPIService.Register(_registerModel, RegisterSuccess, RegisterBadRequest); - void RegisterSuccess(RegisterResponse data) { - _authType = AuthType.SignIn; + _formMessageIsSuccess = true; + _formMessage = "You are registered. You can sign in now."; + _isSingUp = false; } - + void RegisterBadRequest(IDictionary data) { - _errors = data.SelectMany(x => x.Value).Select(x => $"• {x}"); + _formMessageIsSuccess = false; + _formMessage = data.SelectMany(x => x.Value).FirstOrDefault(); } + + + if (_registerModel.Password != _registerPasswordConfirmation) + { + _formMessageIsSuccess = false; + _formMessage = "Password fields don't match"; + return; + } + await AccountsWebAPIService.Register(_registerModel, RegisterSuccess, RegisterBadRequest); } #endregion diff --git a/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor.css b/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor.css index 35fd5d3..0d8700d 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor.css +++ b/WatchIt.Website/WatchIt.Website/Pages/AuthPage.razor.css @@ -4,6 +4,14 @@ html { height: 100%; } +body { + background-position: center; + background-repeat: no-repeat; + background-size: cover; + + height: 100%; +} + /* CLASSES */ diff --git a/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor b/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor new file mode 100644 index 0000000..c96d375 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor @@ -0,0 +1,63 @@ +@using WatchIt.Common.Model.Movies +@using WatchIt.Common.Model.Series +@using WatchIt.Website.Components.DatabasePage + +@page "/database/{type?}" + + + +@if (_loaded) +{ + switch (Type) + { + case "movies": + + + + break; + case "series": + + + + break; + } + +} +else +{ +
+ +
+} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor.cs new file mode 100644 index 0000000..186a3b7 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor.cs @@ -0,0 +1,58 @@ +using Microsoft.AspNetCore.Components; +using WatchIt.Website.Components.DatabasePage; +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 DatabasePage : ComponentBase +{ + #region SERVICES + + [Inject] private NavigationManager NavigationManager { get; set; } = default!; + [Inject] private IMediaWebAPIService MediaWebAPIService { get; set; } = default!; + [Inject] private IMoviesWebAPIService MoviesWebAPIService { get; set; } = default!; + [Inject] private ISeriesWebAPIService SeriesWebAPIService { get; set; } = default!; + + #endregion + + + + #region PARAMETERS + + [Parameter] public string? Type { get; set; } + + #endregion + + + + #region FIELDS + + private static IEnumerable _databaseTypes = ["movies", "series"]; + + private bool _loaded; + + #endregion + + + + #region PRIVATE METHODS + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + // INIT + if (!_databaseTypes.Contains(Type)) + { + NavigationManager.NavigateTo($"/database/{_databaseTypes.First()}"); + } + + _loaded = true; + StateHasChanged(); + } + } + + #endregion +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor.css b/WatchIt.Website/WatchIt.Website/Pages/DatabasePage.razor.css new file mode 100644 index 0000000..e69de29 diff --git a/WatchIt.Website/WatchIt.Website/Pages/HomePage.razor b/WatchIt.Website/WatchIt.Website/Pages/HomePage.razor index 2d99b94..1766091 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/HomePage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/HomePage.razor @@ -1,9 +1,8 @@ @page "/" -@using WatchIt.Common.Model.Movies WatchIt -
+
@if (_loaded) { if (string.IsNullOrWhiteSpace(_error)) @@ -11,7 +10,7 @@
-
+

Top 5 movies this week by popularity

@@ -26,7 +25,7 @@
poster -
+
@(i + 1)
@@ -49,7 +48,7 @@
-
+

Top 5 TV series this week by popularity

@@ -64,7 +63,7 @@
poster -
+
@(i + 1)
@@ -87,11 +86,21 @@ } else { - +
+
+ +
+
} } else { - +
+
+
+ +
+
+
}
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/HomePage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/HomePage.razor.cs index 170b8a6..5f528db 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/HomePage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/HomePage.razor.cs @@ -2,6 +2,7 @@ using WatchIt.Common.Model.Media; using WatchIt.Common.Model.Movies; using WatchIt.Common.Model.Series; +using WatchIt.Website.Layout; using WatchIt.Website.Services.WebAPI.Media; using WatchIt.Website.Services.WebAPI.Movies; 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 private bool _loaded; @@ -39,6 +48,8 @@ public partial class HomePage { if (firstRender) { + Layout.BackgroundPhoto = null; + List step1Tasks = new List(); List endTasks = new List(); @@ -53,8 +64,8 @@ public partial class HomePage await Task.WhenAll(step1Tasks); endTasks.AddRange( [ - Parallel.ForEachAsync(_topMovies, async (x, _) => await MediaWebAPIService.GetPoster(x.Key.Id, y => _topMovies[x.Key] = y)), - Parallel.ForEachAsync(_topSeries, async (x, _) => await MediaWebAPIService.GetPoster(x.Key.Id, y => _topSeries[x.Key] = y)) + Parallel.ForEachAsync(_topMovies, async (x, _) => await MediaWebAPIService.GetMediaPoster(x.Key.Id, y => _topMovies[x.Key] = y)), + Parallel.ForEachAsync(_topSeries, async (x, _) => await MediaWebAPIService.GetMediaPoster(x.Key.Id, y => _topSeries[x.Key] = y)) ]); // END diff --git a/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor b/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor index 5f4e437..68b0d51 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor @@ -1,8 +1,10 @@ -@using WatchIt.Common.Model.Movies +@using Microsoft.IdentityModel.Tokens +@using WatchIt.Common.Model.Movies +@using WatchIt.Common.Model.Photos @using WatchIt.Common.Model.Series @page "/media/{id:long}/edit" -@page "/media/new/{type}" +@page "/media/new/{type?}" @@ -38,7 +40,7 @@ } -
+
@if (_loaded) { if (string.IsNullOrWhiteSpace(_error)) @@ -48,14 +50,14 @@
-

@(_media is not null ? "Edit" : "Create new") @(_movieRequest is not null ? "movie" : "series")@(_media is not null ? $" \"{_media.Title}\"" : string.Empty)

+

@(_media is not null ? "Edit" : "Create new") @(_movieRequest is not null ? "movie" : "series")@(_media is not null ? $" \"{_media.Title}\"" : string.Empty)

-
+
poster @@ -75,12 +77,12 @@
@@ -94,12 +96,12 @@
@@ -113,7 +115,7 @@
-
+
@@ -181,12 +183,12 @@
@@ -197,34 +199,193 @@
+
+
+
+
+
+
+
+

Photos

+
+
+
+ @if (!_photoEditMode) + { + + } + else + { +
+ @if (!string.IsNullOrWhiteSpace(_photoEditError)) + { +
+ @_photoEditError +
+ } + + +
+ } +
+
+
+
+ @if (!_photoEditMode) + { + if (!_photos.IsNullOrEmpty()) + { +
+ @foreach (PhotoResponse photo in _photos) + { +
+
+
+ photo +
+
+
+ @if (photo.Background is not null) + { +
+
+
+ background_icon +
+
+
+ } +
+
+ Upload: @(photo.UploadDate.ToString()) +
+
+
+ +
+
+ +
+
+
+ } +
+ } + else + { +
+ Photo list is empty +
+ } + } + else + { +
+
+
+
+
+
+ edit_photo +
+
+ @if (_photoEditId is null) + { +
+
+ +
+
+ } +
+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+ } +
+
+
+
+
+
} else { - +
+
+ +
+
} } else { - +
+
+ +
+
} } else { - +
+
+
+ +
+
+
} -
- - - -@if (_background is not null) -{ - -} \ No newline at end of file +
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor.cs index 6d98912..e034282 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor.cs @@ -3,10 +3,13 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Forms; using WatchIt.Common.Model.Media; using WatchIt.Common.Model.Movies; +using WatchIt.Common.Model.Photos; using WatchIt.Common.Model.Series; +using WatchIt.Website.Layout; using WatchIt.Website.Services.Utility.Authentication; using WatchIt.Website.Services.WebAPI.Media; using WatchIt.Website.Services.WebAPI.Movies; +using WatchIt.Website.Services.WebAPI.Photos; using WatchIt.Website.Services.WebAPI.Series; namespace WatchIt.Website.Pages; @@ -20,6 +23,7 @@ public partial class MediaEditPage : ComponentBase [Inject] public IMediaWebAPIService MediaWebAPIService { get; set; } = default!; [Inject] public IMoviesWebAPIService MoviesWebAPIService { get; set; } = default!; [Inject] public ISeriesWebAPIService SeriesWebAPIService { get; set; } = default!; + [Inject] public IPhotosWebAPIService PhotosWebAPIService { get; set; } = default!; #endregion @@ -30,6 +34,8 @@ public partial class MediaEditPage : ComponentBase [Parameter] public long? Id { get; set; } [Parameter] public string? Type { get; set; } + [CascadingParameter] public MainLayout Layout { get; set; } + #endregion @@ -40,8 +46,6 @@ public partial class MediaEditPage : ComponentBase private string? _error; private User? _user; - - private MediaPhotoResponse? _background; private MediaResponse? _media; private MovieRequest? _movieRequest; @@ -55,6 +59,21 @@ public partial class MediaEditPage : ComponentBase private bool _mediaPosterChanged; private bool _mediaPosterSaving; private bool _mediaPosterDeleting; + + private IEnumerable _photos = new List(); + private List _photoDeleting = new List(); + private bool _photoEditMode; + private string? _photoEditError; + private Guid? _photoEditId; + private bool _photoEditSaving; + private bool _photoEditIsBackground; + private MediaPhotoRequest? _photoEditRequest; + private PhotoBackgroundDataRequest? _photoEditBackgroundData = new PhotoBackgroundDataRequest() + { + FirstGradientColor = [0xFF, 0xFF, 0xFF], + SecondGradientColor = [0x00, 0x00, 0x00], + IsUniversalBackground = false + }; #endregion @@ -68,6 +87,8 @@ public partial class MediaEditPage : ComponentBase { if (firstRender) { + Layout.BackgroundPhoto = null; + List step1Tasks = new List(); List step2Tasks = new List(); List endTasks = new List(); @@ -94,12 +115,13 @@ public partial class MediaEditPage : ComponentBase { endTasks.AddRange( [ - MediaWebAPIService.GetPhotoMediaRandomBackground(Id.Value, data => _background = data), - MediaWebAPIService.GetPoster(Id.Value, data => + MediaWebAPIService.GetMediaPhotoRandomBackground(Id.Value, data => Layout.BackgroundPhoto = data), + MediaWebAPIService.GetMediaPoster(Id.Value, data => { _mediaPosterSaved = data; _mediaPosterRequest = new MediaPosterRequest(data); - }) + }), + MediaWebAPIService.GetMediaPhotos(Id.Value, successAction: data => _photos = data) ]); } @@ -179,7 +201,7 @@ public partial class MediaEditPage : ComponentBase } _mediaPosterSaving = true; - await MediaWebAPIService.PutPoster(Id.Value, _mediaPosterRequest, Success); + await MediaWebAPIService.PutMediaPoster(Id.Value, _mediaPosterRequest, Success); } private void CancelPoster() @@ -199,7 +221,7 @@ public partial class MediaEditPage : ComponentBase } _mediaPosterDeleting = true; - await MediaWebAPIService.DeletePoster(Id.Value, Success); + await MediaWebAPIService.DeleteMediaPoster(Id.Value, Success); } #endregion @@ -250,5 +272,116 @@ public partial class MediaEditPage : ComponentBase #endregion + #region Photos + + private async Task DeletePhoto(Guid id) + { + async Task Success() + { + NavigationManager.Refresh(true); + } + + _photoDeleting.Add(id); + await PhotosWebAPIService.DeletePhoto(id, async () => await Success()); + } + + private void InitEditPhoto(Guid? id) + { + _photoEditMode = true; + _photoEditId = id; + _photoEditRequest = null; + _photoEditIsBackground = false; + _photoEditBackgroundData = new PhotoBackgroundDataRequest + { + FirstGradientColor = [0xFF, 0xFF, 0xFF], + SecondGradientColor = [0x00, 0x00, 0x00], + IsUniversalBackground = false + }; + if (id is not null) + { + PhotoResponse response = _photos.First(x => x.Id == id); + _photoEditRequest = new MediaPhotoRequest(response); + if (response.Background is not null) + { + _photoEditIsBackground = true; + _photoEditBackgroundData = new PhotoBackgroundDataRequest(response.Background); + } + } + } + + private void CancelEditPhoto() + { + _photoEditMode = false; + _photoEditId = null; + _photoEditError = null; + } + + private async Task SaveEditPhoto() + { + void Success() + { + NavigationManager.Refresh(true); + } + + void BadRequest(IDictionary errors) + { + _photoEditError = errors.SelectMany(x => x.Value).FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(_basicDataError)) + { + _photoEditSaving = false; + } + } + + _photoEditSaving = true; + if (_photoEditId is null) + { + _photoEditRequest.Background = _photoEditIsBackground ? _photoEditBackgroundData : null; + await MediaWebAPIService.PostMediaPhoto(Id.Value, _photoEditRequest, Success, BadRequest); + } + else + { + if (_photoEditIsBackground) + { + await PhotosWebAPIService.PutPhotoBackgroundData(_photoEditId.Value, _photoEditBackgroundData, Success, BadRequest); + } + else + { + await PhotosWebAPIService.DeletePhotoBackgroundData(_photoEditId.Value, Success); + } + } + } + + private async Task LoadPhoto(InputFileChangeEventArgs args) + { + if (args.File.ContentType.StartsWith("image")) + { + Stream stream = args.File.OpenReadStream(5242880); + byte[] array; + using (MemoryStream ms = new MemoryStream()) + { + await stream.CopyToAsync(ms); + array = ms.ToArray(); + } + + _photoEditRequest = new MediaPhotoRequest + { + Image = array, + MimeType = args.File.ContentType + }; + } + } + + private void EditPhotoFirstGradientColorChanged(ChangeEventArgs e) + { + _photoEditBackgroundData.FirstGradientColor = Convert.FromHexString(e.Value.ToString().Replace("#", string.Empty)); + } + + private void EditPhotoSecondGradientColorChanged(ChangeEventArgs e) + { + _photoEditBackgroundData.SecondGradientColor = Convert.FromHexString(e.Value.ToString().Replace("#", string.Empty)); + } + + #endregion + #endregion } \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor.css b/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor.css new file mode 100644 index 0000000..d03857d --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor.css @@ -0,0 +1,45 @@ +/* IDS */ + +#scrollPhotos { + overflow-x: scroll; + + border-color: grey; + border-width: 1px; + border-style: solid; + border-radius: 10px; +} + +#backgroundIndicator { + height: 30px; + width: 30px; +} + + + +/* CLASSES */ + +.circle-blue { + background-color: dodgerblue; + border-color: dodgerblue; +} + +.circle-grey { + background-color: grey; + border-color: grey; +} + +.no-vertical-align { + vertical-align: inherit; +} + +.photo-container { + width: 350px; +} + +.text-size-upload-date { + font-size: 12px; +} + +.photo-default-aspect-ratio { + aspect-ratio: 16/9; +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor b/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor index a59a95f..6c144cc 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor +++ b/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor @@ -24,7 +24,7 @@ else -
+
@if (_loaded) { if (string.IsNullOrWhiteSpace(_error)) @@ -35,7 +35,7 @@ else
-
+

@@ -60,7 +60,7 @@ else
-
+
@@ -133,11 +133,11 @@ else
-
+

- Global rating: @(_globalRating.RatingCount == 0 ? "no ratings" : $"{Math.Round(_globalRating.RatingAverage, 1)}/10") + Global rating: @(_globalRating.Count == 0 ? "no ratings" : $"{Math.Round(_globalRating.Average, 1)}/10")

@@ -193,26 +193,21 @@ else } else { - +
+
+ +
+
} } else { - +
+
+
+ +
+
+
} -
- - - -@if (_background is not null) -{ - -} \ No newline at end of file +
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor.cs index cf834a2..fb48fd0 100644 --- a/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor.cs +++ b/WatchIt.Website/WatchIt.Website/Pages/MediaPage.razor.cs @@ -3,7 +3,10 @@ using Microsoft.AspNetCore.Components; using WatchIt.Common.Model.Genres; using WatchIt.Common.Model.Media; using WatchIt.Common.Model.Movies; +using WatchIt.Common.Model.Photos; +using WatchIt.Common.Model.Rating; using WatchIt.Common.Model.Series; +using WatchIt.Website.Layout; using WatchIt.Website.Services.Utility.Authentication; using WatchIt.Website.Services.WebAPI.Media; using WatchIt.Website.Services.WebAPI.Movies; @@ -29,6 +32,8 @@ public partial class MediaPage : ComponentBase [Parameter] public long Id { get; set; } + [CascadingParameter] public MainLayout Layout { get; set; } + #endregion @@ -42,10 +47,9 @@ public partial class MediaPage : ComponentBase private User? _user; - private MediaPhotoResponse? _background; private MediaPosterResponse? _poster; private IEnumerable _genres; - private MediaRatingResponse _globalRating; + private RatingResponse _globalRating; private MovieResponse? _movie; private SeriesResponse? _series; @@ -61,6 +65,8 @@ public partial class MediaPage : ComponentBase { if (firstRender) { + Layout.BackgroundPhoto = null; + List step1Tasks = new List(); List step2Tasks = new List(); List endTasks = new List(); @@ -83,8 +89,8 @@ public partial class MediaPage : ComponentBase endTasks.AddRange( [ MediaWebAPIService.PostMediaView(Id), - MediaWebAPIService.GetPhotoMediaRandomBackground(Id, data => _background = data), - MediaWebAPIService.GetPoster(Id, data => _poster = data), + MediaWebAPIService.GetMediaPhotoRandomBackground(Id, data => Layout.BackgroundPhoto = data), + MediaWebAPIService.GetMediaPoster(Id, data => _poster = data), MediaWebAPIService.GetMediaGenres(Id, data => _genres = data), MediaWebAPIService.GetMediaRating(Id, data => _globalRating = data), _media.Type == MediaType.Movie ? MoviesWebAPIService.GetMovie(Id, data => _movie = data) : SeriesWebAPIService.GetSeries(Id, data => _series = data), @@ -118,7 +124,7 @@ public partial class MediaPage : ComponentBase } else { - await MediaWebAPIService.PutMediaRating(Id, new MediaRatingRequest(rating)); + await MediaWebAPIService.PutMediaRating(Id, new RatingRequest(rating)); _userRating = rating; } await MediaWebAPIService.GetMediaRating(Id, data => _globalRating = data); diff --git a/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor b/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor new file mode 100644 index 0000000..c0bb6e3 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor @@ -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}" + +WatchIt - Searching "@(Query)" + + + +
+ @if (_loaded) + { + if (string.IsNullOrWhiteSpace(_error)) + { +
+
+
+
+

+ Search results for phrase: "@(DecodedQuery)" +

+
+
+
+
+
+
+ +
+
+
+
+ +
+
+ } + else + { +
+
+ +
+
+ } + } + else + { +
+
+ +
+
+ } +
\ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor.cs b/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor.cs new file mode 100644 index 0000000..0e926d6 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor.cs @@ -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 +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor.css b/WatchIt.Website/WatchIt.Website/Pages/SearchPage.razor.css new file mode 100644 index 0000000..e69de29 diff --git a/WatchIt.Website/WatchIt.Website/Program.cs b/WatchIt.Website/WatchIt.Website/Program.cs index 133fc07..283995a 100644 --- a/WatchIt.Website/WatchIt.Website/Program.cs +++ b/WatchIt.Website/WatchIt.Website/Program.cs @@ -1,5 +1,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +using Blazorise; +using Blazorise.Bootstrap5; +using Blazorise.Icons.FontAwesome; using Microsoft.AspNetCore.Components.Authorization; using WatchIt.Common.Services.HttpClient; using WatchIt.Website.Services.Utility.Authentication; @@ -8,6 +11,7 @@ using WatchIt.Website.Services.Utility.Tokens; using WatchIt.Website.Services.WebAPI.Accounts; using WatchIt.Website.Services.WebAPI.Media; using WatchIt.Website.Services.WebAPI.Movies; +using WatchIt.Website.Services.WebAPI.Photos; using WatchIt.Website.Services.WebAPI.Series; namespace WatchIt.Website; @@ -52,6 +56,12 @@ public static class Program private static WebApplicationBuilder SetupServices(this WebApplicationBuilder builder) { builder.Services.AddSingleton(); + builder.Services.AddBlazorise(options => + { + options.Immediate = true; + }) + .AddBootstrap5Providers() + .AddFontAwesomeIcons(); // Utility builder.Services.AddSingleton(); @@ -64,6 +74,7 @@ public static class Program builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); return builder; } diff --git a/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj b/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj index 7dbaed0..3ba0027 100644 --- a/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj +++ b/WatchIt.Website/WatchIt.Website/WatchIt.Website.csproj @@ -22,6 +22,7 @@ + @@ -34,10 +35,16 @@ <_ContentIncludedByDefault Remove="Components\Pages\Weather.razor" /> <_ContentIncludedByDefault Remove="wwwroot\bootstrap\bootstrap.min.css" /> <_ContentIncludedByDefault Remove="wwwroot\bootstrap\bootstrap.min.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\scripts\popper.min.js" /> + + + + + diff --git a/WatchIt.Website/WatchIt.Website/_Imports.razor b/WatchIt.Website/WatchIt.Website/_Imports.razor index 7376a6e..3a478f4 100644 --- a/WatchIt.Website/WatchIt.Website/_Imports.razor +++ b/WatchIt.Website/WatchIt.Website/_Imports.razor @@ -14,4 +14,6 @@ @using WatchIt.Website.Services.Utility.Tokens @using WatchIt.Website.Services.Utility.Authentication @using WatchIt.Website.Services.WebAPI.Accounts -@using WatchIt.Website.Services.WebAPI.Media \ No newline at end of file +@using WatchIt.Website.Services.WebAPI.Media +@using Blazorise +@using Blazorise.Bootstrap5 \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/appsettings.json b/WatchIt.Website/WatchIt.Website/appsettings.json index 1cc3e65..02e7c38 100644 --- a/WatchIt.Website/WatchIt.Website/appsettings.json +++ b/WatchIt.Website/WatchIt.Website/appsettings.json @@ -30,26 +30,21 @@ }, "Media": { "Base": "/media", - "Get": "/{0}", - "GetGenres": "/{0}/genres", - "PostGenre": "/{0}/genres/{1}", - "DeleteGenre": "/{0}/genres/{1}", + "GetMedia": "/{0}", + "GetMediaGenres": "/{0}/genres", + "PostMediaGenre": "/{0}/genres/{1}", + "DeleteMediaGenre": "/{0}/genres/{1}", "GetMediaRating": "/{0}/rating", "GetMediaRatingByUser": "/{0}/rating/{1}", "PutMediaRating": "/{0}/rating", "DeleteMediaRating": "/{0}/rating", "PostMediaView": "/{0}/view", - - "GetPhotoMediaRandomBackground": "/{0}/photos/random_background", - "GetPoster": "/{0}/poster", - "PutPoster": "/{0}/poster", - "DeletePoster": "/{0}/poster", - "GetPhoto": "/photos/{0}", - "GetPhotos": "/photos", - "GetPhotoRandomBackground": "/photos/random_background", - "PostPhoto": "/photos", - "PutPhoto": "/photos/{0}", - "DeletePhoto": "/photos/{0}" + "GetMediaPoster": "/{0}/poster", + "PutMediaPoster": "/{0}/poster", + "DeleteMediaPoster": "/{0}/poster", + "GetMediaPhotos": "/{0}/photos", + "GetMediaPhotoRandomBackground": "/{0}/photos/random_background", + "PostMediaPhoto": "/{0}/photos" }, "Movies": { "Base": "/movies", @@ -68,6 +63,13 @@ "PutSeries": "/{0}", "DeleteSeries": "/{0}", "GetSeriesViewRank": "/view" + }, + "Photos": { + "Base": "/photos", + "GetPhotoRandomBackground": "/random_background", + "DeletePhoto": "/{0}", + "PutPhotoBackgroundData": "/{0}/background_data", + "DeletePhotoBackgroundData": "/{0}/background_data" } } } diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/background.png b/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/background.png new file mode 100644 index 0000000..9ad7696 Binary files /dev/null and b/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/background.png differ diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/cancel.png b/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/cancel.png new file mode 100644 index 0000000..68a9cba Binary files /dev/null and b/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/cancel.png differ diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/delete.png b/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/delete.png new file mode 100644 index 0000000..c834e3b Binary files /dev/null and b/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/delete.png differ diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/edit.png b/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/edit.png new file mode 100644 index 0000000..bdfab9a Binary files /dev/null and b/WatchIt.Website/WatchIt.Website/wwwroot/assets/icons/edit.png differ diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/assets/photo.png b/WatchIt.Website/WatchIt.Website/wwwroot/assets/photo.png new file mode 100644 index 0000000..71d49bd Binary files /dev/null and b/WatchIt.Website/WatchIt.Website/wwwroot/assets/photo.png differ diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/css/general.css b/WatchIt.Website/WatchIt.Website/wwwroot/css/general.css new file mode 100644 index 0000000..443ec27 --- /dev/null +++ b/WatchIt.Website/WatchIt.Website/wwwroot/css/general.css @@ -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; +} \ No newline at end of file diff --git a/WatchIt.Website/WatchIt.Website/wwwroot/app.css b/WatchIt.Website/WatchIt.Website/wwwroot/css/main_button.css similarity index 59% rename from WatchIt.Website/WatchIt.Website/wwwroot/app.css rename to WatchIt.Website/WatchIt.Website/wwwroot/css/main_button.css index 3504642..2bfc5c7 100644 --- a/WatchIt.Website/WatchIt.Website/wwwroot/app.css +++ b/WatchIt.Website/WatchIt.Website/wwwroot/css/main_button.css @@ -1,40 +1,4 @@ -body, html { - 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); -} +/* CLASSES */ .main-button { --r:10px; @@ -83,13 +47,4 @@ body, html { .main-button:hover::before { -webkit-mask:none; -} - -.dropdown-menu-left { - right: auto; - left: 0; -} - -.btn-stretch-x { - width: 100%; } \ No newline at end of file diff --git a/WatchIt.sln b/WatchIt.sln index d94dd47..07a0606 100644 --- a/WatchIt.sln +++ b/WatchIt.sln @@ -84,6 +84,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Con EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.WebAPI.Series", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Series\WatchIt.Website.Services.WebAPI.Series.csproj", "{783C743A-85BF-4382-BFE5-7A90E3F3B8B6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.WebAPI.Services.Controllers.Photos", "WatchIt.WebAPI\WatchIt.WebAPI.Services\WatchIt.WebAPI.Services.Controllers\WatchIt.WebAPI.Services.Controllers.Photos\WatchIt.WebAPI.Services.Controllers.Photos.csproj", "{ABDF8471-2FAB-4930-B016-7DD3E48AE6B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchIt.Website.Services.WebAPI.Photos", "WatchIt.Website\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Photos\WatchIt.Website.Services.WebAPI.Photos.csproj", "{960A833F-C195-4D1D-AD4F-D00B57067181}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -128,6 +132,8 @@ Global {8720AECA-7084-429A-BA15-49B6622C1A32} = {130BC8F5-82CE-4EDF-AECB-21594DD41849} {F8FCEF7B-72EA-48BC-AC68-E11244B067DD} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7} {783C743A-85BF-4382-BFE5-7A90E3F3B8B6} = {46E3711F-18BD-4004-AF53-EA4D8643D92F} + {ABDF8471-2FAB-4930-B016-7DD3E48AE6B8} = {CEC468DB-CC49-47D3-9E3E-1CC9530C3CE7} + {960A833F-C195-4D1D-AD4F-D00B57067181} = {46E3711F-18BD-4004-AF53-EA4D8643D92F} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {23383776-1F27-4B5D-8C7C-57BFF75FA473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -250,5 +256,13 @@ Global {783C743A-85BF-4382-BFE5-7A90E3F3B8B6}.Debug|Any CPU.Build.0 = Debug|Any CPU {783C743A-85BF-4382-BFE5-7A90E3F3B8B6}.Release|Any CPU.ActiveCfg = Release|Any CPU {783C743A-85BF-4382-BFE5-7A90E3F3B8B6}.Release|Any CPU.Build.0 = Release|Any CPU + {ABDF8471-2FAB-4930-B016-7DD3E48AE6B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ABDF8471-2FAB-4930-B016-7DD3E48AE6B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ABDF8471-2FAB-4930-B016-7DD3E48AE6B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ABDF8471-2FAB-4930-B016-7DD3E48AE6B8}.Release|Any CPU.Build.0 = Release|Any CPU + {960A833F-C195-4D1D-AD4F-D00B57067181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {960A833F-C195-4D1D-AD4F-D00B57067181}.Debug|Any CPU.Build.0 = Debug|Any CPU + {960A833F-C195-4D1D-AD4F-D00B57067181}.Release|Any CPU.ActiveCfg = Release|Any CPU + {960A833F-C195-4D1D-AD4F-D00B57067181}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal