mirror of
				https://github.com/Kareadita/Kavita.git
				synced 2025-11-04 03:27:05 -05:00 
			
		
		
		
	* Added an id for komf userscript to help it inject into Kavita's UI without relying on strings, given localization. * Still working the filter fields, there is a bug with selecting an input and it setting undefined like crazy. Path is coded but not tested or validated. * Stashing changed. Really not sure what's happening. I'm seeing 2 constructor calls for one row. I'm seeing a field change trigger 400 events. Values aren't getting set correctly on default string. I've made a ton of changes, when resuming this work, look at the diff. All of this can be reset excluding the Path work. * Lots of comments but the double instantiation is due to the mobile drawer. Added an ngIf which seems to work. * Fixed dropdown options triggering a ton of looped calls. Default limitTo to 0 when user empties blank or negative. * Removed a ton of UserId db calls from a ton of apis. Added a new API to allow UI to query a specific role to lessen load on UI. * Optimized the code on new filtering to only load people by a given role. This should speed up heavily tagged libraries. Commented out a bunch of code that's no longer used. Will be cleaned up later. * Fixed support so that library filter can handle multiple selections. * Fixed a bug when hitting enter in an input, the statement would be removed. * Fixed multi-select not resuming from url correctly. * Restored the series/all api for Tachiyomi to continue using until I'm motivated enough to update the extension. * Fixed some resuming of state with dropdowns, not always setting values in correct order. * Added FilePath Filter which lets a user search on individual files (slow, may need index) * Added a full filepath for new filtering.
		
			
				
	
	
		
			158 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Linq;
 | 
						|
using System.Threading.Tasks;
 | 
						|
using API.Constants;
 | 
						|
using API.Data;
 | 
						|
using API.Data.Repositories;
 | 
						|
using API.DTOs.SeriesDetail;
 | 
						|
using API.Extensions;
 | 
						|
using API.Helpers.Builders;
 | 
						|
using API.Services;
 | 
						|
using API.Services.Plus;
 | 
						|
using AutoMapper;
 | 
						|
using EasyCaching.Core;
 | 
						|
using Hangfire;
 | 
						|
using Microsoft.AspNetCore.Mvc;
 | 
						|
using Microsoft.Extensions.Logging;
 | 
						|
 | 
						|
namespace API.Controllers;
 | 
						|
 | 
						|
public class ReviewController : BaseApiController
 | 
						|
{
 | 
						|
    private readonly ILogger<ReviewController> _logger;
 | 
						|
    private readonly IUnitOfWork _unitOfWork;
 | 
						|
    private readonly ILicenseService _licenseService;
 | 
						|
    private readonly IMapper _mapper;
 | 
						|
    private readonly IReviewService _reviewService;
 | 
						|
    private readonly IScrobblingService _scrobblingService;
 | 
						|
    private readonly IEasyCachingProvider _cacheProvider;
 | 
						|
    public const string CacheKey = "review_";
 | 
						|
 | 
						|
    public ReviewController(ILogger<ReviewController> logger, IUnitOfWork unitOfWork, ILicenseService licenseService,
 | 
						|
        IMapper mapper, IReviewService reviewService, IScrobblingService scrobblingService,
 | 
						|
        IEasyCachingProviderFactory cachingProviderFactory)
 | 
						|
    {
 | 
						|
        _logger = logger;
 | 
						|
        _unitOfWork = unitOfWork;
 | 
						|
        _licenseService = licenseService;
 | 
						|
        _mapper = mapper;
 | 
						|
        _reviewService = reviewService;
 | 
						|
        _scrobblingService = scrobblingService;
 | 
						|
 | 
						|
        _cacheProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.KavitaPlusReviews);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Fetches reviews from the server for a given series
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="seriesId"></param>
 | 
						|
    [HttpGet]
 | 
						|
    [ResponseCache(CacheProfileName = ResponseCacheProfiles.KavitaPlus, VaryByQueryKeys = new []{"seriesId"})]
 | 
						|
    public async Task<ActionResult<IEnumerable<UserReviewDto>>> GetReviews(int seriesId)
 | 
						|
    {
 | 
						|
        var userId = User.GetUserId();
 | 
						|
        var userRatings = (await _unitOfWork.UserRepository.GetUserRatingDtosForSeriesAsync(seriesId, userId))
 | 
						|
            .Where(r => !string.IsNullOrEmpty(r.Body) && !string.IsNullOrEmpty(r.Tagline))
 | 
						|
            .ToList();
 | 
						|
        if (!await _licenseService.HasActiveLicense())
 | 
						|
        {
 | 
						|
            return Ok(userRatings);
 | 
						|
        }
 | 
						|
 | 
						|
        var cacheKey = CacheKey + seriesId;
 | 
						|
        IList<UserReviewDto> externalReviews;
 | 
						|
 | 
						|
        var result = await _cacheProvider.GetAsync<IEnumerable<UserReviewDto>>(cacheKey);
 | 
						|
        if (result.HasValue)
 | 
						|
        {
 | 
						|
            externalReviews = result.Value.ToList();
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            var reviews = (await _reviewService.GetReviewsForSeries(userId, seriesId)).ToList();
 | 
						|
            externalReviews = SelectSpectrumOfReviews(reviews);
 | 
						|
 | 
						|
            await _cacheProvider.SetAsync(cacheKey, externalReviews, TimeSpan.FromHours(10));
 | 
						|
            _logger.LogDebug("Caching external reviews for {Key}", cacheKey);
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        // Fetch external reviews and splice them in
 | 
						|
        userRatings.AddRange(externalReviews);
 | 
						|
 | 
						|
 | 
						|
        return Ok(userRatings);
 | 
						|
    }
 | 
						|
 | 
						|
    private static IList<UserReviewDto> SelectSpectrumOfReviews(IList<UserReviewDto> reviews)
 | 
						|
    {
 | 
						|
        IList<UserReviewDto> externalReviews;
 | 
						|
        var totalReviews = reviews.Count;
 | 
						|
 | 
						|
        if (totalReviews > 10)
 | 
						|
        {
 | 
						|
            //var stepSize = Math.Max(totalReviews / 10, 1); // Calculate step size, ensuring it's at least 1
 | 
						|
            var stepSize = Math.Max((totalReviews - 4) / 8, 1);
 | 
						|
 | 
						|
            var selectedReviews = new List<UserReviewDto>()
 | 
						|
            {
 | 
						|
                reviews[0],
 | 
						|
                reviews[1],
 | 
						|
            };
 | 
						|
            for (var i = 2; i < totalReviews - 2; i += stepSize)
 | 
						|
            {
 | 
						|
                selectedReviews.Add(reviews[i]);
 | 
						|
 | 
						|
                if (selectedReviews.Count >= 8)
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
 | 
						|
            selectedReviews.Add(reviews[totalReviews - 2]);
 | 
						|
            selectedReviews.Add(reviews[totalReviews - 1]);
 | 
						|
 | 
						|
            externalReviews = selectedReviews;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            externalReviews = reviews;
 | 
						|
        }
 | 
						|
 | 
						|
        return externalReviews;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Updates the review for a given series
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="dto"></param>
 | 
						|
    /// <returns></returns>
 | 
						|
    [HttpPost]
 | 
						|
    public async Task<ActionResult<UserReviewDto>> UpdateReview(UpdateUserReviewDto dto)
 | 
						|
    {
 | 
						|
        var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId(), AppUserIncludes.Ratings);
 | 
						|
        if (user == null) return Unauthorized();
 | 
						|
 | 
						|
        var ratingBuilder = new RatingBuilder(user.Ratings.FirstOrDefault(r => r.SeriesId == dto.SeriesId));
 | 
						|
 | 
						|
        var rating = ratingBuilder
 | 
						|
            .WithBody(dto.Body)
 | 
						|
            .WithSeriesId(dto.SeriesId)
 | 
						|
            .WithTagline(dto.Tagline)
 | 
						|
            .Build();
 | 
						|
 | 
						|
        if (rating.Id == 0)
 | 
						|
        {
 | 
						|
            user.Ratings.Add(rating);
 | 
						|
        }
 | 
						|
        _unitOfWork.UserRepository.Update(user);
 | 
						|
 | 
						|
        await _unitOfWork.CommitAsync();
 | 
						|
 | 
						|
 | 
						|
        BackgroundJob.Enqueue(() =>
 | 
						|
            _scrobblingService.ScrobbleReviewUpdate(user.Id, dto.SeriesId, dto.Tagline, dto.Body));
 | 
						|
        return Ok(_mapper.Map<UserReviewDto>(rating));
 | 
						|
    }
 | 
						|
}
 |